| 1 | //! The overall strategy here is to load all the object file data into memory |
| 2 | //! as inputs are parsed. During `prelink`, as much linking as possible is |
| 3 | //! performed without any knowledge of functions and globals provided by the |
| 4 | //! Zcu. If there is no Zcu, effectively all linking is done in `prelink`. |
| 5 | //! |
| 6 | //! `updateFunc`, `updateNav`, and `updateExports` are handled by merely |
| 7 | //! tracking references to the relevant functions and globals. All the linking |
| 8 | //! logic between objects and Zcu happens in `flush`. Many components of the |
| 9 | //! final output are computed on-the-fly at this time rather than being |
| 10 | //! precomputed and stored separately. |
| 11 | |
| 12 | const Wasm = @This(); |
| 13 | const Archive = @import("Wasm/Archive.zig"); |
| 14 | const Object = @import("Wasm/Object.zig"); |
| 15 | pub const Flush = @import("Wasm/Flush.zig"); |
| 16 | |
| 17 | const builtin = @import("builtin"); |
| 18 | const native_endian = builtin.cpu.arch.endian(); |
| 19 | |
| 20 | const build_options = @import("build_options"); |
| 21 | |
| 22 | const std = @import("std"); |
| 23 | const Io = std.Io; |
| 24 | const Allocator = std.mem.Allocator; |
| 25 | const Cache = std.Build.Cache; |
| 26 | const Path = Cache.Path; |
| 27 | const assert = std.debug.assert; |
| 28 | const fs = std.fs; |
| 29 | const leb = std.leb; |
| 30 | const log = std.log.scoped(.link); |
| 31 | const mem = std.mem; |
| 32 | |
| 33 | const Mir = @import("../codegen/wasm/Mir.zig"); |
| 34 | const CodeGen = @import("../codegen/wasm/CodeGen.zig"); |
| 35 | const abi = @import("../codegen/wasm/abi.zig"); |
| 36 | const Compilation = @import("../Compilation.zig"); |
| 37 | const Dwarf = @import("Dwarf.zig"); |
| 38 | const InternPool = @import("../InternPool.zig"); |
| 39 | const Zcu = @import("../Zcu.zig"); |
| 40 | const codegen = @import("../codegen.zig"); |
| 41 | const link = @import("../link.zig"); |
| 42 | const trace = @import("../tracy.zig").trace; |
| 43 | const wasi_libc = @import("../libs/wasi_libc.zig"); |
| 44 | const Value = @import("../Value.zig"); |
| 45 | |
| 46 | base: link.File, |
| 47 | /// Null-terminated strings, indexes have type String and string_table provides |
| 48 | /// lookup. |
| 49 | /// |
| 50 | /// There are a couple of sites that add things here without adding |
| 51 | /// corresponding string_table entries. For such cases, when implementing |
| 52 | /// serialization/deserialization, they should be adjusted to prefix that data |
| 53 | /// with a null byte so that deserialization does not attempt to create |
| 54 | /// string_table entries for them. Alternately those sites could be moved to |
| 55 | /// use a different byte array for this purpose. |
| 56 | string_bytes: std.ArrayList(u8), |
| 57 | /// Sometimes we have logic that wants to borrow string bytes to store |
| 58 | /// arbitrary things in there. In this case it is not allowed to intern new |
| 59 | /// strings during this time. This safety lock is used to detect misuses. |
| 60 | string_bytes_lock: std.debug.SafetyLock = .{}, |
| 61 | /// Omitted when serializing linker state. |
| 62 | string_table: String.Table, |
| 63 | /// Symbol name of the entry function to export |
| 64 | entry_name: OptionalString, |
| 65 | /// When true, will allow undefined symbols |
| 66 | import_symbols: bool, |
| 67 | /// Set of *global* symbol names to export to the host environment. |
| 68 | export_symbol_names: []const []const u8, |
| 69 | /// When defined, sets the start of the data section. |
| 70 | global_base: ?u64, |
| 71 | /// When defined, sets the initial memory size of the memory. |
| 72 | initial_memory: ?u64, |
| 73 | /// When defined, sets the maximum memory size of the memory. |
| 74 | max_memory: ?u64, |
| 75 | /// When true, will export the function table to the host environment. |
| 76 | export_table: bool, |
| 77 | /// When true, remove maximum size from function table, allowing table to grow. |
| 78 | growable_table: bool, |
| 79 | /// Output name of the file |
| 80 | name: []const u8, |
| 81 | /// List of relocatable files to be linked into the final binary. |
| 82 | objects: std.ArrayList(Object) = .empty, |
| 83 | |
| 84 | func_types: std.array_hash_map.Auto(FunctionType, void) = .empty, |
| 85 | /// Provides a mapping of both imports and provided functions to symbol name. |
| 86 | /// Local functions may be unnamed. |
| 87 | /// Key is symbol name, however the `FunctionImport` may have an name override for the import name. |
| 88 | object_function_imports: std.array_hash_map.Auto(String, FunctionImport) = .empty, |
| 89 | /// All functions for all objects. |
| 90 | object_functions: std.ArrayList(ObjectFunction) = .empty, |
| 91 | |
| 92 | /// Provides a mapping of both imports and provided globals to symbol name. |
| 93 | /// Local globals may be unnamed. |
| 94 | object_global_imports: std.array_hash_map.Auto(String, GlobalImport) = .empty, |
| 95 | /// All globals for all objects. |
| 96 | object_globals: std.ArrayList(ObjectGlobal) = .empty, |
| 97 | |
| 98 | /// All table imports for all objects. |
| 99 | object_table_imports: std.array_hash_map.Auto(String, TableImport) = .empty, |
| 100 | /// All parsed table sections for all objects. |
| 101 | object_tables: std.ArrayList(Table) = .empty, |
| 102 | |
| 103 | /// All memory imports for all objects. |
| 104 | object_memory_imports: std.array_hash_map.Auto(String, MemoryImport) = .empty, |
| 105 | /// All parsed memory sections for all objects. |
| 106 | object_memories: std.ArrayList(ObjectMemory) = .empty, |
| 107 | |
| 108 | /// All relocations from all objects concatenated. `relocs_start` marks the end |
| 109 | /// point of object relocations and start point of Zcu relocations. |
| 110 | object_relocations: std.MultiArrayList(ObjectRelocation) = .empty, |
| 111 | |
| 112 | /// List of initialization functions. These must be called in order of priority |
| 113 | /// by the (synthetic) `__wasm_call_ctors` function. |
| 114 | object_init_funcs: std.ArrayList(InitFunc) = .empty, |
| 115 | |
| 116 | /// The data section of an object has many segments. Each segment corresponds |
| 117 | /// logically to an object file's .data section, or .rodata section. In |
| 118 | /// the case of `-fdata-sections` there will be one segment per data symbol. |
| 119 | object_data_segments: std.ArrayList(ObjectDataSegment) = .empty, |
| 120 | /// Each segment has many data symbols, which correspond logically to global |
| 121 | /// constants. |
| 122 | object_datas: std.ArrayList(ObjectData) = .empty, |
| 123 | object_data_imports: std.array_hash_map.Auto(String, ObjectDataImport) = .empty, |
| 124 | /// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations. |
| 125 | object_custom_segments: std.array_hash_map.Auto(ObjectSectionIndex, CustomSegment) = .empty, |
| 126 | |
| 127 | /// All comdat information for all objects. |
| 128 | object_comdats: std.ArrayList(Comdat) = .empty, |
| 129 | /// A table that maps the relocations to be performed where the key represents |
| 130 | /// the section (across all objects) that the slice of relocations applies to. |
| 131 | object_relocations_table: std.array_hash_map.Auto(ObjectSectionIndex, ObjectRelocation.Slice) = .empty, |
| 132 | /// Incremented across all objects in order to enable calculation of `ObjectSectionIndex` values. |
| 133 | object_total_sections: u32 = 0, |
| 134 | /// All comdat symbols from all objects concatenated. |
| 135 | object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty, |
| 136 | |
| 137 | /// Relocations produced by Zig code and data lowering. These retain semantic |
| 138 | /// targets until `flush`, where final output indexes are known. |
| 139 | zcu_relocations: std.MultiArrayList(ZcuRelocation) = .empty, |
| 140 | /// List of locations within `string_bytes` that must be patched with the virtual |
| 141 | /// memory address of a Uav during `flush`. |
| 142 | /// When emitting an object file, `zcu_relocations` is used instead. |
| 143 | uav_fixups: std.ArrayList(UavFixup) = .empty, |
| 144 | /// List of locations within `string_bytes` that must be patched with the virtual |
| 145 | /// memory address of a Nav during `flush`. |
| 146 | /// When emitting an object file, `zcu_relocations` is used instead. |
| 147 | /// No functions here only global variables. |
| 148 | nav_fixups: std.ArrayList(NavFixup) = .empty, |
| 149 | /// When a nav reference is a function pointer, this tracks the required function |
| 150 | /// table entry index that needs to overwrite the code in the final output. |
| 151 | func_table_fixups: std.ArrayList(FuncTableFixup) = .empty, |
| 152 | /// When importing objects from the host environment, a name must be supplied. |
| 153 | /// LLVM uses "env" by default when none is given. |
| 154 | /// This value is passed to object files since wasm tooling conventions provides |
| 155 | /// no way to specify the module name in the symbol table. |
| 156 | object_host_name: OptionalString, |
| 157 | |
| 158 | /// Memory section |
| 159 | memories: std.wasm.Memory = .{ .limits = .{ |
| 160 | .min = 0, |
| 161 | .max = 0, |
| 162 | .flags = .{ .has_max = false, .is_shared = false }, |
| 163 | } }, |
| 164 | |
| 165 | /// `--verbose-link` output. |
| 166 | /// Initialized on creation, appended to as inputs are added, printed during `flush`. |
| 167 | /// String data is allocated into Compilation arena. |
| 168 | dump_argv_list: std.ArrayList([]const u8), |
| 169 | |
| 170 | preloaded_strings: PreloadedStrings, |
| 171 | |
| 172 | /// This field is used when emitting an object; `navs_exe` used otherwise. |
| 173 | /// Does not include externs since that data lives elsewhere. |
| 174 | navs_obj: std.array_hash_map.Auto(InternPool.Nav.Index, ZcuDataObj) = .empty, |
| 175 | /// This field is unused when emitting an object; `navs_obj` used otherwise. |
| 176 | /// Does not include externs since that data lives elsewhere. |
| 177 | navs_exe: std.array_hash_map.Auto(InternPool.Nav.Index, ZcuDataExe) = .empty, |
| 178 | /// Tracks all InternPool values referenced by codegen. Needed for outputting |
| 179 | /// the data segment. This one does not track ref count because object files |
| 180 | /// require using max LEB encoding for these references anyway. |
| 181 | uavs_obj: std.array_hash_map.Auto(InternPool.Index, ZcuDataObj) = .empty, |
| 182 | /// Tracks ref count to optimize LEB encodings for UAV references. |
| 183 | uavs_exe: std.array_hash_map.Auto(InternPool.Index, ZcuDataExe) = .empty, |
| 184 | /// Sparse table of uavs that need to be emitted with greater alignment than |
| 185 | /// the default for the type. |
| 186 | overaligned_uavs: std.array_hash_map.Auto(InternPool.Index, Alignment) = .empty, |
| 187 | /// When the key is an enum type, this represents a `@tagName` function. |
| 188 | zcu_funcs: std.array_hash_map.Auto(InternPool.Index, ZcuFunc) = .empty, |
| 189 | nav_exports: std.array_hash_map.Auto(NavExport, Zcu.Export.Index) = .empty, |
| 190 | uav_exports: std.array_hash_map.Auto(UavExport, Zcu.Export.Index) = .empty, |
| 191 | imports: std.array_hash_map.Auto(InternPool.Nav.Index, String) = .empty, |
| 192 | |
| 193 | dwarf: ?Dwarf = null, |
| 194 | |
| 195 | flush_buffer: Flush = .{}, |
| 196 | |
| 197 | /// Empty until `prelink`. There it is populated based on object files. |
| 198 | /// Next, it is copied into `Flush.missing_exports` just before `flush` |
| 199 | /// and that data is used during `flush`. |
| 200 | missing_exports: std.array_hash_map.Auto(String, void) = .empty, |
| 201 | entry_resolution: FunctionImport.Resolution = .unresolved, |
| 202 | |
| 203 | /// Empty when outputting an object. |
| 204 | function_exports: std.array_hash_map.Auto(String, FunctionIndex) = .empty, |
| 205 | hidden_function_exports: std.array_hash_map.Auto(String, FunctionIndex) = .empty, |
| 206 | global_exports: std.ArrayList(GlobalExport) = .empty, |
| 207 | /// Tracks the value at the end of prelink. |
| 208 | global_exports_len: u32 = 0, |
| 209 | |
| 210 | /// Ordered list of non-import functions that will appear in the final binary. |
| 211 | /// Empty until prelink. |
| 212 | functions: std.array_hash_map.Auto(FunctionImport.Resolution, void) = .empty, |
| 213 | /// Tracks the value at the end of prelink, at which point `functions` |
| 214 | /// contains only object file functions, and nothing from the Zcu yet. |
| 215 | functions_end_prelink: u32 = 0, |
| 216 | |
| 217 | function_imports_len_prelink: u32 = 0, |
| 218 | data_imports_len_prelink: u32 = 0, |
| 219 | /// At the end of prelink, this is populated with needed functions from |
| 220 | /// objects. |
| 221 | /// |
| 222 | /// During the Zcu phase, entries are not deleted from this table |
| 223 | /// because doing so would be irreversible when an export is deleted. |
| 224 | /// However, entries are added during the Zcu phase when extern functions |
| 225 | /// are passed to `updateNav`. |
| 226 | /// |
| 227 | /// `flush` gets a copy of this table, and then Zcu exports are applied to |
| 228 | /// remove elements from the table, and the remainder are either undefined |
| 229 | /// symbol errors, or import section entries depending on the output mode. |
| 230 | function_imports: std.array_hash_map.Auto(String, FunctionImportId) = .empty, |
| 231 | |
| 232 | /// At the end of prelink, this is populated with data symbols needed by |
| 233 | /// objects. |
| 234 | /// |
| 235 | /// During the Zcu phase, entries are not deleted from this table |
| 236 | /// because doing so would be irreversible when an export is deleted. |
| 237 | /// However, entries are added during the Zcu phase when extern functions |
| 238 | /// are passed to `updateNav`. |
| 239 | /// |
| 240 | /// `flush` gets a copy of this table, and then Zcu exports are applied to |
| 241 | /// remove elements from the table, and the remainder are either undefined |
| 242 | /// symbol errors, or symbol table entries depending on the output mode. |
| 243 | data_imports: std.array_hash_map.Auto(String, DataImportId) = .empty, |
| 244 | /// Set of data symbols that will appear in the final binary when outputting an object file. |
| 245 | datas: std.array_hash_map.Auto(ObjectDataImport.Resolution, void) = .empty, |
| 246 | /// Set of data segment symbols that will appear in the final binary. Used to populate |
| 247 | /// `Flush.data_segments` before sorting. |
| 248 | data_segments: std.array_hash_map.Auto(DataSegmentId, void) = .empty, |
| 249 | |
| 250 | /// Ordered list of non-import globals that will appear in the final binary. |
| 251 | /// Empty until prelink. |
| 252 | globals: std.array_hash_map.Auto(GlobalImport.Resolution, void) = .empty, |
| 253 | /// Tracks the value at the end of prelink, at which point `globals` |
| 254 | /// contains only object file globals, and nothing from the Zcu yet. |
| 255 | globals_end_prelink: u32 = 0, |
| 256 | global_imports: std.array_hash_map.Auto(String, GlobalImportId) = .empty, |
| 257 | |
| 258 | /// Ordered list of non-import tables that will appear in the final binary. |
| 259 | /// Empty until prelink. |
| 260 | tables: std.array_hash_map.Auto(TableImport.Resolution, void) = .empty, |
| 261 | table_imports: std.array_hash_map.Auto(String, TableImport.Index) = .empty, |
| 262 | |
| 263 | /// All functions that have had their address taken and therefore might be |
| 264 | /// called via a `call_indirect` function. |
| 265 | zcu_indirect_function_set: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty, |
| 266 | object_indirect_function_import_set: std.array_hash_map.Auto(String, void) = .empty, |
| 267 | object_indirect_function_set: std.array_hash_map.Auto(ObjectFunctionIndex, void) = .empty, |
| 268 | |
| 269 | error_name_table_ref_count: u32 = 0, |
| 270 | tag_name_table_ref_count: u32 = 0, |
| 271 | |
| 272 | /// Set to true if any `GLOBAL_INDEX` relocation is encountered with |
| 273 | /// `SymbolFlags.tls` set to true. This is for objects only; final |
| 274 | /// value must be this OR'd with the same logic for zig functions |
| 275 | /// (set to true if any threadlocal global is used). |
| 276 | any_tls_relocs: bool = false, |
| 277 | any_passive_inits: bool = false, |
| 278 | |
| 279 | /// All MIR instructions for all Zcu functions. |
| 280 | mir_instructions: std.MultiArrayList(Mir.Inst) = .empty, |
| 281 | /// Corresponds to `mir_instructions`. |
| 282 | mir_extra: std.ArrayList(u32) = .empty, |
| 283 | /// All local types for all Zcu functions. |
| 284 | mir_locals: std.ArrayList(std.wasm.Valtype) = .empty, |
| 285 | |
| 286 | params_scratch: std.ArrayList(std.wasm.Valtype) = .empty, |
| 287 | returns_scratch: std.ArrayList(std.wasm.Valtype) = .empty, |
| 288 | |
| 289 | /// All Zcu error names in order, null-terminated, concatenated. No need to |
| 290 | /// serialize; trivially reconstructed. |
| 291 | error_name_bytes: std.ArrayList(u8) = .empty, |
| 292 | /// For each Zcu error, in order, offset into `error_name_bytes` where the name |
| 293 | /// is stored. No need to serialize; trivially reconstructed. |
| 294 | error_name_offs: std.ArrayList(u32) = .empty, |
| 295 | |
| 296 | tag_name_bytes: std.ArrayList(u8) = .empty, |
| 297 | tag_name_offs: std.ArrayList(u32) = .empty, |
| 298 | |
| 299 | pub const TagNameOff = extern struct { |
| 300 | off: u32, |
| 301 | len: u32, |
| 302 | }; |
| 303 | |
| 304 | pub const UavFixup = extern struct { |
| 305 | uavs_exe_index: UavsExeIndex, |
| 306 | /// Index into `string_bytes`. |
| 307 | offset: u32, |
| 308 | addend: u32, |
| 309 | }; |
| 310 | |
| 311 | pub const NavFixup = extern struct { |
| 312 | nav_index: InternPool.Nav.Index, |
| 313 | /// Index into `string_bytes`. |
| 314 | offset: u32, |
| 315 | addend: u32, |
| 316 | }; |
| 317 | |
| 318 | pub const FuncTableFixup = extern struct { |
| 319 | nav_index: InternPool.Nav.Index, |
| 320 | /// Index into `string_bytes`. |
| 321 | offset: u32, |
| 322 | }; |
| 323 | |
| 324 | /// Index into `objects`. |
| 325 | pub const ObjectIndex = enum(u32) { |
| 326 | _, |
| 327 | |
| 328 | pub fn ptr(index: ObjectIndex, wasm: *const Wasm) *Object { |
| 329 | return &wasm.objects.items[@backingInt(index)]; |
| 330 | } |
| 331 | }; |
| 332 | |
| 333 | /// Index into `Wasm.functions`. |
| 334 | pub const FunctionIndex = enum(u32) { |
| 335 | _, |
| 336 | |
| 337 | pub fn ptr(index: FunctionIndex, wasm: *const Wasm) *FunctionImport.Resolution { |
| 338 | return &wasm.functions.keys()[@backingInt(index)]; |
| 339 | } |
| 340 | |
| 341 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex { |
| 342 | return fromResolution(wasm, .fromIpNav(wasm, nav_index)); |
| 343 | } |
| 344 | |
| 345 | pub fn fromTagIndexType(wasm: *const Wasm, tag_type: InternPool.Index) ?FunctionIndex { |
| 346 | const zcu_func: ZcuFunc.Index = @fromBackingInt(@intCast(wasm.zcu_funcs.getIndex(tag_type) orelse return null)); |
| 347 | return fromResolution(wasm, .pack(wasm, .{ .zcu_func = zcu_func })); |
| 348 | } |
| 349 | |
| 350 | pub fn fromSymbolName(wasm: *const Wasm, name: String) ?FunctionIndex { |
| 351 | if (wasm.object_function_imports.getPtr(name)) |import| { |
| 352 | if (import.resolution != .unresolved) { |
| 353 | return fromResolution(wasm, import.resolution); |
| 354 | } |
| 355 | } |
| 356 | if (wasm.function_exports.get(name)) |index| return index; |
| 357 | if (wasm.hidden_function_exports.get(name)) |index| return index; |
| 358 | return null; |
| 359 | } |
| 360 | |
| 361 | pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?FunctionIndex { |
| 362 | const i = wasm.functions.getIndex(resolution) orelse return null; |
| 363 | return @fromBackingInt(@intCast(i)); |
| 364 | } |
| 365 | }; |
| 366 | |
| 367 | pub const GlobalExport = extern struct { |
| 368 | name: String, |
| 369 | global_index: GlobalIndex, |
| 370 | }; |
| 371 | |
| 372 | /// 0. Index into `Flush.function_imports` |
| 373 | /// 1. Index into `Flush.intrinsic_function_imports` |
| 374 | /// 2. Index into `functions`. |
| 375 | /// |
| 376 | /// Note that function_imports indexes are subject to swap removals during |
| 377 | /// `flush`. |
| 378 | pub const OutputFunctionIndex = enum(u32) { |
| 379 | _, |
| 380 | |
| 381 | pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?OutputFunctionIndex { |
| 382 | return fromFunctionIndex(wasm, FunctionIndex.fromResolution(wasm, resolution) orelse return null); |
| 383 | } |
| 384 | |
| 385 | pub fn fromFunctionIndex(wasm: *const Wasm, index: FunctionIndex) OutputFunctionIndex { |
| 386 | return @fromBackingInt(@intCast( |
| 387 | wasm.flush_buffer.function_imports.entries.len + |
| 388 | wasm.flush_buffer.intrinsic_function_imports.entries.len + |
| 389 | @backingInt(index), |
| 390 | )); |
| 391 | } |
| 392 | |
| 393 | pub fn fromObjectFunction(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex { |
| 394 | return fromResolution(wasm, .fromObjectFunction(wasm, index)).?; |
| 395 | } |
| 396 | |
| 397 | pub fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex { |
| 398 | const ptr = index.ptr(wasm); |
| 399 | if (ptr.flags.binding == .weak) { |
| 400 | const name = ptr.name.unwrap().?; |
| 401 | const import = wasm.object_function_imports.getPtr(name).?; |
| 402 | assert(import.resolution != .unresolved); |
| 403 | return fromResolution(wasm, import.resolution).?; |
| 404 | } |
| 405 | return fromResolution(wasm, .fromObjectFunction(wasm, index)).?; |
| 406 | } |
| 407 | |
| 408 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) OutputFunctionIndex { |
| 409 | const zcu = wasm.base.comp.zcu.?; |
| 410 | const ip = &zcu.intern_pool; |
| 411 | return switch (ip.indexToKey(ip_index)) { |
| 412 | .@"extern" => |ext| { |
| 413 | const name = wasm.imports.get(ext.owner_nav).?; |
| 414 | return fromSymbolName(wasm, name); |
| 415 | }, |
| 416 | else => fromResolution(wasm, .fromIpIndex(wasm, ip_index)).?, |
| 417 | }; |
| 418 | } |
| 419 | |
| 420 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) OutputFunctionIndex { |
| 421 | const zcu = wasm.base.comp.zcu.?; |
| 422 | const ip = &zcu.intern_pool; |
| 423 | const nav = ip.getNav(nav_index); |
| 424 | return fromIpIndex(wasm, nav.resolved.?.value); |
| 425 | } |
| 426 | |
| 427 | pub fn fromTagIndexType(wasm: *const Wasm, tag_type: InternPool.Index) OutputFunctionIndex { |
| 428 | return fromFunctionIndex(wasm, FunctionIndex.fromTagIndexType(wasm, tag_type).?); |
| 429 | } |
| 430 | |
| 431 | pub fn fromSymbolName(wasm: *const Wasm, name: String) OutputFunctionIndex { |
| 432 | if (wasm.flush_buffer.function_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); |
| 433 | if (wasm.flush_buffer.intrinsic_function_imports.getIndex(name)) |i| return @fromBackingInt(@intCast( |
| 434 | wasm.flush_buffer.function_imports.entries.len + i, |
| 435 | )); |
| 436 | return fromFunctionIndex(wasm, FunctionIndex.fromSymbolName(wasm, name) orelse { |
| 437 | if (std.debug.runtime_safety) { |
| 438 | std.debug.panic("function index for symbol not found: {s}", .{name.slice(wasm)}); |
| 439 | } else unreachable; |
| 440 | }); |
| 441 | } |
| 442 | }; |
| 443 | |
| 444 | // Order |
| 445 | // 0. Flush.data_imports |
| 446 | // 1. Wasm.datas |
| 447 | pub const OutputDataIndex = enum(u32) { |
| 448 | _, |
| 449 | |
| 450 | pub fn fromSymbolName(wasm: *const Wasm, name: String) OutputDataIndex { |
| 451 | if (wasm.flush_buffer.data_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); |
| 452 | if (wasm.object_data_imports.getPtr(name)) |import| { |
| 453 | if (import.resolution != .unresolved) return fromResolution(wasm, import.resolution).?; |
| 454 | } |
| 455 | if (wasm.flush_buffer.data_exports.get(name)) |symbol| return fromResolution(wasm, symbol.resolution).?; |
| 456 | if (std.debug.runtime_safety) { |
| 457 | std.debug.panic("data index for symbol not found: {s}", .{name.slice(wasm)}); |
| 458 | } else unreachable; |
| 459 | } |
| 460 | |
| 461 | pub fn fromObjectData(wasm: *const Wasm, index: ObjectData.Index) OutputDataIndex { |
| 462 | return fromResolution(wasm, .fromObjectDataIndex(wasm, index)).?; |
| 463 | } |
| 464 | |
| 465 | pub fn fromResolution(wasm: *const Wasm, resolution: ObjectDataImport.Resolution) ?OutputDataIndex { |
| 466 | const i = wasm.datas.getIndex(resolution) orelse return null; |
| 467 | return @fromBackingInt(@intCast(wasm.flush_buffer.data_imports.entries.len + i)); |
| 468 | } |
| 469 | |
| 470 | pub fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) OutputDataIndex { |
| 471 | const comp = wasm.base.comp; |
| 472 | const resolution: ObjectDataImport.Resolution = if (comp.config.output_mode == .Obj) |
| 473 | .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)) }) |
| 474 | else |
| 475 | .pack(wasm, .{ .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)) }); |
| 476 | return fromResolution(wasm, resolution).?; |
| 477 | } |
| 478 | |
| 479 | pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) OutputDataIndex { |
| 480 | const zcu = wasm.base.comp.zcu.?; |
| 481 | const ip = &zcu.intern_pool; |
| 482 | const nav = ip.getNav(nav_index); |
| 483 | if (nav.getExtern(ip) != null) { |
| 484 | return fromSymbolName(wasm, wasm.imports.get(nav_index).?); |
| 485 | } |
| 486 | const resolution: ObjectDataImport.Resolution = if (wasm.base.comp.config.output_mode == .Obj) |
| 487 | .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)) }) |
| 488 | else |
| 489 | .pack(wasm, .{ .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)) }); |
| 490 | return fromResolution(wasm, resolution).?; |
| 491 | } |
| 492 | }; |
| 493 | |
| 494 | /// Index into `Wasm.globals`. |
| 495 | pub const GlobalIndex = enum(u32) { |
| 496 | _, |
| 497 | |
| 498 | /// This is only accurate when not emitting an object and there is a Zcu. |
| 499 | pub const stack_pointer: GlobalIndex = @fromBackingInt(@intCast(0)); |
| 500 | |
| 501 | /// Same as `stack_pointer` but with a safety assertion. |
| 502 | pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index { |
| 503 | const comp = wasm.base.comp; |
| 504 | assert(comp.config.output_mode != .Obj); |
| 505 | assert(comp.zcu != null); |
| 506 | return .stack_pointer; |
| 507 | } |
| 508 | |
| 509 | pub fn fromResolution(wasm: *const Wasm, resolution: GlobalImport.Resolution) ?GlobalIndex { |
| 510 | const i = wasm.globals.getIndex(resolution) orelse return null; |
| 511 | return @fromBackingInt(@intCast(wasm.flush_buffer.global_imports.entries.len + i)); |
| 512 | } |
| 513 | |
| 514 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?GlobalIndex { |
| 515 | return fromResolution(wasm, .fromIpNav(wasm, nav_index)); |
| 516 | } |
| 517 | |
| 518 | pub fn fromObjectGlobal(wasm: *const Wasm, i: ObjectGlobalIndex) GlobalIndex { |
| 519 | return fromResolution(wasm, .fromObjectGlobal(wasm, i)).?; |
| 520 | } |
| 521 | |
| 522 | pub fn fromObjectGlobalHandlingWeak(wasm: *const Wasm, index: ObjectGlobalIndex) GlobalIndex { |
| 523 | const global = index.ptr(wasm); |
| 524 | return if (global.flags.binding == .weak) |
| 525 | fromSymbolName(wasm, global.name.unwrap().?) |
| 526 | else |
| 527 | fromObjectGlobal(wasm, index); |
| 528 | } |
| 529 | |
| 530 | pub fn fromSymbolName(wasm: *const Wasm, name: String) GlobalIndex { |
| 531 | if (wasm.flush_buffer.global_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); |
| 532 | const import = wasm.object_global_imports.getPtr(name).?; |
| 533 | return fromResolution(wasm, import.resolution).?; |
| 534 | } |
| 535 | }; |
| 536 | |
| 537 | /// Index into `tables`. |
| 538 | pub const TableIndex = enum(u32) { |
| 539 | _, |
| 540 | |
| 541 | pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex { |
| 542 | return @fromBackingInt(@intCast(wasm.tables.getIndex(.fromObjectTable(i)).?)); |
| 543 | } |
| 544 | |
| 545 | pub fn fromSymbolName(wasm: *const Wasm, name: String) TableIndex { |
| 546 | const import = wasm.object_table_imports.getPtr(name).?; |
| 547 | return @fromBackingInt(@intCast(wasm.tables.getIndex(import.resolution).?)); |
| 548 | } |
| 549 | }; |
| 550 | |
| 551 | /// The first N indexes correspond to input objects (`objects`) array. |
| 552 | /// After that, the indexes correspond to the `source_locations` array, |
| 553 | /// representing a location in a Zig source file that can be pinpointed |
| 554 | /// precisely via AST node and token. |
| 555 | pub const SourceLocation = enum(u32) { |
| 556 | /// From the Zig compilation unit but no precise source location. |
| 557 | zig_object_nofile = std.math.maxInt(u32) - 1, |
| 558 | none = std.math.maxInt(u32), |
| 559 | _, |
| 560 | |
| 561 | /// Index into `source_locations`. |
| 562 | pub const Index = enum(u32) { |
| 563 | _, |
| 564 | }; |
| 565 | |
| 566 | pub const Unpacked = union(enum) { |
| 567 | none, |
| 568 | zig_object_nofile, |
| 569 | object_index: ObjectIndex, |
| 570 | source_location_index: Index, |
| 571 | }; |
| 572 | |
| 573 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) SourceLocation { |
| 574 | _ = wasm; |
| 575 | return switch (unpacked) { |
| 576 | .zig_object_nofile => .zig_object_nofile, |
| 577 | .none => .none, |
| 578 | .object_index => |object_index| @fromBackingInt(@intCast(@backingInt(object_index))), |
| 579 | .source_location_index => @panic("TODO"), |
| 580 | }; |
| 581 | } |
| 582 | |
| 583 | pub fn unpack(sl: SourceLocation, wasm: *const Wasm) Unpacked { |
| 584 | return switch (sl) { |
| 585 | .zig_object_nofile => .zig_object_nofile, |
| 586 | .none => .none, |
| 587 | _ => { |
| 588 | const i = @backingInt(sl); |
| 589 | if (i < wasm.objects.items.len) return .{ .object_index = @fromBackingInt(@intCast(i)) }; |
| 590 | const sl_index = i - wasm.objects.items.len; |
| 591 | _ = sl_index; |
| 592 | @panic("TODO"); |
| 593 | }, |
| 594 | }; |
| 595 | } |
| 596 | |
| 597 | pub fn fromObject(object_index: ObjectIndex, wasm: *const Wasm) SourceLocation { |
| 598 | return pack(.{ .object_index = object_index }, wasm); |
| 599 | } |
| 600 | |
| 601 | pub fn addError(sl: SourceLocation, wasm: *Wasm, comptime f: []const u8, args: anytype) void { |
| 602 | const diags = &wasm.base.comp.link_diags; |
| 603 | switch (sl.unpack(wasm)) { |
| 604 | .none => unreachable, |
| 605 | .zig_object_nofile => diags.addError("zig compilation unit: " ++ f, args), |
| 606 | .object_index => |i| diags.addError("{f}: " ++ f, .{i.ptr(wasm).path} ++ args), |
| 607 | .source_location_index => @panic("TODO"), |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | pub fn addNote( |
| 612 | sl: SourceLocation, |
| 613 | err: *link.Diags.ErrorWithNotes, |
| 614 | comptime f: []const u8, |
| 615 | args: anytype, |
| 616 | ) void { |
| 617 | err.addNote(f, args); |
| 618 | const err_msg = &err.diags.msgs.items[err.index]; |
| 619 | err_msg.notes[err.note_slot - 1].source_location = .{ .wasm = sl }; |
| 620 | } |
| 621 | |
| 622 | pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{AlreadyReported} { |
| 623 | return diags.failSourceLocation(.{ .wasm = sl }, format, args); |
| 624 | } |
| 625 | |
| 626 | pub fn string( |
| 627 | sl: SourceLocation, |
| 628 | msg: []const u8, |
| 629 | bundle: *std.zig.ErrorBundle.Wip, |
| 630 | wasm: *const Wasm, |
| 631 | ) Allocator.Error!std.zig.ErrorBundle.String { |
| 632 | return switch (sl.unpack(wasm)) { |
| 633 | .none => try bundle.addString(msg), |
| 634 | .zig_object_nofile => try bundle.printString("zig compilation unit: {s}", .{msg}), |
| 635 | .object_index => |i| { |
| 636 | const obj = i.ptr(wasm); |
| 637 | return if (obj.archive_member_name.slice(wasm)) |obj_name| |
| 638 | try bundle.printString("{f} ({s}): {s}", .{ obj.path, std.fs.path.basename(obj_name), msg }) |
| 639 | else |
| 640 | try bundle.printString("{f}: {s}", .{ obj.path, msg }); |
| 641 | }, |
| 642 | .source_location_index => @panic("TODO"), |
| 643 | }; |
| 644 | } |
| 645 | }; |
| 646 | |
| 647 | /// The lower bits of this ABI-match the flags here: |
| 648 | /// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection |
| 649 | /// The upper bits are used for nefarious purposes. |
| 650 | pub const SymbolFlags = packed struct(u32) { |
| 651 | binding: Binding = .strong, |
| 652 | /// Indicating that this is a hidden symbol. Hidden symbols are not to be |
| 653 | /// exported when performing the final link, but may be linked to other |
| 654 | /// modules. |
| 655 | visibility_hidden: bool = false, |
| 656 | padding0: u1 = 0, |
| 657 | /// For non-data symbols, this must match whether the symbol is an import |
| 658 | /// or is defined; for data symbols, determines whether a segment is |
| 659 | /// specified. |
| 660 | undefined: bool = false, |
| 661 | /// The symbol is intended to be exported from the wasm module to the host |
| 662 | /// environment. This differs from the visibility flags in that it affects |
| 663 | /// static linking. |
| 664 | exported: bool = false, |
| 665 | /// The symbol uses an explicit symbol name, rather than reusing the name |
| 666 | /// from a wasm import. This allows it to remap imports from foreign |
| 667 | /// WebAssembly modules into local symbols with different names. |
| 668 | explicit_name: bool = false, |
| 669 | /// The symbol is intended to be included in the linker output, regardless |
| 670 | /// of whether it is used by the program. Same meaning as `retain`. |
| 671 | no_strip: bool = false, |
| 672 | /// The symbol resides in thread local storage. |
| 673 | tls: bool = false, |
| 674 | /// The symbol represents an absolute address. This means its offset is |
| 675 | /// relative to the start of the wasm memory as opposed to being relative |
| 676 | /// to a data segment. |
| 677 | absolute: bool = false, |
| 678 | |
| 679 | // Above here matches the tooling conventions ABI. |
| 680 | |
| 681 | padding1: u13 = 0, |
| 682 | /// Zig-specific. Dead things are allowed to be garbage collected. |
| 683 | alive: bool = false, |
| 684 | /// Zig-specific. This symbol comes from an object that must be included in |
| 685 | /// the final link. |
| 686 | must_link: bool = false, |
| 687 | /// Zig-specific. |
| 688 | global_type: GlobalType4 = .zero, |
| 689 | /// Zig-specific. |
| 690 | limits_has_max: bool = false, |
| 691 | /// Zig-specific. |
| 692 | limits_is_shared: bool = false, |
| 693 | /// Zig-specific. |
| 694 | ref_type: RefType1 = .funcref, |
| 695 | |
| 696 | pub const Binding = enum(u2) { |
| 697 | strong = 0, |
| 698 | /// Indicating that this is a weak symbol. When linking multiple modules |
| 699 | /// defining the same symbol, all weak definitions are discarded if any |
| 700 | /// strong definitions exist; then if multiple weak definitions exist all |
| 701 | /// but one (unspecified) are discarded; and finally it is an error if more |
| 702 | /// than one definition remains. |
| 703 | weak = 1, |
| 704 | /// Indicating that this is a local symbol. Local symbols are not to be |
| 705 | /// exported, or linked to other modules/sections. The names of all |
| 706 | /// non-local symbols must be unique, but the names of local symbols |
| 707 | /// are not considered for uniqueness. A local function or global |
| 708 | /// symbol cannot reference an import. |
| 709 | local = 2, |
| 710 | }; |
| 711 | |
| 712 | pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void { |
| 713 | flags.no_strip = no_strip; |
| 714 | flags.alive = false; |
| 715 | flags.must_link = must_link; |
| 716 | flags.global_type = .zero; |
| 717 | flags.limits_has_max = false; |
| 718 | flags.limits_is_shared = false; |
| 719 | flags.ref_type = .funcref; |
| 720 | } |
| 721 | |
| 722 | pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool, is_obj: bool) bool { |
| 723 | return flags.exported or |
| 724 | (is_dynamic and !flags.visibility_hidden) or |
| 725 | (is_obj and flags.binding != .local) or |
| 726 | (flags.no_strip and flags.must_link); |
| 727 | } |
| 728 | |
| 729 | pub fn isExported(flags: SymbolFlags, is_dynamic: bool) bool { |
| 730 | if (flags.undefined or flags.binding == .local) return false; |
| 731 | if (is_dynamic and !flags.visibility_hidden) return true; |
| 732 | return flags.exported; |
| 733 | } |
| 734 | |
| 735 | /// Returns the name as how it will be output into the final object |
| 736 | /// file or binary. When `merge` is true, this will return the |
| 737 | /// short name. i.e. ".rodata". When false, it returns the entire name instead. |
| 738 | pub fn outputName(flags: SymbolFlags, name: []const u8, merge: bool) []const u8 { |
| 739 | if (flags.tls) return ".tdata"; |
| 740 | if (!merge) return name; |
| 741 | if (mem.startsWith(u8, name, ".rodata.")) return ".rodata"; |
| 742 | if (mem.startsWith(u8, name, ".text.")) return ".text"; |
| 743 | if (mem.startsWith(u8, name, ".data.")) return ".data"; |
| 744 | if (mem.startsWith(u8, name, ".bss.")) return ".bss"; |
| 745 | return name; |
| 746 | } |
| 747 | |
| 748 | /// Masks off the Zig-specific stuff. |
| 749 | pub fn toAbiInteger(flags: SymbolFlags) u32 { |
| 750 | var copy = flags; |
| 751 | copy.initZigSpecific(false, flags.no_strip); |
| 752 | return @backingInt(copy); |
| 753 | } |
| 754 | }; |
| 755 | |
| 756 | pub const GlobalType4 = packed struct(u4) { |
| 757 | valtype: Valtype3, |
| 758 | mutable: bool, |
| 759 | |
| 760 | pub const zero: GlobalType4 = @bitCast(@as(u4, 0)); |
| 761 | |
| 762 | pub fn to(gt: GlobalType4) ObjectGlobal.Type { |
| 763 | return .{ |
| 764 | .valtype = gt.valtype.to(), |
| 765 | .mutable = gt.mutable, |
| 766 | }; |
| 767 | } |
| 768 | }; |
| 769 | |
| 770 | pub const Valtype3 = enum(u3) { |
| 771 | i32, |
| 772 | i64, |
| 773 | f32, |
| 774 | f64, |
| 775 | v128, |
| 776 | |
| 777 | pub fn from(v: std.wasm.Valtype) Valtype3 { |
| 778 | return switch (v) { |
| 779 | .i32 => .i32, |
| 780 | .i64 => .i64, |
| 781 | .f32 => .f32, |
| 782 | .f64 => .f64, |
| 783 | .v128 => .v128, |
| 784 | }; |
| 785 | } |
| 786 | |
| 787 | pub fn to(v: Valtype3) std.wasm.Valtype { |
| 788 | return switch (v) { |
| 789 | .i32 => .i32, |
| 790 | .i64 => .i64, |
| 791 | .f32 => .f32, |
| 792 | .f64 => .f64, |
| 793 | .v128 => .v128, |
| 794 | }; |
| 795 | } |
| 796 | }; |
| 797 | |
| 798 | /// Index into `Wasm.navs_obj`. |
| 799 | pub const NavsObjIndex = enum(u32) { |
| 800 | _, |
| 801 | |
| 802 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index { |
| 803 | return &wasm.navs_obj.keys()[@backingInt(i)]; |
| 804 | } |
| 805 | |
| 806 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj { |
| 807 | return &wasm.navs_obj.values()[@backingInt(i)]; |
| 808 | } |
| 809 | |
| 810 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { |
| 811 | const zcu = wasm.base.comp.zcu.?; |
| 812 | const ip = &zcu.intern_pool; |
| 813 | const nav = ip.getNav(i.key(wasm).*); |
| 814 | return nav.fqn.toSlice(ip); |
| 815 | } |
| 816 | }; |
| 817 | |
| 818 | /// Index into `Wasm.navs_exe`. |
| 819 | pub const NavsExeIndex = enum(u32) { |
| 820 | _, |
| 821 | |
| 822 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index { |
| 823 | return &wasm.navs_exe.keys()[@backingInt(i)]; |
| 824 | } |
| 825 | |
| 826 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe { |
| 827 | return &wasm.navs_exe.values()[@backingInt(i)]; |
| 828 | } |
| 829 | |
| 830 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { |
| 831 | const zcu = wasm.base.comp.zcu.?; |
| 832 | const ip = &zcu.intern_pool; |
| 833 | const nav = ip.getNav(i.key(wasm).*); |
| 834 | return nav.fqn.toSlice(ip); |
| 835 | } |
| 836 | }; |
| 837 | |
| 838 | /// Index into `Wasm.uavs_obj`. |
| 839 | pub const UavsObjIndex = enum(u32) { |
| 840 | _, |
| 841 | |
| 842 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { |
| 843 | return &wasm.uavs_obj.keys()[@backingInt(i)]; |
| 844 | } |
| 845 | |
| 846 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj { |
| 847 | return &wasm.uavs_obj.values()[@backingInt(i)]; |
| 848 | } |
| 849 | }; |
| 850 | |
| 851 | /// Index into `Wasm.uavs_exe`. |
| 852 | pub const UavsExeIndex = enum(u32) { |
| 853 | _, |
| 854 | |
| 855 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { |
| 856 | return &wasm.uavs_exe.keys()[@backingInt(i)]; |
| 857 | } |
| 858 | |
| 859 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe { |
| 860 | return &wasm.uavs_exe.values()[@backingInt(i)]; |
| 861 | } |
| 862 | }; |
| 863 | |
| 864 | /// Used when emitting a relocatable object. |
| 865 | pub const ZcuDataObj = extern struct { |
| 866 | code: DataPayload, |
| 867 | relocs: ZcuRelocation.Slice, |
| 868 | }; |
| 869 | |
| 870 | /// Used when not emitting a relocatable object. |
| 871 | pub const ZcuDataExe = extern struct { |
| 872 | code: DataPayload, |
| 873 | /// Tracks how many references there are for the purposes of sorting data segments. |
| 874 | count: u32, |
| 875 | }; |
| 876 | |
| 877 | /// An abstraction for calling `lowerZcuData` repeatedly until all data entries |
| 878 | /// are populated. |
| 879 | const ZcuDataStarts = struct { |
| 880 | uavs_i: u32, |
| 881 | |
| 882 | fn init(wasm: *const Wasm) ZcuDataStarts { |
| 883 | const comp = wasm.base.comp; |
| 884 | const is_obj = comp.config.output_mode == .Obj; |
| 885 | return if (is_obj) initObj(wasm) else initExe(wasm); |
| 886 | } |
| 887 | |
| 888 | fn initObj(wasm: *const Wasm) ZcuDataStarts { |
| 889 | return .{ |
| 890 | .uavs_i = @intCast(wasm.uavs_obj.entries.len), |
| 891 | }; |
| 892 | } |
| 893 | |
| 894 | fn initExe(wasm: *const Wasm) ZcuDataStarts { |
| 895 | return .{ |
| 896 | .uavs_i = @intCast(wasm.uavs_exe.entries.len), |
| 897 | }; |
| 898 | } |
| 899 | |
| 900 | fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { |
| 901 | const comp = wasm.base.comp; |
| 902 | const is_obj = comp.config.output_mode == .Obj; |
| 903 | return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt); |
| 904 | } |
| 905 | |
| 906 | fn finishObj(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { |
| 907 | var uavs_i = zds.uavs_i; |
| 908 | while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) { |
| 909 | // Call to `lowerZcuData` here possibly creates more entries in these tables. |
| 910 | const uav = wasm.uavs_obj.keys()[uavs_i]; |
| 911 | const zcu_data = try lowerZcuData(wasm, pt, uav); |
| 912 | wasm.uavs_obj.values()[uavs_i] = zcu_data; |
| 913 | } |
| 914 | } |
| 915 | |
| 916 | fn finishExe(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { |
| 917 | var uavs_i = zds.uavs_i; |
| 918 | while (uavs_i < wasm.uavs_exe.entries.len) : (uavs_i += 1) { |
| 919 | // Call to `lowerZcuData` here possibly creates more entries in these tables. |
| 920 | const zcu_data = try lowerZcuData(wasm, pt, wasm.uavs_exe.keys()[uavs_i]); |
| 921 | wasm.uavs_exe.values()[uavs_i].code = zcu_data.code; |
| 922 | } |
| 923 | } |
| 924 | }; |
| 925 | |
| 926 | pub const ZcuFunc = union { |
| 927 | function: Function, |
| 928 | tag_name: TagName, |
| 929 | |
| 930 | pub const Function = extern struct { |
| 931 | /// Index into `Wasm.mir_instructions`. |
| 932 | instructions_off: u32, |
| 933 | /// This is unused except for as a safety slice bound and could be removed. |
| 934 | instructions_len: u32, |
| 935 | /// Index into `Wasm.mir_extra`. |
| 936 | extra_off: u32, |
| 937 | /// This is unused except for as a safety slice bound and could be removed. |
| 938 | extra_len: u32, |
| 939 | /// Index into `Wasm.mir_locals`. |
| 940 | locals_off: u32, |
| 941 | locals_len: u32, |
| 942 | prologue: Mir.Prologue, |
| 943 | }; |
| 944 | |
| 945 | pub const TagName = extern struct { |
| 946 | symbol_name: String, |
| 947 | type_index: FunctionType.Index, |
| 948 | }; |
| 949 | |
| 950 | /// Index into `Wasm.zcu_funcs`. |
| 951 | /// Note that swapRemove is sometimes performed on `zcu_funcs`. |
| 952 | pub const Index = enum(u32) { |
| 953 | _, |
| 954 | |
| 955 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { |
| 956 | return &wasm.zcu_funcs.keys()[@backingInt(i)]; |
| 957 | } |
| 958 | |
| 959 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc { |
| 960 | return &wasm.zcu_funcs.values()[@backingInt(i)]; |
| 961 | } |
| 962 | |
| 963 | pub fn flags(i: @This(), wasm: *const Wasm) SymbolFlags { |
| 964 | const zcu = wasm.base.comp.zcu.?; |
| 965 | const ip = &zcu.intern_pool; |
| 966 | const ip_index = i.key(wasm).*; |
| 967 | switch (ip.indexToKey(ip_index)) { |
| 968 | .func => |func| { |
| 969 | const nav = ip.getNav(func.owner_nav); |
| 970 | if (nav.getExtern(ip)) |ext| { |
| 971 | const name_slice = ext.name.toSlice(ip); |
| 972 | const name_string = wasm.getExistingString(name_slice).?; |
| 973 | return .{ |
| 974 | .binding = switch (ext.linkage) { |
| 975 | .internal => .local, |
| 976 | .strong => .strong, |
| 977 | .weak => .weak, |
| 978 | .link_once => @panic("TODO: COMDAT"), |
| 979 | }, |
| 980 | .visibility_hidden = switch (ext.visibility) { |
| 981 | .default => false, |
| 982 | .hidden => true, |
| 983 | .protected => false, |
| 984 | }, |
| 985 | .undefined = false, |
| 986 | .exported = wasm.missing_exports.contains(name_string), |
| 987 | .explicit_name = false, |
| 988 | .no_strip = false, |
| 989 | .tls = ext.is_threadlocal, |
| 990 | .absolute = false, |
| 991 | }; |
| 992 | } else { |
| 993 | return .{ |
| 994 | .binding = .local, |
| 995 | .tls = nav.resolved.?.@"threadlocal", |
| 996 | }; |
| 997 | } |
| 998 | }, |
| 999 | .enum_type => { |
| 1000 | return .{ |
| 1001 | .binding = .local, |
| 1002 | }; |
| 1003 | }, |
| 1004 | else => unreachable, |
| 1005 | } |
| 1006 | } |
| 1007 | |
| 1008 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { |
| 1009 | const zcu = wasm.base.comp.zcu.?; |
| 1010 | const ip = &zcu.intern_pool; |
| 1011 | const ip_index = i.key(wasm).*; |
| 1012 | switch (ip.indexToKey(ip_index)) { |
| 1013 | .func => |func| { |
| 1014 | const nav = ip.getNav(func.owner_nav); |
| 1015 | return nav.fqn.toSlice(ip); |
| 1016 | }, |
| 1017 | .enum_type => { |
| 1018 | return i.value(wasm).tag_name.symbol_name.slice(wasm); |
| 1019 | }, |
| 1020 | else => unreachable, |
| 1021 | } |
| 1022 | } |
| 1023 | |
| 1024 | pub fn typeIndex(i: @This(), wasm: *Wasm) FunctionType.Index { |
| 1025 | const comp = wasm.base.comp; |
| 1026 | const zcu = comp.zcu.?; |
| 1027 | const target = &comp.root_mod.resolved_target.result; |
| 1028 | const ip = &zcu.intern_pool; |
| 1029 | switch (ip.indexToKey(i.key(wasm).*)) { |
| 1030 | .func => |func| { |
| 1031 | const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?; |
| 1032 | return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target).?; |
| 1033 | }, |
| 1034 | .enum_type => { |
| 1035 | return i.value(wasm).tag_name.type_index; |
| 1036 | }, |
| 1037 | else => unreachable, |
| 1038 | } |
| 1039 | } |
| 1040 | }; |
| 1041 | }; |
| 1042 | |
| 1043 | pub const NavExport = extern struct { |
| 1044 | name: String, |
| 1045 | nav_index: InternPool.Nav.Index, |
| 1046 | }; |
| 1047 | |
| 1048 | pub const UavExport = extern struct { |
| 1049 | name: String, |
| 1050 | uav_index: InternPool.Index, |
| 1051 | }; |
| 1052 | |
| 1053 | pub const FunctionImport = extern struct { |
| 1054 | flags: SymbolFlags, |
| 1055 | module_name: OptionalString, |
| 1056 | /// May be different than the key which is a symbol name. |
| 1057 | name: String, |
| 1058 | source_location: SourceLocation, |
| 1059 | resolution: Resolution, |
| 1060 | type: FunctionType.Index, |
| 1061 | |
| 1062 | /// Represents a synthetic function, a function from an object, or a |
| 1063 | /// function from the Zcu. |
| 1064 | pub const Resolution = enum(u32) { |
| 1065 | unresolved, |
| 1066 | __wasm_apply_global_tls_relocs, |
| 1067 | __wasm_call_ctors, |
| 1068 | __wasm_init_memory, |
| 1069 | __wasm_init_tls, |
| 1070 | // Next, index into `object_functions`. |
| 1071 | // Next, index into `zcu_funcs`. |
| 1072 | _, |
| 1073 | |
| 1074 | const first_object_function = @backingInt(Resolution.__wasm_init_tls) + 1; |
| 1075 | |
| 1076 | pub const Unpacked = union(enum) { |
| 1077 | unresolved, |
| 1078 | __wasm_apply_global_tls_relocs, |
| 1079 | __wasm_call_ctors, |
| 1080 | __wasm_init_memory, |
| 1081 | __wasm_init_tls, |
| 1082 | object_function: ObjectFunctionIndex, |
| 1083 | zcu_func: ZcuFunc.Index, |
| 1084 | }; |
| 1085 | |
| 1086 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { |
| 1087 | return switch (r) { |
| 1088 | .unresolved => .unresolved, |
| 1089 | .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs, |
| 1090 | .__wasm_call_ctors => .__wasm_call_ctors, |
| 1091 | .__wasm_init_memory => .__wasm_init_memory, |
| 1092 | .__wasm_init_tls => .__wasm_init_tls, |
| 1093 | _ => { |
| 1094 | const object_function_index = @backingInt(r) - first_object_function; |
| 1095 | |
| 1096 | const zcu_func_index = if (object_function_index < wasm.object_functions.items.len) |
| 1097 | return .{ .object_function = @fromBackingInt(@intCast(object_function_index)) } |
| 1098 | else |
| 1099 | object_function_index - wasm.object_functions.items.len; |
| 1100 | |
| 1101 | return .{ .zcu_func = @fromBackingInt(@intCast(zcu_func_index)) }; |
| 1102 | }, |
| 1103 | }; |
| 1104 | } |
| 1105 | |
| 1106 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { |
| 1107 | return switch (unpacked) { |
| 1108 | .unresolved => .unresolved, |
| 1109 | .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs, |
| 1110 | .__wasm_call_ctors => .__wasm_call_ctors, |
| 1111 | .__wasm_init_memory => .__wasm_init_memory, |
| 1112 | .__wasm_init_tls => .__wasm_init_tls, |
| 1113 | .object_function => |i| @fromBackingInt(@intCast(first_object_function + @backingInt(i))), |
| 1114 | .zcu_func => |i| @fromBackingInt(@intCast(first_object_function + wasm.object_functions.items.len + @backingInt(i))), |
| 1115 | }; |
| 1116 | } |
| 1117 | |
| 1118 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution { |
| 1119 | const zcu = wasm.base.comp.zcu.?; |
| 1120 | const ip = &zcu.intern_pool; |
| 1121 | return fromIpIndex(wasm, ip.getNav(nav_index).resolved.?.value); |
| 1122 | } |
| 1123 | |
| 1124 | pub fn fromZcuFunc(wasm: *const Wasm, i: ZcuFunc.Index) Resolution { |
| 1125 | return pack(wasm, .{ .zcu_func = i }); |
| 1126 | } |
| 1127 | |
| 1128 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { |
| 1129 | return fromZcuFunc(wasm, @fromBackingInt(@intCast(wasm.zcu_funcs.getIndex(ip_index).?))); |
| 1130 | } |
| 1131 | |
| 1132 | pub fn fromObjectFunction(wasm: *const Wasm, object_function: ObjectFunctionIndex) Resolution { |
| 1133 | return pack(wasm, .{ .object_function = object_function }); |
| 1134 | } |
| 1135 | |
| 1136 | pub fn flags(r: Resolution, wasm: *Wasm) SymbolFlags { |
| 1137 | return switch (unpack(r, wasm)) { |
| 1138 | .unresolved => unreachable, |
| 1139 | .__wasm_apply_global_tls_relocs, .__wasm_call_ctors, .__wasm_init_memory, .__wasm_init_tls => unreachable, |
| 1140 | .object_function => |i| i.ptr(wasm).flags, |
| 1141 | .zcu_func => |i| i.flags(wasm), |
| 1142 | }; |
| 1143 | } |
| 1144 | |
| 1145 | pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool { |
| 1146 | return switch (r.unpack(wasm)) { |
| 1147 | .unresolved, .zcu_func => true, |
| 1148 | else => false, |
| 1149 | }; |
| 1150 | } |
| 1151 | |
| 1152 | pub fn typeIndex(r: Resolution, wasm: *Wasm) FunctionType.Index { |
| 1153 | return switch (unpack(r, wasm)) { |
| 1154 | .unresolved => unreachable, |
| 1155 | .__wasm_apply_global_tls_relocs, |
| 1156 | .__wasm_call_ctors, |
| 1157 | .__wasm_init_memory, |
| 1158 | => getExistingFuncType2(wasm, &.{}, &.{}), |
| 1159 | .__wasm_init_tls => getExistingFuncType2(wasm, &.{.i32}, &.{}), |
| 1160 | .object_function => |i| i.ptr(wasm).type_index, |
| 1161 | .zcu_func => |i| i.typeIndex(wasm), |
| 1162 | }; |
| 1163 | } |
| 1164 | |
| 1165 | pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { |
| 1166 | return switch (unpack(r, wasm)) { |
| 1167 | .unresolved => unreachable, |
| 1168 | .__wasm_apply_global_tls_relocs => @tagName(Unpacked.__wasm_apply_global_tls_relocs), |
| 1169 | .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors), |
| 1170 | .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory), |
| 1171 | .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls), |
| 1172 | .object_function => |i| i.ptr(wasm).name.slice(wasm), |
| 1173 | .zcu_func => |i| i.name(wasm), |
| 1174 | }; |
| 1175 | } |
| 1176 | }; |
| 1177 | |
| 1178 | /// Index into `object_function_imports`. |
| 1179 | pub const Index = enum(u32) { |
| 1180 | _, |
| 1181 | |
| 1182 | pub fn key(index: Index, wasm: *const Wasm) *String { |
| 1183 | return &wasm.object_function_imports.keys()[@backingInt(index)]; |
| 1184 | } |
| 1185 | |
| 1186 | pub fn value(index: Index, wasm: *const Wasm) *FunctionImport { |
| 1187 | return &wasm.object_function_imports.values()[@backingInt(index)]; |
| 1188 | } |
| 1189 | |
| 1190 | pub fn symbolName(index: Index, wasm: *const Wasm) String { |
| 1191 | return index.key(wasm).*; |
| 1192 | } |
| 1193 | |
| 1194 | pub fn importName(index: Index, wasm: *const Wasm) String { |
| 1195 | return index.value(wasm).name; |
| 1196 | } |
| 1197 | |
| 1198 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { |
| 1199 | return index.value(wasm).module_name; |
| 1200 | } |
| 1201 | |
| 1202 | pub fn functionType(index: Index, wasm: *const Wasm) FunctionType.Index { |
| 1203 | return value(index, wasm).type; |
| 1204 | } |
| 1205 | }; |
| 1206 | }; |
| 1207 | |
| 1208 | pub const ObjectFunction = extern struct { |
| 1209 | flags: SymbolFlags, |
| 1210 | /// `none` if this function has no symbol describing it. |
| 1211 | name: OptionalString, |
| 1212 | type_index: FunctionType.Index, |
| 1213 | code: Code, |
| 1214 | /// The offset within the code section where the data starts. |
| 1215 | offset: u32, |
| 1216 | /// The object file whose code section contains this function. |
| 1217 | object_index: ObjectIndex, |
| 1218 | |
| 1219 | pub const Code = DataPayload; |
| 1220 | |
| 1221 | pub fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice { |
| 1222 | const code_section_index = of.object_index.ptr(wasm).code_section_index.?; |
| 1223 | const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty; |
| 1224 | return .init(relocs, of.offset, of.code.len, wasm); |
| 1225 | } |
| 1226 | }; |
| 1227 | |
| 1228 | pub const GlobalImport = extern struct { |
| 1229 | flags: SymbolFlags, |
| 1230 | module_name: OptionalString, |
| 1231 | /// May be different than the key which is a symbol name. |
| 1232 | name: String, |
| 1233 | source_location: SourceLocation, |
| 1234 | resolution: Resolution, |
| 1235 | |
| 1236 | /// Represents a synthetic global, a global from an object, or a global |
| 1237 | /// from the Zcu. |
| 1238 | pub const Resolution = enum(u32) { |
| 1239 | unresolved, |
| 1240 | __heap_base, |
| 1241 | __heap_end, |
| 1242 | __stack_pointer, |
| 1243 | __tls_align, |
| 1244 | __tls_base, |
| 1245 | __tls_size, |
| 1246 | // Next, index into `object_globals`. |
| 1247 | // Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. |
| 1248 | // Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. |
| 1249 | _, |
| 1250 | |
| 1251 | const first_object_global = @backingInt(Resolution.__tls_size) + 1; |
| 1252 | |
| 1253 | pub const Unpacked = union(enum) { |
| 1254 | unresolved, |
| 1255 | __heap_base, |
| 1256 | __heap_end, |
| 1257 | __stack_pointer, |
| 1258 | __tls_align, |
| 1259 | __tls_base, |
| 1260 | __tls_size, |
| 1261 | object_global: ObjectGlobalIndex, |
| 1262 | uav_exe: UavsExeIndex, |
| 1263 | uav_obj: UavsObjIndex, |
| 1264 | nav_exe: NavsExeIndex, |
| 1265 | nav_obj: NavsObjIndex, |
| 1266 | }; |
| 1267 | |
| 1268 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { |
| 1269 | return switch (r) { |
| 1270 | .unresolved => .unresolved, |
| 1271 | .__heap_base => .__heap_base, |
| 1272 | .__heap_end => .__heap_end, |
| 1273 | .__stack_pointer => .__stack_pointer, |
| 1274 | .__tls_align => .__tls_align, |
| 1275 | .__tls_base => .__tls_base, |
| 1276 | .__tls_size => .__tls_size, |
| 1277 | _ => { |
| 1278 | const i: u32 = @backingInt(r); |
| 1279 | const object_global_index = i - first_object_global; |
| 1280 | if (object_global_index < wasm.object_globals.items.len) |
| 1281 | return .{ .object_global = @fromBackingInt(@intCast(object_global_index)) }; |
| 1282 | const comp = wasm.base.comp; |
| 1283 | const is_obj = comp.config.output_mode == .Obj; |
| 1284 | const uav_index = object_global_index - wasm.object_globals.items.len; |
| 1285 | if (is_obj) { |
| 1286 | if (uav_index < wasm.uavs_obj.entries.len) { |
| 1287 | return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }; |
| 1288 | } |
| 1289 | return .{ .nav_obj = @fromBackingInt( |
| 1290 | @intCast(uav_index - wasm.uavs_obj.entries.len), |
| 1291 | ) }; |
| 1292 | } else { |
| 1293 | if (uav_index < wasm.uavs_exe.entries.len) { |
| 1294 | return .{ .uav_exe = @fromBackingInt(@intCast(uav_index)) }; |
| 1295 | } |
| 1296 | return .{ .nav_exe = @fromBackingInt( |
| 1297 | @intCast(uav_index - wasm.uavs_exe.entries.len), |
| 1298 | ) }; |
| 1299 | } |
| 1300 | }, |
| 1301 | }; |
| 1302 | } |
| 1303 | |
| 1304 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { |
| 1305 | return switch (unpacked) { |
| 1306 | .unresolved => .unresolved, |
| 1307 | .__heap_base => .__heap_base, |
| 1308 | .__heap_end => .__heap_end, |
| 1309 | .__stack_pointer => .__stack_pointer, |
| 1310 | .__tls_align => .__tls_align, |
| 1311 | .__tls_base => .__tls_base, |
| 1312 | .__tls_size => .__tls_size, |
| 1313 | .object_global => |i| @fromBackingInt(@intCast(first_object_global + @backingInt(i))), |
| 1314 | inline .uav_obj, .uav_exe => |i| @fromBackingInt(@intCast( |
| 1315 | first_object_global + wasm.object_globals.items.len + @backingInt(i), |
| 1316 | )), |
| 1317 | .nav_obj => |i| @fromBackingInt(@intCast( |
| 1318 | first_object_global + wasm.object_globals.items.len + |
| 1319 | wasm.uavs_obj.entries.len + @backingInt(i), |
| 1320 | )), |
| 1321 | .nav_exe => |i| @fromBackingInt(@intCast( |
| 1322 | first_object_global + wasm.object_globals.items.len + |
| 1323 | wasm.uavs_exe.entries.len + @backingInt(i), |
| 1324 | )), |
| 1325 | }; |
| 1326 | } |
| 1327 | |
| 1328 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { |
| 1329 | const is_obj = wasm.base.comp.config.output_mode == .Obj; |
| 1330 | return pack(wasm, if (is_obj) .{ |
| 1331 | .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)), |
| 1332 | } else .{ |
| 1333 | .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)), |
| 1334 | }); |
| 1335 | } |
| 1336 | |
| 1337 | pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution { |
| 1338 | const comp = wasm.base.comp; |
| 1339 | const is_obj = comp.config.output_mode == .Obj; |
| 1340 | return pack(wasm, if (is_obj) .{ |
| 1341 | .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(ip_nav).?)), |
| 1342 | } else .{ |
| 1343 | .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(ip_nav).?)), |
| 1344 | }); |
| 1345 | } |
| 1346 | |
| 1347 | pub fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution { |
| 1348 | return pack(wasm, .{ .object_global = object_global }); |
| 1349 | } |
| 1350 | |
| 1351 | pub fn flags(r: Resolution, wasm: *const Wasm) SymbolFlags { |
| 1352 | return switch (unpack(r, wasm)) { |
| 1353 | .unresolved, |
| 1354 | .__heap_base, |
| 1355 | .__heap_end, |
| 1356 | .__stack_pointer, |
| 1357 | .__tls_align, |
| 1358 | .__tls_base, |
| 1359 | .__tls_size, |
| 1360 | => unreachable, |
| 1361 | .object_global => |i| i.ptr(wasm).flags, |
| 1362 | .uav_obj, .uav_exe, .nav_obj, .nav_exe => unreachable, |
| 1363 | }; |
| 1364 | } |
| 1365 | |
| 1366 | pub fn name(r: Resolution, wasm: *const Wasm, buf: []u8) ?[]const u8 { |
| 1367 | return switch (unpack(r, wasm)) { |
| 1368 | .unresolved => unreachable, |
| 1369 | .__heap_base => @tagName(Unpacked.__heap_base), |
| 1370 | .__heap_end => @tagName(Unpacked.__heap_end), |
| 1371 | .__stack_pointer => @tagName(Unpacked.__stack_pointer), |
| 1372 | .__tls_align => @tagName(Unpacked.__tls_align), |
| 1373 | .__tls_base => @tagName(Unpacked.__tls_base), |
| 1374 | .__tls_size => @tagName(Unpacked.__tls_size), |
| 1375 | .object_global => |i| i.name(wasm).slice(wasm), |
| 1376 | inline .uav_obj, .uav_exe => |i| std.mem.print(buf, "__anon_{d}", .{i}) catch unreachable, |
| 1377 | .nav_obj => |i| i.name(wasm), |
| 1378 | .nav_exe => |i| i.name(wasm), |
| 1379 | }; |
| 1380 | } |
| 1381 | }; |
| 1382 | |
| 1383 | /// Index into `Wasm.object_global_imports`. |
| 1384 | pub const Index = enum(u32) { |
| 1385 | _, |
| 1386 | |
| 1387 | pub fn key(index: Index, wasm: *const Wasm) *String { |
| 1388 | return &wasm.object_global_imports.keys()[@backingInt(index)]; |
| 1389 | } |
| 1390 | |
| 1391 | pub fn value(index: Index, wasm: *const Wasm) *GlobalImport { |
| 1392 | return &wasm.object_global_imports.values()[@backingInt(index)]; |
| 1393 | } |
| 1394 | |
| 1395 | pub fn symbolName(index: Index, wasm: *const Wasm) String { |
| 1396 | return index.key(wasm).*; |
| 1397 | } |
| 1398 | |
| 1399 | pub fn importName(index: Index, wasm: *const Wasm) String { |
| 1400 | return index.value(wasm).name; |
| 1401 | } |
| 1402 | |
| 1403 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { |
| 1404 | return index.value(wasm).module_name; |
| 1405 | } |
| 1406 | |
| 1407 | pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type { |
| 1408 | return value(index, wasm).type(); |
| 1409 | } |
| 1410 | }; |
| 1411 | |
| 1412 | pub fn @"type"(gi: *const GlobalImport) ObjectGlobal.Type { |
| 1413 | return gi.flags.global_type.to(); |
| 1414 | } |
| 1415 | }; |
| 1416 | |
| 1417 | pub const ObjectGlobal = extern struct { |
| 1418 | /// `none` if this function has no symbol describing it. |
| 1419 | name: OptionalString, |
| 1420 | flags: SymbolFlags, |
| 1421 | expr: Expr, |
| 1422 | /// The object file whose global section contains this global. |
| 1423 | object_index: ObjectIndex, |
| 1424 | offset: u32, |
| 1425 | size: u32, |
| 1426 | |
| 1427 | pub fn @"type"(og: *const ObjectGlobal) Type { |
| 1428 | return og.flags.global_type.to(); |
| 1429 | } |
| 1430 | |
| 1431 | pub const Type = struct { |
| 1432 | valtype: std.wasm.Valtype, |
| 1433 | mutable: bool, |
| 1434 | }; |
| 1435 | |
| 1436 | pub fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice { |
| 1437 | const global_section_index = og.object_index.ptr(wasm).global_section_index.?; |
| 1438 | const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty; |
| 1439 | return .init(relocs, og.offset, og.size, wasm); |
| 1440 | } |
| 1441 | }; |
| 1442 | |
| 1443 | pub const RefType1 = enum(u1) { |
| 1444 | funcref, |
| 1445 | externref, |
| 1446 | |
| 1447 | pub fn from(rt: std.wasm.RefType) RefType1 { |
| 1448 | return switch (rt) { |
| 1449 | .funcref => .funcref, |
| 1450 | .externref => .externref, |
| 1451 | }; |
| 1452 | } |
| 1453 | |
| 1454 | pub fn to(rt: RefType1) std.wasm.RefType { |
| 1455 | return switch (rt) { |
| 1456 | .funcref => .funcref, |
| 1457 | .externref => .externref, |
| 1458 | }; |
| 1459 | } |
| 1460 | }; |
| 1461 | |
| 1462 | pub const TableImport = extern struct { |
| 1463 | flags: SymbolFlags, |
| 1464 | module_name: String, |
| 1465 | /// May be different than the key which is a symbol name. |
| 1466 | name: String, |
| 1467 | source_location: SourceLocation, |
| 1468 | resolution: Resolution, |
| 1469 | limits_min: u32, |
| 1470 | limits_max: u32, |
| 1471 | |
| 1472 | /// Represents a synthetic table, or a table from an object. |
| 1473 | pub const Resolution = enum(u32) { |
| 1474 | unresolved, |
| 1475 | __indirect_function_table, |
| 1476 | // Next, index into `object_tables`. |
| 1477 | _, |
| 1478 | |
| 1479 | const first_object_table = @backingInt(Resolution.__indirect_function_table) + 1; |
| 1480 | |
| 1481 | pub const Unpacked = union(enum) { |
| 1482 | unresolved, |
| 1483 | __indirect_function_table, |
| 1484 | object_table: ObjectTableIndex, |
| 1485 | }; |
| 1486 | |
| 1487 | pub fn unpack(r: Resolution) Unpacked { |
| 1488 | return switch (r) { |
| 1489 | .unresolved => .unresolved, |
| 1490 | .__indirect_function_table => .__indirect_function_table, |
| 1491 | _ => .{ .object_table = @fromBackingInt(@intCast(@backingInt(r) - first_object_table)) }, |
| 1492 | }; |
| 1493 | } |
| 1494 | |
| 1495 | fn pack(unpacked: Unpacked) Resolution { |
| 1496 | return switch (unpacked) { |
| 1497 | .unresolved => .unresolved, |
| 1498 | .__indirect_function_table => .__indirect_function_table, |
| 1499 | .object_table => |i| @fromBackingInt(@intCast(first_object_table + @backingInt(i))), |
| 1500 | }; |
| 1501 | } |
| 1502 | |
| 1503 | fn fromObjectTable(object_table: ObjectTableIndex) Resolution { |
| 1504 | return pack(.{ .object_table = object_table }); |
| 1505 | } |
| 1506 | |
| 1507 | pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { |
| 1508 | return switch (unpack(r)) { |
| 1509 | .unresolved => unreachable, |
| 1510 | .__indirect_function_table => @tagName(Unpacked.__indirect_function_table), |
| 1511 | .object_table => |i| i.ptr(wasm).name.slice(wasm), |
| 1512 | }; |
| 1513 | } |
| 1514 | |
| 1515 | pub fn flags(r: Resolution, wasm: *const Wasm) SymbolFlags { |
| 1516 | return switch (unpack(r)) { |
| 1517 | .unresolved => unreachable, |
| 1518 | .__indirect_function_table => unreachable, |
| 1519 | .object_table => |i| i.ptr(wasm).flags, |
| 1520 | }; |
| 1521 | } |
| 1522 | |
| 1523 | pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType { |
| 1524 | return switch (unpack(r)) { |
| 1525 | .unresolved => unreachable, |
| 1526 | .__indirect_function_table => .funcref, |
| 1527 | .object_table => |i| i.ptr(wasm).flags.ref_type.to(), |
| 1528 | }; |
| 1529 | } |
| 1530 | |
| 1531 | pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits { |
| 1532 | return switch (unpack(r)) { |
| 1533 | .unresolved => unreachable, |
| 1534 | .__indirect_function_table => .{ |
| 1535 | .flags = .{ .has_max = !wasm.growable_table, .is_shared = false }, |
| 1536 | .min = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1), |
| 1537 | .max = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1), |
| 1538 | }, |
| 1539 | .object_table => |i| i.ptr(wasm).limits(), |
| 1540 | }; |
| 1541 | } |
| 1542 | }; |
| 1543 | |
| 1544 | /// Index into `object_table_imports`. |
| 1545 | pub const Index = enum(u32) { |
| 1546 | _, |
| 1547 | |
| 1548 | pub fn key(index: Index, wasm: *const Wasm) *String { |
| 1549 | return &wasm.object_table_imports.keys()[@backingInt(index)]; |
| 1550 | } |
| 1551 | |
| 1552 | pub fn value(index: Index, wasm: *const Wasm) *TableImport { |
| 1553 | return &wasm.object_table_imports.values()[@backingInt(index)]; |
| 1554 | } |
| 1555 | |
| 1556 | pub fn name(index: Index, wasm: *const Wasm) String { |
| 1557 | return index.key(wasm).*; |
| 1558 | } |
| 1559 | |
| 1560 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { |
| 1561 | return index.value(wasm).module_name; |
| 1562 | } |
| 1563 | }; |
| 1564 | |
| 1565 | pub fn limits(ti: *const TableImport) std.wasm.Limits { |
| 1566 | return .{ |
| 1567 | .flags = .{ |
| 1568 | .has_max = ti.flags.limits_has_max, |
| 1569 | .is_shared = ti.flags.limits_is_shared, |
| 1570 | }, |
| 1571 | .min = ti.limits_min, |
| 1572 | .max = ti.limits_max, |
| 1573 | }; |
| 1574 | } |
| 1575 | }; |
| 1576 | |
| 1577 | pub const Table = extern struct { |
| 1578 | module_name: OptionalString, |
| 1579 | name: OptionalString, |
| 1580 | flags: SymbolFlags, |
| 1581 | limits_min: u32, |
| 1582 | limits_max: u32, |
| 1583 | |
| 1584 | pub fn limits(t: *const Table) std.wasm.Limits { |
| 1585 | return .{ |
| 1586 | .flags = .{ |
| 1587 | .has_max = t.flags.limits_has_max, |
| 1588 | .is_shared = t.flags.limits_is_shared, |
| 1589 | }, |
| 1590 | .min = t.limits_min, |
| 1591 | .max = t.limits_max, |
| 1592 | }; |
| 1593 | } |
| 1594 | }; |
| 1595 | |
| 1596 | /// Uniquely identifies a section across all objects. By subtracting |
| 1597 | /// `Object.local_section_index_base` from this one, the Object section index |
| 1598 | /// is obtained. |
| 1599 | pub const ObjectSectionIndex = enum(u32) { |
| 1600 | _, |
| 1601 | }; |
| 1602 | |
| 1603 | /// Index into `object_tables`. |
| 1604 | pub const ObjectTableIndex = enum(u32) { |
| 1605 | _, |
| 1606 | |
| 1607 | pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table { |
| 1608 | return &wasm.object_tables.items[@backingInt(index)]; |
| 1609 | } |
| 1610 | |
| 1611 | pub fn chaseWeak(i: ObjectTableIndex, wasm: *const Wasm) ObjectTableIndex { |
| 1612 | const table = ptr(i, wasm); |
| 1613 | if (table.flags.binding != .weak) return i; |
| 1614 | const name = table.name.unwrap().?; |
| 1615 | const import = wasm.object_table_imports.getPtr(name).?; |
| 1616 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. |
| 1617 | return import.resolution.unpack().object_table; |
| 1618 | } |
| 1619 | }; |
| 1620 | |
| 1621 | /// Index into `Wasm.object_globals`. |
| 1622 | pub const ObjectGlobalIndex = enum(u32) { |
| 1623 | _, |
| 1624 | |
| 1625 | pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal { |
| 1626 | return &wasm.object_globals.items[@backingInt(index)]; |
| 1627 | } |
| 1628 | |
| 1629 | pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString { |
| 1630 | return index.ptr(wasm).name; |
| 1631 | } |
| 1632 | |
| 1633 | pub fn chaseWeak(i: ObjectGlobalIndex, wasm: *const Wasm) ObjectGlobalIndex { |
| 1634 | const global = ptr(i, wasm); |
| 1635 | if (global.flags.binding != .weak) return i; |
| 1636 | const import_name = global.name.unwrap().?; |
| 1637 | const import = wasm.object_global_imports.getPtr(import_name).?; |
| 1638 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. |
| 1639 | return import.resolution.unpack(wasm).object_global; |
| 1640 | } |
| 1641 | }; |
| 1642 | |
| 1643 | pub const ObjectMemory = extern struct { |
| 1644 | flags: SymbolFlags, |
| 1645 | name: OptionalString, |
| 1646 | limits_min: u32, |
| 1647 | limits_max: u32, |
| 1648 | |
| 1649 | /// Index into `Wasm.object_memories`. |
| 1650 | pub const Index = enum(u32) { |
| 1651 | _, |
| 1652 | |
| 1653 | pub fn ptr(index: Index, wasm: *const Wasm) *ObjectMemory { |
| 1654 | return &wasm.object_memories.items[@backingInt(index)]; |
| 1655 | } |
| 1656 | }; |
| 1657 | |
| 1658 | pub fn limits(om: *const ObjectMemory) std.wasm.Limits { |
| 1659 | return .{ |
| 1660 | .flags = .{ |
| 1661 | .has_max = om.limits_has_max, |
| 1662 | .is_shared = om.limits_is_shared, |
| 1663 | }, |
| 1664 | .min = om.limits_min, |
| 1665 | .max = om.limits_max, |
| 1666 | }; |
| 1667 | } |
| 1668 | }; |
| 1669 | |
| 1670 | /// Index into `Wasm.object_functions`. |
| 1671 | pub const ObjectFunctionIndex = enum(u32) { |
| 1672 | _, |
| 1673 | |
| 1674 | pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction { |
| 1675 | return &wasm.object_functions.items[@backingInt(index)]; |
| 1676 | } |
| 1677 | |
| 1678 | pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex { |
| 1679 | const result: OptionalObjectFunctionIndex = @fromBackingInt(@intCast(@backingInt(i))); |
| 1680 | assert(result != .none); |
| 1681 | return result; |
| 1682 | } |
| 1683 | |
| 1684 | pub fn chaseWeak(i: ObjectFunctionIndex, wasm: *const Wasm) ObjectFunctionIndex { |
| 1685 | const func = ptr(i, wasm); |
| 1686 | if (func.flags.binding != .weak) return i; |
| 1687 | const name = func.name.unwrap().?; |
| 1688 | const import = wasm.object_function_imports.getPtr(name).?; |
| 1689 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. |
| 1690 | return import.resolution.unpack(wasm).object_function; |
| 1691 | } |
| 1692 | }; |
| 1693 | |
| 1694 | /// Index into `object_functions`, or null. |
| 1695 | pub const OptionalObjectFunctionIndex = enum(u32) { |
| 1696 | none = std.math.maxInt(u32), |
| 1697 | _, |
| 1698 | |
| 1699 | pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex { |
| 1700 | if (i == .none) return null; |
| 1701 | return @fromBackingInt(@intCast(@backingInt(i))); |
| 1702 | } |
| 1703 | }; |
| 1704 | |
| 1705 | pub const ObjectDataSegment = extern struct { |
| 1706 | /// `none` if segment info custom subsection is missing. |
| 1707 | name: OptionalString, |
| 1708 | flags: Flags, |
| 1709 | payload: DataPayload, |
| 1710 | offset: u32, |
| 1711 | object_index: ObjectIndex, |
| 1712 | |
| 1713 | pub const Flags = packed struct(u32) { |
| 1714 | alive: bool = false, |
| 1715 | is_passive: bool = false, |
| 1716 | alignment: Alignment = .none, |
| 1717 | /// Signals that the segment contains only null terminated strings allowing |
| 1718 | /// the linker to perform merging. |
| 1719 | strings: bool = false, |
| 1720 | /// The segment contains thread-local data. This means that a unique copy |
| 1721 | /// of this segment will be created for each thread. |
| 1722 | tls: bool = false, |
| 1723 | /// If the object file is included in the final link, the segment should be |
| 1724 | /// retained in the final output regardless of whether it is used by the |
| 1725 | /// program. |
| 1726 | retain: bool = false, |
| 1727 | |
| 1728 | _: u21 = 0, |
| 1729 | }; |
| 1730 | |
| 1731 | /// Index into `Wasm.object_data_segments`. |
| 1732 | pub const Index = enum(u32) { |
| 1733 | _, |
| 1734 | |
| 1735 | pub fn ptr(i: Index, wasm: *const Wasm) *ObjectDataSegment { |
| 1736 | return &wasm.object_data_segments.items[@backingInt(i)]; |
| 1737 | } |
| 1738 | }; |
| 1739 | |
| 1740 | pub fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice { |
| 1741 | const data_section_index = ods.object_index.ptr(wasm).data_section_index.?; |
| 1742 | const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty; |
| 1743 | return .init(relocs, ods.offset, ods.payload.len, wasm); |
| 1744 | } |
| 1745 | }; |
| 1746 | |
| 1747 | /// A local or exported global const from an object file. |
| 1748 | pub const ObjectData = extern struct { |
| 1749 | segment: ObjectDataSegment.Index, |
| 1750 | /// Index into the object segment payload. Must be <= the segment's size. |
| 1751 | offset: u32, |
| 1752 | /// May be zero. `offset + size` must be <= the segment's size. |
| 1753 | size: u32, |
| 1754 | name: String, |
| 1755 | flags: SymbolFlags, |
| 1756 | |
| 1757 | /// Index into `Wasm.object_datas`. |
| 1758 | pub const Index = enum(u32) { |
| 1759 | _, |
| 1760 | |
| 1761 | pub fn ptr(i: Index, wasm: *const Wasm) *ObjectData { |
| 1762 | return &wasm.object_datas.items[@backingInt(i)]; |
| 1763 | } |
| 1764 | }; |
| 1765 | }; |
| 1766 | |
| 1767 | pub const ObjectDataImport = extern struct { |
| 1768 | resolution: Resolution, |
| 1769 | flags: SymbolFlags, |
| 1770 | source_location: SourceLocation, |
| 1771 | |
| 1772 | pub const Resolution = enum(u32) { |
| 1773 | unresolved, |
| 1774 | __zig_error_names, |
| 1775 | __zig_error_name_table, |
| 1776 | __zig_tag_names, |
| 1777 | __zig_tag_name_table, |
| 1778 | __global_base, |
| 1779 | __heap_base, |
| 1780 | __heap_end, |
| 1781 | __wasm_first_page_end, |
| 1782 | /// Next, an `ObjectData.Index`. |
| 1783 | /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. |
| 1784 | /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. |
| 1785 | _, |
| 1786 | |
| 1787 | const first_object = @backingInt(Resolution.__wasm_first_page_end) + 1; |
| 1788 | |
| 1789 | pub const Unpacked = union(enum) { |
| 1790 | unresolved, |
| 1791 | __zig_error_names, |
| 1792 | __zig_error_name_table, |
| 1793 | __zig_tag_names, |
| 1794 | __zig_tag_name_table, |
| 1795 | __global_base, |
| 1796 | __heap_base, |
| 1797 | __heap_end, |
| 1798 | __wasm_first_page_end, |
| 1799 | object: ObjectData.Index, |
| 1800 | uav_exe: UavsExeIndex, |
| 1801 | uav_obj: UavsObjIndex, |
| 1802 | nav_exe: NavsExeIndex, |
| 1803 | nav_obj: NavsObjIndex, |
| 1804 | }; |
| 1805 | |
| 1806 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { |
| 1807 | return switch (r) { |
| 1808 | .unresolved => .unresolved, |
| 1809 | .__zig_error_names => .__zig_error_names, |
| 1810 | .__zig_error_name_table => .__zig_error_name_table, |
| 1811 | .__zig_tag_names => .__zig_tag_names, |
| 1812 | .__zig_tag_name_table => .__zig_tag_name_table, |
| 1813 | .__global_base => .__global_base, |
| 1814 | .__heap_base => .__heap_base, |
| 1815 | .__heap_end => .__heap_end, |
| 1816 | .__wasm_first_page_end => .__wasm_first_page_end, |
| 1817 | _ => { |
| 1818 | const object_index = @backingInt(r) - first_object; |
| 1819 | |
| 1820 | const uav_index = if (object_index < wasm.object_datas.items.len) |
| 1821 | return .{ .object = @fromBackingInt(@intCast(object_index)) } |
| 1822 | else |
| 1823 | object_index - wasm.object_datas.items.len; |
| 1824 | |
| 1825 | const comp = wasm.base.comp; |
| 1826 | const is_obj = comp.config.output_mode == .Obj; |
| 1827 | if (is_obj) { |
| 1828 | const nav_index = if (uav_index < wasm.uavs_obj.entries.len) |
| 1829 | return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) } |
| 1830 | else |
| 1831 | uav_index - wasm.uavs_obj.entries.len; |
| 1832 | |
| 1833 | return .{ .nav_obj = @fromBackingInt(@intCast(nav_index)) }; |
| 1834 | } else { |
| 1835 | const nav_index = if (uav_index < wasm.uavs_exe.entries.len) |
| 1836 | return .{ .uav_exe = @fromBackingInt(@intCast(uav_index)) } |
| 1837 | else |
| 1838 | uav_index - wasm.uavs_exe.entries.len; |
| 1839 | |
| 1840 | return .{ .nav_exe = @fromBackingInt(@intCast(nav_index)) }; |
| 1841 | } |
| 1842 | }, |
| 1843 | }; |
| 1844 | } |
| 1845 | |
| 1846 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { |
| 1847 | return switch (unpacked) { |
| 1848 | .unresolved => .unresolved, |
| 1849 | .__zig_error_names => .__zig_error_names, |
| 1850 | .__zig_error_name_table => .__zig_error_name_table, |
| 1851 | .__zig_tag_names => .__zig_tag_names, |
| 1852 | .__zig_tag_name_table => .__zig_tag_name_table, |
| 1853 | .__global_base => .__global_base, |
| 1854 | .__heap_base => .__heap_base, |
| 1855 | .__heap_end => .__heap_end, |
| 1856 | .__wasm_first_page_end => .__wasm_first_page_end, |
| 1857 | .object => |i| @fromBackingInt(@intCast(first_object + @backingInt(i))), |
| 1858 | inline .uav_exe, .uav_obj => |i| @fromBackingInt(@intCast(first_object + wasm.object_datas.items.len + @backingInt(i))), |
| 1859 | .nav_exe => |i| @fromBackingInt(@intCast(first_object + wasm.object_datas.items.len + wasm.uavs_exe.entries.len + @backingInt(i))), |
| 1860 | .nav_obj => |i| @fromBackingInt(@intCast(first_object + wasm.object_datas.items.len + wasm.uavs_obj.entries.len + @backingInt(i))), |
| 1861 | }; |
| 1862 | } |
| 1863 | |
| 1864 | pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution { |
| 1865 | return pack(wasm, .{ .object = object_data_index }); |
| 1866 | } |
| 1867 | |
| 1868 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { |
| 1869 | const is_obj = wasm.base.comp.config.output_mode == .Obj; |
| 1870 | return pack(wasm, if (is_obj) .{ |
| 1871 | .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)), |
| 1872 | } else .{ |
| 1873 | .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)), |
| 1874 | }); |
| 1875 | } |
| 1876 | |
| 1877 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution { |
| 1878 | const is_obj = wasm.base.comp.config.output_mode == .Obj; |
| 1879 | return pack(wasm, if (is_obj) .{ |
| 1880 | .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)), |
| 1881 | } else .{ |
| 1882 | .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)), |
| 1883 | }); |
| 1884 | } |
| 1885 | |
| 1886 | pub fn objectDataSegment(r: Resolution, wasm: *const Wasm) ?ObjectDataSegment.Index { |
| 1887 | return switch (unpack(r, wasm)) { |
| 1888 | .unresolved => unreachable, |
| 1889 | .object => |i| i.ptr(wasm).segment, |
| 1890 | .__zig_error_names, |
| 1891 | .__zig_error_name_table, |
| 1892 | .__zig_tag_names, |
| 1893 | .__zig_tag_name_table, |
| 1894 | .__global_base, |
| 1895 | .__heap_base, |
| 1896 | .__heap_end, |
| 1897 | .__wasm_first_page_end, |
| 1898 | .uav_exe, |
| 1899 | .uav_obj, |
| 1900 | .nav_exe, |
| 1901 | .nav_obj, |
| 1902 | => null, |
| 1903 | }; |
| 1904 | } |
| 1905 | |
| 1906 | pub fn dataLoc(r: Resolution, wasm: *const Wasm) DataLoc { |
| 1907 | return switch (unpack(r, wasm)) { |
| 1908 | .unresolved => unreachable, |
| 1909 | .object => |i| { |
| 1910 | const ptr = i.ptr(wasm); |
| 1911 | return .{ |
| 1912 | .segment = .fromObjectDataSegment(wasm, ptr.segment), |
| 1913 | .offset = ptr.offset, |
| 1914 | }; |
| 1915 | }, |
| 1916 | .__zig_error_names => .{ .segment = .__zig_error_names, .offset = 0 }, |
| 1917 | .__zig_error_name_table => .{ .segment = .__zig_error_name_table, .offset = 0 }, |
| 1918 | .__zig_tag_names => .{ .segment = .__zig_tag_names, .offset = 0 }, |
| 1919 | .__zig_tag_name_table => .{ .segment = .__zig_tag_name_table, .offset = 0 }, |
| 1920 | .__global_base, |
| 1921 | .__heap_base, |
| 1922 | .__heap_end, |
| 1923 | .__wasm_first_page_end, |
| 1924 | => unreachable, |
| 1925 | .uav_exe => |i| .{ .segment = .pack(wasm, .{ .uav_exe = i }), .offset = 0 }, |
| 1926 | .uav_obj => |i| .{ .segment = .pack(wasm, .{ .uav_obj = i }), .offset = 0 }, |
| 1927 | .nav_exe => |i| .{ .segment = .pack(wasm, .{ .nav_exe = i }), .offset = 0 }, |
| 1928 | .nav_obj => |i| .{ .segment = .pack(wasm, .{ .nav_obj = i }), .offset = 0 }, |
| 1929 | }; |
| 1930 | } |
| 1931 | |
| 1932 | pub fn flags(r: Resolution, wasm: *const Wasm) SymbolFlags { |
| 1933 | return switch (unpack(r, wasm)) { |
| 1934 | .unresolved => unreachable, |
| 1935 | .__zig_error_names, |
| 1936 | .__zig_error_name_table, |
| 1937 | .__zig_tag_names, |
| 1938 | .__zig_tag_name_table, |
| 1939 | => .{ .binding = .local }, |
| 1940 | .__global_base, |
| 1941 | .__heap_base, |
| 1942 | .__heap_end, |
| 1943 | .__wasm_first_page_end, |
| 1944 | => unreachable, |
| 1945 | .object => |i| i.ptr(wasm).flags, |
| 1946 | inline .nav_exe, .nav_obj => |i| { |
| 1947 | const zcu = wasm.base.comp.zcu.?; |
| 1948 | const ip = &zcu.intern_pool; |
| 1949 | const nav = ip.getNav(i.key(wasm).*); |
| 1950 | if (nav.getExtern(ip)) |ext| { |
| 1951 | const name_slice = ext.name.toSlice(ip); |
| 1952 | const name_string = wasm.getExistingString(name_slice).?; |
| 1953 | return .{ |
| 1954 | .binding = switch (ext.linkage) { |
| 1955 | .internal => .local, |
| 1956 | .strong => .strong, |
| 1957 | .weak => .weak, |
| 1958 | .link_once => @panic("TODO: COMDAT"), |
| 1959 | }, |
| 1960 | .visibility_hidden = switch (ext.visibility) { |
| 1961 | .default => false, |
| 1962 | .hidden => true, |
| 1963 | .protected => false, |
| 1964 | }, |
| 1965 | .undefined = false, |
| 1966 | .exported = wasm.missing_exports.contains(name_string), |
| 1967 | .explicit_name = false, |
| 1968 | .no_strip = false, |
| 1969 | .tls = ext.is_threadlocal, |
| 1970 | .absolute = false, |
| 1971 | }; |
| 1972 | } else { |
| 1973 | return .{ |
| 1974 | .binding = .local, |
| 1975 | .tls = nav.resolved.?.@"threadlocal", |
| 1976 | }; |
| 1977 | } |
| 1978 | }, |
| 1979 | .uav_exe, .uav_obj => .{ .binding = .local }, |
| 1980 | }; |
| 1981 | } |
| 1982 | |
| 1983 | pub fn name(r: Resolution, wasm: *const Wasm, buf: []u8) []const u8 { |
| 1984 | return switch (unpack(r, wasm)) { |
| 1985 | .unresolved => unreachable, |
| 1986 | .object => |i| i.ptr(wasm).name.slice(wasm), |
| 1987 | .__zig_error_names => @tagName(.__zig_error_names), |
| 1988 | .__zig_error_name_table => @tagName(.__zig_error_name_table), |
| 1989 | .__zig_tag_names => @tagName(.__zig_tag_names), |
| 1990 | .__zig_tag_name_table => @tagName(.__zig_tag_name_table), |
| 1991 | .__global_base => @tagName(.__global_base), |
| 1992 | .__heap_base => @tagName(.__heap_base), |
| 1993 | .__heap_end => @tagName(.__heap_end), |
| 1994 | .__wasm_first_page_end => @tagName(.__wasm_first_page_end), |
| 1995 | inline .uav_exe, .uav_obj => |i| std.mem.print(buf, "__anon_{d}", .{i}) catch unreachable, |
| 1996 | inline .nav_exe, .nav_obj => |i| i.name(wasm), |
| 1997 | }; |
| 1998 | } |
| 1999 | |
| 2000 | pub fn size(r: Resolution, wasm: *const Wasm) u32 { |
| 2001 | return switch (unpack(r, wasm)) { |
| 2002 | .unresolved => unreachable, |
| 2003 | .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), |
| 2004 | .__zig_error_name_table => { |
| 2005 | const comp = wasm.base.comp; |
| 2006 | const zcu = comp.zcu.?; |
| 2007 | const errors_len = wasm.error_name_offs.items.len; |
| 2008 | const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); |
| 2009 | return @intCast(errors_len * elem_size); |
| 2010 | }, |
| 2011 | .__zig_tag_names => @intCast(wasm.tag_name_bytes.items.len), |
| 2012 | .__zig_tag_name_table => { |
| 2013 | const comp = wasm.base.comp; |
| 2014 | const zcu = comp.zcu.?; |
| 2015 | const table_len = wasm.tag_name_offs.items.len; |
| 2016 | const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); |
| 2017 | return @intCast(table_len * elem_size); |
| 2018 | }, |
| 2019 | .__global_base, .__heap_base, .__heap_end, .__wasm_first_page_end => 0, |
| 2020 | .object => |i| i.ptr(wasm).size, |
| 2021 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, |
| 2022 | }; |
| 2023 | } |
| 2024 | }; |
| 2025 | |
| 2026 | /// Points into `Wasm.object_data_imports`. |
| 2027 | pub const Index = enum(u32) { |
| 2028 | _, |
| 2029 | |
| 2030 | pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport { |
| 2031 | return &wasm.object_data_imports.values()[@backingInt(i)]; |
| 2032 | } |
| 2033 | |
| 2034 | pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index { |
| 2035 | return @fromBackingInt(@intCast(wasm.object_data_imports.getIndex(name) orelse return null)); |
| 2036 | } |
| 2037 | }; |
| 2038 | }; |
| 2039 | |
| 2040 | pub const DataPayload = extern struct { |
| 2041 | off: Off, |
| 2042 | /// The size in bytes of the data representing the segment within the section. |
| 2043 | len: u32, |
| 2044 | |
| 2045 | pub const Off = enum(u32) { |
| 2046 | /// The payload is all zeroes (bss section). |
| 2047 | none = std.math.maxInt(u32), |
| 2048 | /// Points into string_bytes. No corresponding string_table entry. |
| 2049 | _, |
| 2050 | |
| 2051 | pub fn unwrap(off: Off) ?u32 { |
| 2052 | return if (off == .none) null else @backingInt(off); |
| 2053 | } |
| 2054 | }; |
| 2055 | |
| 2056 | pub fn slice(p: DataPayload, wasm: *const Wasm) []const u8 { |
| 2057 | return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len]; |
| 2058 | } |
| 2059 | }; |
| 2060 | |
| 2061 | /// A reference to a local or exported global const. |
| 2062 | pub const DataSegmentId = enum(u32) { |
| 2063 | __zig_error_names, |
| 2064 | __zig_error_name_table, |
| 2065 | /// All name string bytes for all `@tagName` implementations, concatenated together. |
| 2066 | __zig_tag_names, |
| 2067 | /// All tag name slices for all `@tagName` implementations, concatenated together. |
| 2068 | __zig_tag_name_table, |
| 2069 | /// First, an `ObjectDataSegment.Index`. |
| 2070 | /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. |
| 2071 | /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. |
| 2072 | _, |
| 2073 | |
| 2074 | const first_object = @backingInt(DataSegmentId.__zig_tag_name_table) + 1; |
| 2075 | |
| 2076 | pub const Category = enum { |
| 2077 | /// Thread-local variables. |
| 2078 | tls, |
| 2079 | /// Data that is not zero initialized and not threadlocal. |
| 2080 | data, |
| 2081 | /// Zero-initialized. Does not require corresponding bytes in the |
| 2082 | /// output file. |
| 2083 | zero, |
| 2084 | }; |
| 2085 | |
| 2086 | pub const Unpacked = union(enum) { |
| 2087 | __zig_error_names, |
| 2088 | __zig_error_name_table, |
| 2089 | __zig_tag_names, |
| 2090 | __zig_tag_name_table, |
| 2091 | object: ObjectDataSegment.Index, |
| 2092 | uav_exe: UavsExeIndex, |
| 2093 | uav_obj: UavsObjIndex, |
| 2094 | nav_exe: NavsExeIndex, |
| 2095 | nav_obj: NavsObjIndex, |
| 2096 | }; |
| 2097 | |
| 2098 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) DataSegmentId { |
| 2099 | return switch (unpacked) { |
| 2100 | .__zig_error_names => .__zig_error_names, |
| 2101 | .__zig_error_name_table => .__zig_error_name_table, |
| 2102 | .__zig_tag_names => .__zig_tag_names, |
| 2103 | .__zig_tag_name_table => .__zig_tag_name_table, |
| 2104 | .object => |i| @fromBackingInt(@intCast(first_object + @backingInt(i))), |
| 2105 | inline .uav_exe, .uav_obj => |i| @fromBackingInt(@intCast(first_object + wasm.object_data_segments.items.len + @backingInt(i))), |
| 2106 | .nav_exe => |i| @fromBackingInt(@intCast(first_object + wasm.object_data_segments.items.len + wasm.uavs_exe.entries.len + @backingInt(i))), |
| 2107 | .nav_obj => |i| @fromBackingInt(@intCast(first_object + wasm.object_data_segments.items.len + wasm.uavs_obj.entries.len + @backingInt(i))), |
| 2108 | }; |
| 2109 | } |
| 2110 | |
| 2111 | pub fn unpack(id: DataSegmentId, wasm: *const Wasm) Unpacked { |
| 2112 | return switch (id) { |
| 2113 | .__zig_error_names => .__zig_error_names, |
| 2114 | .__zig_error_name_table => .__zig_error_name_table, |
| 2115 | .__zig_tag_names => .__zig_tag_names, |
| 2116 | .__zig_tag_name_table => .__zig_tag_name_table, |
| 2117 | _ => { |
| 2118 | const object_index = @backingInt(id) - first_object; |
| 2119 | |
| 2120 | const uav_index = if (object_index < wasm.object_data_segments.items.len) |
| 2121 | return .{ .object = @fromBackingInt(@intCast(object_index)) } |
| 2122 | else |
| 2123 | object_index - wasm.object_data_segments.items.len; |
| 2124 | |
| 2125 | const comp = wasm.base.comp; |
| 2126 | const is_obj = comp.config.output_mode == .Obj; |
| 2127 | if (is_obj) { |
| 2128 | const nav_index = if (uav_index < wasm.uavs_obj.entries.len) |
| 2129 | return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) } |
| 2130 | else |
| 2131 | uav_index - wasm.uavs_obj.entries.len; |
| 2132 | |
| 2133 | return .{ .nav_obj = @fromBackingInt(@intCast(nav_index)) }; |
| 2134 | } else { |
| 2135 | const nav_index = if (uav_index < wasm.uavs_exe.entries.len) |
| 2136 | return .{ .uav_exe = @fromBackingInt(@intCast(uav_index)) } |
| 2137 | else |
| 2138 | uav_index - wasm.uavs_exe.entries.len; |
| 2139 | |
| 2140 | return .{ .nav_exe = @fromBackingInt(@intCast(nav_index)) }; |
| 2141 | } |
| 2142 | }, |
| 2143 | }; |
| 2144 | } |
| 2145 | |
| 2146 | pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId { |
| 2147 | const comp = wasm.base.comp; |
| 2148 | const is_obj = comp.config.output_mode == .Obj; |
| 2149 | return pack(wasm, if (is_obj) .{ |
| 2150 | .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)), |
| 2151 | } else .{ |
| 2152 | .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)), |
| 2153 | }); |
| 2154 | } |
| 2155 | |
| 2156 | pub fn fromObjectDataSegment(wasm: *const Wasm, object_data_segment: ObjectDataSegment.Index) DataSegmentId { |
| 2157 | return pack(wasm, .{ .object = object_data_segment }); |
| 2158 | } |
| 2159 | |
| 2160 | pub fn category(id: DataSegmentId, wasm: *const Wasm) Category { |
| 2161 | return switch (unpack(id, wasm)) { |
| 2162 | .__zig_error_names, |
| 2163 | .__zig_error_name_table, |
| 2164 | .__zig_tag_names, |
| 2165 | .__zig_tag_name_table, |
| 2166 | => .data, |
| 2167 | |
| 2168 | .object => |i| { |
| 2169 | const ptr = i.ptr(wasm); |
| 2170 | if (ptr.flags.tls) return .tls; |
| 2171 | if (wasm.isBss(ptr.name)) return .zero; |
| 2172 | return .data; |
| 2173 | }, |
| 2174 | inline .uav_exe, .uav_obj => |i| if (i.value(wasm).code.off == .none) .zero else .data, |
| 2175 | inline .nav_exe, .nav_obj => |i| { |
| 2176 | const zcu = wasm.base.comp.zcu.?; |
| 2177 | const ip = &zcu.intern_pool; |
| 2178 | const nav = ip.getNav(i.key(wasm).*); |
| 2179 | if (nav.resolved.?.@"threadlocal") return .tls; |
| 2180 | const code = i.value(wasm).code; |
| 2181 | return if (code.off == .none) .zero else .data; |
| 2182 | }, |
| 2183 | }; |
| 2184 | } |
| 2185 | |
| 2186 | pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2187 | return switch (unpack(id, wasm)) { |
| 2188 | .__zig_error_names, |
| 2189 | .__zig_error_name_table, |
| 2190 | .__zig_tag_names, |
| 2191 | .__zig_tag_name_table, |
| 2192 | => false, |
| 2193 | |
| 2194 | .object => |i| i.ptr(wasm).flags.tls, |
| 2195 | .uav_exe, .uav_obj => false, |
| 2196 | inline .nav_exe, .nav_obj => |i| { |
| 2197 | const zcu = wasm.base.comp.zcu.?; |
| 2198 | const ip = &zcu.intern_pool; |
| 2199 | const nav = ip.getNav(i.key(wasm).*); |
| 2200 | return nav.resolved.?.@"threadlocal"; |
| 2201 | }, |
| 2202 | }; |
| 2203 | } |
| 2204 | |
| 2205 | pub fn isStrings(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2206 | return switch (unpack(id, wasm)) { |
| 2207 | .__zig_error_names, .__zig_tag_names => true, |
| 2208 | |
| 2209 | .__zig_error_name_table, |
| 2210 | .__zig_tag_name_table, |
| 2211 | => false, |
| 2212 | |
| 2213 | .object => |i| i.ptr(wasm).flags.strings, |
| 2214 | .uav_exe, .uav_obj => false, |
| 2215 | .nav_exe, .nav_obj => false, |
| 2216 | }; |
| 2217 | } |
| 2218 | |
| 2219 | pub fn isRetain(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2220 | return switch (unpack(id, wasm)) { |
| 2221 | .__zig_error_names, |
| 2222 | .__zig_error_name_table, |
| 2223 | .__zig_tag_names, |
| 2224 | .__zig_tag_name_table, |
| 2225 | => false, |
| 2226 | |
| 2227 | .object => |i| i.ptr(wasm).flags.retain, |
| 2228 | .uav_exe, .uav_obj => false, |
| 2229 | .nav_exe, .nav_obj => false, |
| 2230 | }; |
| 2231 | } |
| 2232 | |
| 2233 | pub fn isBss(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2234 | return id.category(wasm) == .zero; |
| 2235 | } |
| 2236 | |
| 2237 | pub fn name(id: DataSegmentId, wasm: *const Wasm) []const u8 { |
| 2238 | return switch (unpack(id, wasm)) { |
| 2239 | .__zig_error_names, |
| 2240 | .__zig_error_name_table, |
| 2241 | .__zig_tag_names, |
| 2242 | .__zig_tag_name_table, |
| 2243 | .uav_exe, |
| 2244 | .uav_obj, |
| 2245 | => ".data", |
| 2246 | |
| 2247 | .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm), |
| 2248 | inline .nav_exe, .nav_obj => |i| { |
| 2249 | const zcu = wasm.base.comp.zcu.?; |
| 2250 | const ip = &zcu.intern_pool; |
| 2251 | const nav = ip.getNav(i.key(wasm).*); |
| 2252 | return nav.resolved.?.@"linksection".toSlice(ip) orelse switch (category(id, wasm)) { |
| 2253 | .tls => ".tdata", |
| 2254 | .data => ".data", |
| 2255 | .zero => ".bss", |
| 2256 | }; |
| 2257 | }, |
| 2258 | }; |
| 2259 | } |
| 2260 | |
| 2261 | pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment { |
| 2262 | return switch (unpack(id, wasm)) { |
| 2263 | .__zig_error_names, .__zig_tag_names => .@"1", |
| 2264 | .__zig_error_name_table, .__zig_tag_name_table => wasm.pointerAlignment(), |
| 2265 | .object => |i| i.ptr(wasm).flags.alignment, |
| 2266 | inline .uav_exe, .uav_obj => |i| { |
| 2267 | const zcu = wasm.base.comp.zcu.?; |
| 2268 | const ip = &zcu.intern_pool; |
| 2269 | const ip_index = i.key(wasm).*; |
| 2270 | if (wasm.overaligned_uavs.get(ip_index)) |a| return a; |
| 2271 | const ty: Zcu.Type = .fromInterned(ip.typeOf(ip_index)); |
| 2272 | const result = ty.abiAlignment(zcu); |
| 2273 | assert(result != .none); |
| 2274 | return result; |
| 2275 | }, |
| 2276 | inline .nav_exe, .nav_obj => |i| { |
| 2277 | const zcu = wasm.base.comp.zcu.?; |
| 2278 | const ip = &zcu.intern_pool; |
| 2279 | const nav = ip.getNav(i.key(wasm).*); |
| 2280 | const explicit = nav.resolved.?.@"align"; |
| 2281 | if (explicit != .none) return explicit; |
| 2282 | const ty: Zcu.Type = .fromInterned(nav.resolved.?.type); |
| 2283 | const result = ty.abiAlignment(zcu); |
| 2284 | assert(result != .none); |
| 2285 | return result; |
| 2286 | }, |
| 2287 | }; |
| 2288 | } |
| 2289 | |
| 2290 | pub fn refCount(id: DataSegmentId, wasm: *const Wasm) u32 { |
| 2291 | return switch (unpack(id, wasm)) { |
| 2292 | .__zig_error_names => @intCast(wasm.error_name_offs.items.len), |
| 2293 | .__zig_error_name_table => wasm.error_name_table_ref_count, |
| 2294 | .__zig_tag_names => @intCast(wasm.tag_name_offs.items.len), |
| 2295 | .__zig_tag_name_table => wasm.tag_name_table_ref_count, |
| 2296 | .object, .uav_obj, .nav_obj => 0, |
| 2297 | inline .uav_exe, .nav_exe => |i| i.value(wasm).count, |
| 2298 | }; |
| 2299 | } |
| 2300 | |
| 2301 | pub fn isPassive(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2302 | const comp = wasm.base.comp; |
| 2303 | if (comp.config.import_memory) return true; |
| 2304 | return switch (unpack(id, wasm)) { |
| 2305 | .__zig_error_names, |
| 2306 | .__zig_error_name_table, |
| 2307 | .__zig_tag_names, |
| 2308 | .__zig_tag_name_table, |
| 2309 | => false, |
| 2310 | |
| 2311 | .object => |i| i.ptr(wasm).flags.is_passive, |
| 2312 | .uav_exe, .uav_obj, .nav_exe, .nav_obj => false, |
| 2313 | }; |
| 2314 | } |
| 2315 | |
| 2316 | pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2317 | return switch (unpack(id, wasm)) { |
| 2318 | .__zig_error_names, |
| 2319 | .__zig_error_name_table, |
| 2320 | .__zig_tag_names, |
| 2321 | .__zig_tag_name_table, |
| 2322 | => false, |
| 2323 | |
| 2324 | .object => |i| i.ptr(wasm).payload.off == .none, |
| 2325 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none, |
| 2326 | }; |
| 2327 | } |
| 2328 | |
| 2329 | pub fn size(id: DataSegmentId, wasm: *const Wasm) u32 { |
| 2330 | return switch (unpack(id, wasm)) { |
| 2331 | .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), |
| 2332 | .__zig_error_name_table => { |
| 2333 | const comp = wasm.base.comp; |
| 2334 | const zcu = comp.zcu.?; |
| 2335 | const errors_len = wasm.error_name_offs.items.len; |
| 2336 | const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); |
| 2337 | return @intCast(errors_len * elem_size); |
| 2338 | }, |
| 2339 | .__zig_tag_names => @intCast(wasm.tag_name_bytes.items.len), |
| 2340 | .__zig_tag_name_table => { |
| 2341 | const comp = wasm.base.comp; |
| 2342 | const zcu = comp.zcu.?; |
| 2343 | const table_len = wasm.tag_name_offs.items.len; |
| 2344 | const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); |
| 2345 | return @intCast(table_len * elem_size); |
| 2346 | }, |
| 2347 | .object => |i| i.ptr(wasm).payload.len, |
| 2348 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, |
| 2349 | }; |
| 2350 | } |
| 2351 | }; |
| 2352 | |
| 2353 | pub const DataLoc = struct { |
| 2354 | segment: Wasm.DataSegmentId, |
| 2355 | offset: u32, |
| 2356 | |
| 2357 | pub fn fromObjectDataIndex(wasm: *const Wasm, i: Wasm.ObjectData.Index) DataLoc { |
| 2358 | const ptr = i.ptr(wasm); |
| 2359 | return .{ |
| 2360 | .segment = .fromObjectDataSegment(wasm, ptr.segment), |
| 2361 | .offset = ptr.offset, |
| 2362 | }; |
| 2363 | } |
| 2364 | |
| 2365 | pub fn fromDataImportId(wasm: *const Wasm, id: Wasm.DataImportId) DataLoc { |
| 2366 | return switch (id.unpack(wasm)) { |
| 2367 | .object_data_import => |i| .fromObjectDataImportIndex(wasm, i), |
| 2368 | .zcu_import => |i| .fromZcuImport(wasm, i), |
| 2369 | }; |
| 2370 | } |
| 2371 | |
| 2372 | pub fn fromObjectDataImportIndex(wasm: *const Wasm, i: Wasm.ObjectDataImport.Index) DataLoc { |
| 2373 | return i.value(wasm).resolution.dataLoc(wasm); |
| 2374 | } |
| 2375 | |
| 2376 | pub fn fromZcuImport(wasm: *const Wasm, zcu_import: ZcuImportIndex) DataLoc { |
| 2377 | const nav_index = zcu_import.ptr(wasm).*; |
| 2378 | return .{ |
| 2379 | .segment = .fromNav(wasm, nav_index), |
| 2380 | .offset = 0, |
| 2381 | }; |
| 2382 | } |
| 2383 | }; |
| 2384 | |
| 2385 | /// Index into `Wasm.uavs`. |
| 2386 | pub const UavIndex = enum(u32) { |
| 2387 | _, |
| 2388 | }; |
| 2389 | |
| 2390 | pub const CustomSegment = extern struct { |
| 2391 | payload: Payload, |
| 2392 | flags: SymbolFlags, |
| 2393 | section_name: String, |
| 2394 | |
| 2395 | pub const Payload = DataPayload; |
| 2396 | }; |
| 2397 | |
| 2398 | /// An index into string_bytes where a wasm expression is found. |
| 2399 | pub const Expr = enum(u32) { |
| 2400 | _, |
| 2401 | |
| 2402 | pub const end = @backingInt(std.wasm.Opcode.end); |
| 2403 | |
| 2404 | pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 { |
| 2405 | const start_slice = wasm.string_bytes.items[@backingInt(index)..]; |
| 2406 | const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) { |
| 2407 | error.InvalidInitOpcode => unreachable, |
| 2408 | }; |
| 2409 | return start_slice[0..end_pos :end]; |
| 2410 | } |
| 2411 | }; |
| 2412 | |
| 2413 | pub const FunctionType = extern struct { |
| 2414 | params: ValtypeList, |
| 2415 | returns: ValtypeList, |
| 2416 | |
| 2417 | /// Index into func_types |
| 2418 | pub const Index = enum(u32) { |
| 2419 | _, |
| 2420 | |
| 2421 | pub fn ptr(i: Index, wasm: *const Wasm) *FunctionType { |
| 2422 | return &wasm.func_types.keys()[@backingInt(i)]; |
| 2423 | } |
| 2424 | |
| 2425 | pub fn fmt(i: Index, wasm: *const Wasm) Formatter { |
| 2426 | return i.ptr(wasm).fmt(wasm); |
| 2427 | } |
| 2428 | }; |
| 2429 | |
| 2430 | pub const format = @compileError("can't format without *Wasm reference"); |
| 2431 | |
| 2432 | pub fn eql(a: FunctionType, b: FunctionType) bool { |
| 2433 | return a.params == b.params and a.returns == b.returns; |
| 2434 | } |
| 2435 | |
| 2436 | pub fn fmt(ft: FunctionType, wasm: *const Wasm) Formatter { |
| 2437 | return .{ .wasm = wasm, .ft = ft }; |
| 2438 | } |
| 2439 | |
| 2440 | const Formatter = struct { |
| 2441 | wasm: *const Wasm, |
| 2442 | ft: FunctionType, |
| 2443 | |
| 2444 | pub fn format(self: Formatter, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 2445 | const params = self.ft.params.slice(self.wasm); |
| 2446 | const returns = self.ft.returns.slice(self.wasm); |
| 2447 | |
| 2448 | try writer.writeByte('('); |
| 2449 | for (params, 0..) |param, i| { |
| 2450 | try writer.print("{s}", .{@tagName(param)}); |
| 2451 | if (i + 1 != params.len) { |
| 2452 | try writer.writeAll(", "); |
| 2453 | } |
| 2454 | } |
| 2455 | try writer.writeAll(") -> "); |
| 2456 | if (returns.len == 0) { |
| 2457 | try writer.writeAll("nil"); |
| 2458 | } else { |
| 2459 | for (returns, 0..) |return_ty, i| { |
| 2460 | try writer.print("{s}", .{@tagName(return_ty)}); |
| 2461 | if (i + 1 != returns.len) { |
| 2462 | try writer.writeAll(", "); |
| 2463 | } |
| 2464 | } |
| 2465 | } |
| 2466 | } |
| 2467 | }; |
| 2468 | }; |
| 2469 | |
| 2470 | /// Represents a function entry, holding the index to its type |
| 2471 | pub const Func = extern struct { |
| 2472 | type_index: FunctionType.Index, |
| 2473 | }; |
| 2474 | |
| 2475 | /// Type reflection is used on the field names to autopopulate each field |
| 2476 | /// during initialization. |
| 2477 | const PreloadedStrings = struct { |
| 2478 | __global_base: String, |
| 2479 | __heap_base: String, |
| 2480 | __heap_end: String, |
| 2481 | __indirect_function_table: String, |
| 2482 | __linear_memory: String, |
| 2483 | __stack_pointer: String, |
| 2484 | __tls_align: String, |
| 2485 | __tls_base: String, |
| 2486 | __tls_size: String, |
| 2487 | __wasm_apply_global_tls_relocs: String, |
| 2488 | __wasm_call_ctors: String, |
| 2489 | __wasm_init_memory: String, |
| 2490 | __wasm_init_memory_flag: String, |
| 2491 | __wasm_init_tls: String, |
| 2492 | __wasm_first_page_end: String, |
| 2493 | __zig_error_names: String, |
| 2494 | __zig_error_name_table: String, |
| 2495 | __zig_errors_len: String, |
| 2496 | _initialize: String, |
| 2497 | _start: String, |
| 2498 | memory: String, |
| 2499 | env: String, |
| 2500 | }; |
| 2501 | |
| 2502 | /// Index into string_bytes |
| 2503 | pub const String = enum(u32) { |
| 2504 | _, |
| 2505 | |
| 2506 | const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage); |
| 2507 | |
| 2508 | const TableContext = struct { |
| 2509 | bytes: []const u8, |
| 2510 | |
| 2511 | pub fn eql(_: @This(), a: String, b: String) bool { |
| 2512 | return a == b; |
| 2513 | } |
| 2514 | |
| 2515 | pub fn hash(ctx: @This(), key: String) u64 { |
| 2516 | return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@backingInt(key)..], 0)); |
| 2517 | } |
| 2518 | }; |
| 2519 | |
| 2520 | const TableIndexAdapter = struct { |
| 2521 | bytes: []const u8, |
| 2522 | |
| 2523 | pub fn eql(ctx: @This(), a: []const u8, b: String) bool { |
| 2524 | return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@backingInt(b)..], 0)); |
| 2525 | } |
| 2526 | |
| 2527 | pub fn hash(_: @This(), adapted_key: []const u8) u64 { |
| 2528 | assert(mem.findScalar(u8, adapted_key, 0) == null); |
| 2529 | return std.hash_map.hashString(adapted_key); |
| 2530 | } |
| 2531 | }; |
| 2532 | |
| 2533 | pub fn slice(index: String, wasm: *const Wasm) [:0]const u8 { |
| 2534 | const start_slice = wasm.string_bytes.items[@backingInt(index)..]; |
| 2535 | return start_slice[0..mem.findScalar(u8, start_slice, 0).? :0]; |
| 2536 | } |
| 2537 | |
| 2538 | pub fn toOptional(i: String) OptionalString { |
| 2539 | const result: OptionalString = @fromBackingInt(@intCast(@backingInt(i))); |
| 2540 | assert(result != .none); |
| 2541 | return result; |
| 2542 | } |
| 2543 | }; |
| 2544 | |
| 2545 | pub const OptionalString = enum(u32) { |
| 2546 | none = std.math.maxInt(u32), |
| 2547 | _, |
| 2548 | |
| 2549 | pub fn unwrap(i: OptionalString) ?String { |
| 2550 | if (i == .none) return null; |
| 2551 | return @fromBackingInt(@intCast(@backingInt(i))); |
| 2552 | } |
| 2553 | |
| 2554 | pub fn slice(index: OptionalString, wasm: *const Wasm) ?[:0]const u8 { |
| 2555 | return (index.unwrap() orelse return null).slice(wasm); |
| 2556 | } |
| 2557 | }; |
| 2558 | |
| 2559 | /// Stored identically to `String`. The bytes are reinterpreted as |
| 2560 | /// `std.wasm.Valtype` elements. |
| 2561 | pub const ValtypeList = enum(u32) { |
| 2562 | _, |
| 2563 | |
| 2564 | pub fn fromString(s: String) ValtypeList { |
| 2565 | return @fromBackingInt(@intCast(@backingInt(s))); |
| 2566 | } |
| 2567 | |
| 2568 | pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype { |
| 2569 | return @ptrCast(String.slice(@fromBackingInt(@intCast(@backingInt(index))), wasm)); |
| 2570 | } |
| 2571 | }; |
| 2572 | |
| 2573 | /// Index into `Wasm.imports`. |
| 2574 | pub const ZcuImportIndex = enum(u32) { |
| 2575 | _, |
| 2576 | |
| 2577 | pub fn ptr(index: ZcuImportIndex, wasm: *const Wasm) *InternPool.Nav.Index { |
| 2578 | return &wasm.imports.keys()[@backingInt(index)]; |
| 2579 | } |
| 2580 | |
| 2581 | pub fn symbolName(index: ZcuImportIndex, wasm: *const Wasm) String { |
| 2582 | return wasm.imports.values()[@backingInt(index)]; |
| 2583 | } |
| 2584 | |
| 2585 | pub fn flags(index: ZcuImportIndex, wasm: *const Wasm) SymbolFlags { |
| 2586 | const zcu = wasm.base.comp.zcu.?; |
| 2587 | const ip = &zcu.intern_pool; |
| 2588 | const nav_index = index.ptr(wasm).*; |
| 2589 | const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern"; |
| 2590 | const name_slice = ext.name.toSlice(ip); |
| 2591 | const name_string = wasm.getExistingString(name_slice).?; |
| 2592 | return .{ |
| 2593 | .binding = switch (ext.linkage) { |
| 2594 | .internal => .local, |
| 2595 | .strong => .strong, |
| 2596 | .weak => .weak, |
| 2597 | .link_once => @panic("TODO: COMDAT"), |
| 2598 | }, |
| 2599 | .visibility_hidden = switch (ext.visibility) { |
| 2600 | .default => false, |
| 2601 | .hidden => true, |
| 2602 | .protected => false, |
| 2603 | }, |
| 2604 | .undefined = true, |
| 2605 | .exported = wasm.missing_exports.contains(name_string), |
| 2606 | .explicit_name = index.symbolName(wasm) != name_string, |
| 2607 | .no_strip = false, |
| 2608 | .tls = ext.is_threadlocal, |
| 2609 | .absolute = false, |
| 2610 | }; |
| 2611 | } |
| 2612 | |
| 2613 | pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String { |
| 2614 | const zcu = wasm.base.comp.zcu.?; |
| 2615 | const ip = &zcu.intern_pool; |
| 2616 | const nav_index = index.ptr(wasm).*; |
| 2617 | const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern"; |
| 2618 | const name_slice = ext.name.toSlice(ip); |
| 2619 | return wasm.getExistingString(name_slice).?; |
| 2620 | } |
| 2621 | |
| 2622 | pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString { |
| 2623 | const zcu = wasm.base.comp.zcu.?; |
| 2624 | const ip = &zcu.intern_pool; |
| 2625 | const nav_index = index.ptr(wasm).*; |
| 2626 | const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern"; |
| 2627 | const lib_name = ext.lib_name.toSlice(ip) orelse return .none; |
| 2628 | return wasm.getExistingString(lib_name).?.toOptional(); |
| 2629 | } |
| 2630 | |
| 2631 | pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index { |
| 2632 | const comp = wasm.base.comp; |
| 2633 | const target = &comp.root_mod.resolved_target.result; |
| 2634 | const zcu = comp.zcu.?; |
| 2635 | const ip = &zcu.intern_pool; |
| 2636 | const nav_index = index.ptr(wasm).*; |
| 2637 | const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern"; |
| 2638 | const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?; |
| 2639 | return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target).?; |
| 2640 | } |
| 2641 | |
| 2642 | pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type { |
| 2643 | _ = index; |
| 2644 | _ = wasm; |
| 2645 | unreachable; // Zig has no way to create Wasm globals yet. |
| 2646 | } |
| 2647 | }; |
| 2648 | |
| 2649 | /// 0. Index into `Wasm.object_function_imports`. |
| 2650 | /// 1. Index into `Wasm.imports`. |
| 2651 | pub const FunctionImportId = enum(u32) { |
| 2652 | _, |
| 2653 | |
| 2654 | pub const Unpacked = union(enum) { |
| 2655 | object_function_import: FunctionImport.Index, |
| 2656 | zcu_import: ZcuImportIndex, |
| 2657 | }; |
| 2658 | |
| 2659 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) FunctionImportId { |
| 2660 | return switch (unpacked) { |
| 2661 | .object_function_import => |i| @fromBackingInt(@intCast(@backingInt(i))), |
| 2662 | .zcu_import => |i| @fromBackingInt(@intCast(@backingInt(i) + wasm.object_function_imports.entries.len)), |
| 2663 | }; |
| 2664 | } |
| 2665 | |
| 2666 | pub fn unpack(id: FunctionImportId, wasm: *const Wasm) Unpacked { |
| 2667 | const i = @backingInt(id); |
| 2668 | if (i < wasm.object_function_imports.entries.len) return .{ .object_function_import = @fromBackingInt(@intCast(i)) }; |
| 2669 | const zcu_import_i = i - wasm.object_function_imports.entries.len; |
| 2670 | return .{ .zcu_import = @fromBackingInt(@intCast(zcu_import_i)) }; |
| 2671 | } |
| 2672 | |
| 2673 | pub fn fromObject(function_import_index: FunctionImport.Index, wasm: *const Wasm) FunctionImportId { |
| 2674 | return pack(.{ .object_function_import = function_import_index }, wasm); |
| 2675 | } |
| 2676 | |
| 2677 | pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId { |
| 2678 | return pack(.{ .zcu_import = zcu_import }, wasm); |
| 2679 | } |
| 2680 | |
| 2681 | /// This function is allowed O(N) lookup because it is only called during |
| 2682 | /// diagnostic generation. |
| 2683 | pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation { |
| 2684 | switch (id.unpack(wasm)) { |
| 2685 | .object_function_import => |obj_func_index| { |
| 2686 | // TODO binary search |
| 2687 | for (wasm.objects.items, 0..) |o, i| { |
| 2688 | if (o.function_imports.off <= @backingInt(obj_func_index) and |
| 2689 | o.function_imports.off + o.function_imports.len > @backingInt(obj_func_index)) |
| 2690 | { |
| 2691 | return .pack(.{ .object_index = @fromBackingInt(@intCast(i)) }, wasm); |
| 2692 | } |
| 2693 | } else unreachable; |
| 2694 | }, |
| 2695 | .zcu_import => return .zig_object_nofile, // TODO give a better source location |
| 2696 | } |
| 2697 | } |
| 2698 | |
| 2699 | pub fn flags(id: FunctionImportId, wasm: *const Wasm) SymbolFlags { |
| 2700 | return switch (id.unpack(wasm)) { |
| 2701 | .object_function_import => |i| i.value(wasm).flags, |
| 2702 | .zcu_import => |i| i.flags(wasm), |
| 2703 | }; |
| 2704 | } |
| 2705 | |
| 2706 | pub fn importName(id: FunctionImportId, wasm: *const Wasm) String { |
| 2707 | return switch (unpack(id, wasm)) { |
| 2708 | inline .object_function_import, .zcu_import => |i| i.importName(wasm), |
| 2709 | }; |
| 2710 | } |
| 2711 | |
| 2712 | pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString { |
| 2713 | return switch (unpack(id, wasm)) { |
| 2714 | inline .object_function_import, .zcu_import => |i| i.moduleName(wasm), |
| 2715 | }; |
| 2716 | } |
| 2717 | |
| 2718 | pub fn functionType(id: FunctionImportId, wasm: *Wasm) FunctionType.Index { |
| 2719 | return switch (unpack(id, wasm)) { |
| 2720 | inline .object_function_import, .zcu_import => |i| i.functionType(wasm), |
| 2721 | }; |
| 2722 | } |
| 2723 | |
| 2724 | /// Asserts not emitting an object, and `Wasm.import_symbols` is false. |
| 2725 | pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool { |
| 2726 | assert(!wasm.import_symbols); |
| 2727 | assert(wasm.base.comp.config.output_mode != .Obj); |
| 2728 | return switch (unpack(id, wasm)) { |
| 2729 | .object_function_import => |i| { |
| 2730 | const import = i.value(wasm); |
| 2731 | return import.flags.binding == .strong and import.module_name != .none; |
| 2732 | }, |
| 2733 | .zcu_import => |i| { |
| 2734 | const zcu = wasm.base.comp.zcu.?; |
| 2735 | const ip = &zcu.intern_pool; |
| 2736 | const ext = ip.indexToKey(ip.getNav(i.ptr(wasm).*).resolved.?.value).@"extern"; |
| 2737 | return ext.linkage != .weak and ext.lib_name != .none; |
| 2738 | }, |
| 2739 | }; |
| 2740 | } |
| 2741 | }; |
| 2742 | |
| 2743 | /// 0. `__stack_pointer`. |
| 2744 | /// 1. Index into `object_global_imports`. |
| 2745 | /// 2. Index into `imports`. |
| 2746 | pub const GlobalImportId = enum(u32) { |
| 2747 | __stack_pointer, |
| 2748 | _, |
| 2749 | |
| 2750 | pub const Unpacked = union(enum) { |
| 2751 | __stack_pointer, |
| 2752 | object_global_import: GlobalImport.Index, |
| 2753 | zcu_import: ZcuImportIndex, |
| 2754 | }; |
| 2755 | |
| 2756 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) GlobalImportId { |
| 2757 | return switch (unpacked) { |
| 2758 | .__stack_pointer => .__stack_pointer, |
| 2759 | .object_global_import => |i| @fromBackingInt(@intCast(@backingInt(i) + 1)), |
| 2760 | .zcu_import => |i| @fromBackingInt(@intCast(@backingInt(i) + wasm.object_global_imports.entries.len + 1)), |
| 2761 | }; |
| 2762 | } |
| 2763 | |
| 2764 | pub fn unpack(id: GlobalImportId, wasm: *const Wasm) Unpacked { |
| 2765 | return switch (id) { |
| 2766 | .__stack_pointer => .__stack_pointer, |
| 2767 | _ => { |
| 2768 | const i = @backingInt(id) - 1; |
| 2769 | if (i < wasm.object_global_imports.entries.len) { |
| 2770 | return .{ .object_global_import = @fromBackingInt(@intCast(i)) }; |
| 2771 | } |
| 2772 | const zcu_import_i = i - wasm.object_global_imports.entries.len; |
| 2773 | return .{ .zcu_import = @fromBackingInt(@intCast(zcu_import_i)) }; |
| 2774 | }, |
| 2775 | }; |
| 2776 | } |
| 2777 | |
| 2778 | pub fn fromObject(object_global_import: GlobalImport.Index, wasm: *const Wasm) GlobalImportId { |
| 2779 | return pack(.{ .object_global_import = object_global_import }, wasm); |
| 2780 | } |
| 2781 | |
| 2782 | pub fn flags(id: GlobalImportId, wasm: *const Wasm) SymbolFlags { |
| 2783 | return switch (id.unpack(wasm)) { |
| 2784 | .__stack_pointer => .{ |
| 2785 | .binding = .strong, |
| 2786 | .undefined = true, |
| 2787 | }, |
| 2788 | .object_global_import => |i| i.value(wasm).flags, |
| 2789 | .zcu_import => |i| i.flags(wasm), |
| 2790 | }; |
| 2791 | } |
| 2792 | |
| 2793 | /// This function is allowed O(N) lookup because it is only called during |
| 2794 | /// diagnostic generation. |
| 2795 | pub fn sourceLocation(id: GlobalImportId, wasm: *const Wasm) SourceLocation { |
| 2796 | switch (id.unpack(wasm)) { |
| 2797 | .__stack_pointer => return .zig_object_nofile, |
| 2798 | .object_global_import => |obj_global_index| { |
| 2799 | // TODO binary search |
| 2800 | for (wasm.objects.items, 0..) |o, i| { |
| 2801 | if (o.global_imports.off <= @backingInt(obj_global_index) and |
| 2802 | o.global_imports.off + o.global_imports.len > @backingInt(obj_global_index)) |
| 2803 | { |
| 2804 | return .pack(.{ .object_index = @fromBackingInt(@intCast(i)) }, wasm); |
| 2805 | } |
| 2806 | } else unreachable; |
| 2807 | }, |
| 2808 | .zcu_import => return .zig_object_nofile, // TODO give a better source location |
| 2809 | } |
| 2810 | } |
| 2811 | |
| 2812 | pub fn importName(id: GlobalImportId, wasm: *const Wasm) String { |
| 2813 | return switch (unpack(id, wasm)) { |
| 2814 | .__stack_pointer => wasm.preloaded_strings.__stack_pointer, |
| 2815 | inline .object_global_import, .zcu_import => |i| i.importName(wasm), |
| 2816 | }; |
| 2817 | } |
| 2818 | |
| 2819 | pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString { |
| 2820 | return switch (unpack(id, wasm)) { |
| 2821 | .__stack_pointer => wasm.preloaded_strings.env.toOptional(), |
| 2822 | inline .object_global_import, .zcu_import => |i| i.moduleName(wasm), |
| 2823 | }; |
| 2824 | } |
| 2825 | |
| 2826 | pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type { |
| 2827 | return switch (unpack(id, wasm)) { |
| 2828 | .__stack_pointer => .{ |
| 2829 | .valtype = switch (wasm.pointerSize()) { |
| 2830 | 4 => .i32, |
| 2831 | 8 => .i64, |
| 2832 | else => unreachable, |
| 2833 | }, |
| 2834 | .mutable = true, |
| 2835 | }, |
| 2836 | inline .object_global_import, .zcu_import => |i| i.globalType(wasm), |
| 2837 | }; |
| 2838 | } |
| 2839 | }; |
| 2840 | |
| 2841 | /// 0. Index into `Wasm.object_data_imports`. |
| 2842 | /// 1. Index into `Wasm.imports`. |
| 2843 | pub const DataImportId = enum(u32) { |
| 2844 | _, |
| 2845 | |
| 2846 | pub const Unpacked = union(enum) { |
| 2847 | object_data_import: ObjectDataImport.Index, |
| 2848 | zcu_import: ZcuImportIndex, |
| 2849 | }; |
| 2850 | |
| 2851 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId { |
| 2852 | return switch (unpacked) { |
| 2853 | .object_data_import => |i| @fromBackingInt(@intCast(@backingInt(i))), |
| 2854 | .zcu_import => |i| @fromBackingInt(@intCast(@backingInt(i) + wasm.object_data_imports.entries.len)), |
| 2855 | }; |
| 2856 | } |
| 2857 | |
| 2858 | pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked { |
| 2859 | const i = @backingInt(id); |
| 2860 | if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @fromBackingInt(@intCast(i)) }; |
| 2861 | const zcu_import_i = i - wasm.object_data_imports.entries.len; |
| 2862 | return .{ .zcu_import = @fromBackingInt(@intCast(zcu_import_i)) }; |
| 2863 | } |
| 2864 | |
| 2865 | pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId { |
| 2866 | return pack(.{ .zcu_import = zcu_import }, wasm); |
| 2867 | } |
| 2868 | |
| 2869 | pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId { |
| 2870 | return pack(.{ .object_data_import = object_data_import }, wasm); |
| 2871 | } |
| 2872 | |
| 2873 | pub fn flags(id: DataImportId, wasm: *const Wasm) SymbolFlags { |
| 2874 | return switch (id.unpack(wasm)) { |
| 2875 | .object_data_import => |i| i.value(wasm).flags, |
| 2876 | .zcu_import => |i| i.flags(wasm), |
| 2877 | }; |
| 2878 | } |
| 2879 | |
| 2880 | pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation { |
| 2881 | switch (id.unpack(wasm)) { |
| 2882 | .object_data_import => |obj_data_index| { |
| 2883 | // TODO binary search |
| 2884 | for (wasm.objects.items, 0..) |o, i| { |
| 2885 | if (o.data_imports.off <= @backingInt(obj_data_index) and |
| 2886 | o.data_imports.off + o.data_imports.len > @backingInt(obj_data_index)) |
| 2887 | { |
| 2888 | return .pack(.{ .object_index = @fromBackingInt(@intCast(i)) }, wasm); |
| 2889 | } |
| 2890 | } else unreachable; |
| 2891 | }, |
| 2892 | .zcu_import => return .zig_object_nofile, // TODO give a better source location |
| 2893 | } |
| 2894 | } |
| 2895 | }; |
| 2896 | |
| 2897 | pub const ZcuRelocation = struct { |
| 2898 | tag: Object.RelocationType, |
| 2899 | offset: u32, |
| 2900 | pointee: Pointee, |
| 2901 | addend: i32, |
| 2902 | |
| 2903 | pub const Pointee = union(enum) { |
| 2904 | function_nav: InternPool.Nav.Index, |
| 2905 | function_name: String, |
| 2906 | tag_function: InternPool.Index, |
| 2907 | data_uav: InternPool.Index, |
| 2908 | data_nav: InternPool.Nav.Index, |
| 2909 | data_resolution: ObjectDataImport.Resolution, |
| 2910 | stack_pointer, |
| 2911 | type_index: FunctionType.Index, |
| 2912 | }; |
| 2913 | |
| 2914 | pub const Slice = extern struct { |
| 2915 | /// Index into `zcu_relocations`. |
| 2916 | off: u32, |
| 2917 | len: u32, |
| 2918 | |
| 2919 | pub fn tags(s: Slice, wasm: *const Wasm) []const Object.RelocationType { |
| 2920 | return wasm.zcu_relocations.items(.tag)[s.off..][0..s.len]; |
| 2921 | } |
| 2922 | |
| 2923 | pub fn offsets(s: Slice, wasm: *const Wasm) []const u32 { |
| 2924 | return wasm.zcu_relocations.items(.offset)[s.off..][0..s.len]; |
| 2925 | } |
| 2926 | |
| 2927 | pub fn pointees(s: Slice, wasm: *const Wasm) []const Pointee { |
| 2928 | return wasm.zcu_relocations.items(.pointee)[s.off..][0..s.len]; |
| 2929 | } |
| 2930 | |
| 2931 | pub fn addends(s: Slice, wasm: *const Wasm) []const i32 { |
| 2932 | return wasm.zcu_relocations.items(.addend)[s.off..][0..s.len]; |
| 2933 | } |
| 2934 | }; |
| 2935 | }; |
| 2936 | |
| 2937 | pub const ObjectRelocation = struct { |
| 2938 | tag: Tag, |
| 2939 | /// Offset of the value to rewrite relative to the relevant section's contents. |
| 2940 | /// When `offset` is zero, its position is immediately after the id and size of the section. |
| 2941 | offset: u32, |
| 2942 | pointee: Pointee, |
| 2943 | /// Populated only for `memory_addr_*`, `function_offset_i32` and `section_offset_i32`. |
| 2944 | addend: i32, |
| 2945 | |
| 2946 | pub const Tag = enum(u8) { |
| 2947 | // These use `Pointee.function`. |
| 2948 | function_index_i32, |
| 2949 | function_index_leb, |
| 2950 | function_offset_i32, |
| 2951 | function_offset_i64, |
| 2952 | table_index_i32, |
| 2953 | table_index_i64, |
| 2954 | table_index_rel_sleb, |
| 2955 | table_index_rel_sleb64, |
| 2956 | table_index_sleb, |
| 2957 | table_index_sleb64, |
| 2958 | // These use `Pointee.symbol_name`. |
| 2959 | function_import_index_i32, |
| 2960 | function_import_index_leb, |
| 2961 | function_import_offset_i32, |
| 2962 | function_import_offset_i64, |
| 2963 | table_import_index_i32, |
| 2964 | table_import_index_i64, |
| 2965 | table_import_index_rel_sleb, |
| 2966 | table_import_index_rel_sleb64, |
| 2967 | table_import_index_sleb, |
| 2968 | table_import_index_sleb64, |
| 2969 | // These use `Pointee.global`. |
| 2970 | global_index_i32, |
| 2971 | global_index_leb, |
| 2972 | // These use `Pointee.symbol_name`. |
| 2973 | global_import_index_i32, |
| 2974 | global_import_index_leb, |
| 2975 | // These use `Pointee.data`. |
| 2976 | memory_addr_i32, |
| 2977 | memory_addr_i64, |
| 2978 | memory_addr_leb, |
| 2979 | memory_addr_leb64, |
| 2980 | memory_addr_locrel_i32, |
| 2981 | memory_addr_rel_sleb, |
| 2982 | memory_addr_rel_sleb64, |
| 2983 | memory_addr_sleb, |
| 2984 | memory_addr_sleb64, |
| 2985 | memory_addr_tls_sleb, |
| 2986 | memory_addr_tls_sleb64, |
| 2987 | // These use `Pointee.symbol_name`. |
| 2988 | memory_addr_import_i32, |
| 2989 | memory_addr_import_i64, |
| 2990 | memory_addr_import_leb, |
| 2991 | memory_addr_import_leb64, |
| 2992 | memory_addr_import_locrel_i32, |
| 2993 | memory_addr_import_rel_sleb, |
| 2994 | memory_addr_import_rel_sleb64, |
| 2995 | memory_addr_import_sleb, |
| 2996 | memory_addr_import_sleb64, |
| 2997 | memory_addr_import_tls_sleb, |
| 2998 | memory_addr_import_tls_sleb64, |
| 2999 | /// Uses `Pointee.section`. |
| 3000 | section_offset_i32, |
| 3001 | /// Uses `Pointee.table`. |
| 3002 | table_number_leb, |
| 3003 | /// Uses `Pointee.symbol_name`. |
| 3004 | table_import_number_leb, |
| 3005 | /// Uses `Pointee.type_index`. |
| 3006 | type_index_leb, |
| 3007 | |
| 3008 | pub fn fromType(t: Object.RelocationType) Tag { |
| 3009 | return switch (t) { |
| 3010 | .event_index_leb => unreachable, |
| 3011 | .function_index_i32 => .function_index_i32, |
| 3012 | .function_index_leb => .function_index_leb, |
| 3013 | .function_offset_i32 => .function_offset_i32, |
| 3014 | .function_offset_i64 => .function_offset_i64, |
| 3015 | .global_index_i32 => .global_index_i32, |
| 3016 | .global_index_leb => .global_index_leb, |
| 3017 | .memory_addr_i32 => .memory_addr_i32, |
| 3018 | .memory_addr_i64 => .memory_addr_i64, |
| 3019 | .memory_addr_leb => .memory_addr_leb, |
| 3020 | .memory_addr_leb64 => .memory_addr_leb64, |
| 3021 | .memory_addr_locrel_i32 => .memory_addr_locrel_i32, |
| 3022 | .memory_addr_rel_sleb => .memory_addr_rel_sleb, |
| 3023 | .memory_addr_rel_sleb64 => .memory_addr_rel_sleb64, |
| 3024 | .memory_addr_sleb => .memory_addr_sleb, |
| 3025 | .memory_addr_sleb64 => .memory_addr_sleb64, |
| 3026 | .memory_addr_tls_sleb => .memory_addr_tls_sleb, |
| 3027 | .memory_addr_tls_sleb64 => .memory_addr_tls_sleb64, |
| 3028 | .section_offset_i32 => .section_offset_i32, |
| 3029 | .table_index_i32 => .table_index_i32, |
| 3030 | .table_index_i64 => .table_index_i64, |
| 3031 | .table_index_rel_sleb => .table_index_rel_sleb, |
| 3032 | .table_index_rel_sleb64 => .table_index_rel_sleb64, |
| 3033 | .table_index_sleb => .table_index_sleb, |
| 3034 | .table_index_sleb64 => .table_index_sleb64, |
| 3035 | .table_number_leb => .table_number_leb, |
| 3036 | .type_index_leb => .type_index_leb, |
| 3037 | }; |
| 3038 | } |
| 3039 | |
| 3040 | pub fn fromTypeImport(t: Object.RelocationType) Tag { |
| 3041 | return switch (t) { |
| 3042 | .event_index_leb => unreachable, |
| 3043 | .function_index_i32 => .function_import_index_i32, |
| 3044 | .function_index_leb => .function_import_index_leb, |
| 3045 | .function_offset_i32 => .function_import_offset_i32, |
| 3046 | .function_offset_i64 => .function_import_offset_i64, |
| 3047 | .global_index_i32 => .global_import_index_i32, |
| 3048 | .global_index_leb => .global_import_index_leb, |
| 3049 | .memory_addr_i32 => .memory_addr_import_i32, |
| 3050 | .memory_addr_i64 => .memory_addr_import_i64, |
| 3051 | .memory_addr_leb => .memory_addr_import_leb, |
| 3052 | .memory_addr_leb64 => .memory_addr_import_leb64, |
| 3053 | .memory_addr_locrel_i32 => .memory_addr_import_locrel_i32, |
| 3054 | .memory_addr_rel_sleb => .memory_addr_import_rel_sleb, |
| 3055 | .memory_addr_rel_sleb64 => .memory_addr_import_rel_sleb64, |
| 3056 | .memory_addr_sleb => .memory_addr_import_sleb, |
| 3057 | .memory_addr_sleb64 => .memory_addr_import_sleb64, |
| 3058 | .memory_addr_tls_sleb => .memory_addr_import_tls_sleb, |
| 3059 | .memory_addr_tls_sleb64 => .memory_addr_import_tls_sleb64, |
| 3060 | .section_offset_i32 => unreachable, |
| 3061 | .table_index_i32 => .table_import_index_i32, |
| 3062 | .table_index_i64 => .table_import_index_i64, |
| 3063 | .table_index_rel_sleb => .table_import_index_rel_sleb, |
| 3064 | .table_index_rel_sleb64 => .table_import_index_rel_sleb64, |
| 3065 | .table_index_sleb => .table_import_index_sleb, |
| 3066 | .table_index_sleb64 => .table_import_index_sleb64, |
| 3067 | .table_number_leb => .table_import_number_leb, |
| 3068 | .type_index_leb => unreachable, |
| 3069 | }; |
| 3070 | } |
| 3071 | }; |
| 3072 | |
| 3073 | pub const Pointee = union { |
| 3074 | symbol_name: String, |
| 3075 | data: ObjectData.Index, |
| 3076 | type_index: FunctionType.Index, |
| 3077 | section: ObjectSectionIndex, |
| 3078 | function: ObjectFunctionIndex, |
| 3079 | global: ObjectGlobalIndex, |
| 3080 | table: ObjectTableIndex, |
| 3081 | }; |
| 3082 | |
| 3083 | pub const Slice = extern struct { |
| 3084 | /// Index into `relocations`. |
| 3085 | off: u32, |
| 3086 | len: u32, |
| 3087 | |
| 3088 | const empty: Slice = .{ .off = 0, .len = 0 }; |
| 3089 | |
| 3090 | pub fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag { |
| 3091 | return wasm.object_relocations.items(.tag)[s.off..][0..s.len]; |
| 3092 | } |
| 3093 | |
| 3094 | pub fn offsets(s: Slice, wasm: *const Wasm) []const u32 { |
| 3095 | return wasm.object_relocations.items(.offset)[s.off..][0..s.len]; |
| 3096 | } |
| 3097 | |
| 3098 | pub fn pointees(s: Slice, wasm: *const Wasm) []const Pointee { |
| 3099 | return wasm.object_relocations.items(.pointee)[s.off..][0..s.len]; |
| 3100 | } |
| 3101 | |
| 3102 | pub fn addends(s: Slice, wasm: *const Wasm) []const i32 { |
| 3103 | return wasm.object_relocations.items(.addend)[s.off..][0..s.len]; |
| 3104 | } |
| 3105 | }; |
| 3106 | |
| 3107 | pub const IterableSlice = struct { |
| 3108 | slice: Slice, |
| 3109 | /// Offset at which point to stop iterating. |
| 3110 | end: u32, |
| 3111 | |
| 3112 | const empty: IterableSlice = .{ .slice = .empty, .end = 0 }; |
| 3113 | |
| 3114 | fn init(relocs: Slice, offset: u32, size: u32, wasm: *const Wasm) IterableSlice { |
| 3115 | const offsets = relocs.offsets(wasm); |
| 3116 | const start = std.sort.lowerBound(u32, offsets, offset, order); |
| 3117 | return .{ |
| 3118 | .slice = .{ |
| 3119 | .off = @intCast(relocs.off + start), |
| 3120 | .len = @intCast(relocs.len - start), |
| 3121 | }, |
| 3122 | .end = offset + size, |
| 3123 | }; |
| 3124 | } |
| 3125 | |
| 3126 | fn order(lhs: u32, rhs: u32) std.math.Order { |
| 3127 | return std.math.order(lhs, rhs); |
| 3128 | } |
| 3129 | }; |
| 3130 | }; |
| 3131 | |
| 3132 | pub const MemoryImport = extern struct { |
| 3133 | module_name: String, |
| 3134 | limits_min: u32, |
| 3135 | limits_max: u32, |
| 3136 | source_location: SourceLocation, |
| 3137 | limits_has_max: bool, |
| 3138 | limits_is_shared: bool, |
| 3139 | padding: [2]u8 = .{ 0, 0 }, |
| 3140 | |
| 3141 | pub fn limits(mi: *const MemoryImport) std.wasm.Limits { |
| 3142 | return .{ |
| 3143 | .flags = .{ |
| 3144 | .has_max = mi.limits_has_max, |
| 3145 | .is_shared = mi.limits_is_shared, |
| 3146 | }, |
| 3147 | .min = mi.limits_min, |
| 3148 | .max = mi.limits_max, |
| 3149 | }; |
| 3150 | } |
| 3151 | }; |
| 3152 | |
| 3153 | pub const Alignment = InternPool.Alignment; |
| 3154 | |
| 3155 | pub const InitFunc = extern struct { |
| 3156 | priority: u32, |
| 3157 | function_index: ObjectFunctionIndex, |
| 3158 | |
| 3159 | pub fn lessThan(ctx: void, lhs: InitFunc, rhs: InitFunc) bool { |
| 3160 | _ = ctx; |
| 3161 | if (lhs.priority == rhs.priority) { |
| 3162 | return @backingInt(lhs.function_index) < @backingInt(rhs.function_index); |
| 3163 | } else { |
| 3164 | return lhs.priority < rhs.priority; |
| 3165 | } |
| 3166 | } |
| 3167 | }; |
| 3168 | |
| 3169 | pub const Comdat = struct { |
| 3170 | name: String, |
| 3171 | /// Must be zero, no flags are currently defined by the tool-convention. |
| 3172 | flags: u32, |
| 3173 | symbols: Comdat.Symbol.Slice, |
| 3174 | |
| 3175 | pub const Symbol = struct { |
| 3176 | kind: Comdat.Symbol.Type, |
| 3177 | /// Index of the data segment/function/global/event/table within a WASM module. |
| 3178 | /// The object must not be an import. |
| 3179 | index: u32, |
| 3180 | |
| 3181 | pub const Slice = struct { |
| 3182 | /// Index into Wasm object_comdat_symbols |
| 3183 | off: u32, |
| 3184 | len: u32, |
| 3185 | }; |
| 3186 | |
| 3187 | pub const Type = enum(u8) { |
| 3188 | data = 0, |
| 3189 | function = 1, |
| 3190 | global = 2, |
| 3191 | event = 3, |
| 3192 | table = 4, |
| 3193 | section = 5, |
| 3194 | }; |
| 3195 | }; |
| 3196 | }; |
| 3197 | |
| 3198 | /// Stored as a u8 so it can reuse the string table mechanism. |
| 3199 | pub const Feature = packed struct(u8) { |
| 3200 | prefix: Prefix, |
| 3201 | /// Type of the feature, must be unique in the sequence of features. |
| 3202 | tag: Tag, |
| 3203 | |
| 3204 | pub const sentinel: Feature = @bitCast(@as(u8, 0)); |
| 3205 | |
| 3206 | /// Stored identically to `String`. The bytes are reinterpreted as `Feature` |
| 3207 | /// elements. Elements must be sorted before string-interning. |
| 3208 | pub const Set = enum(u32) { |
| 3209 | _, |
| 3210 | |
| 3211 | pub fn fromString(s: String) Set { |
| 3212 | return @fromBackingInt(@intCast(@backingInt(s))); |
| 3213 | } |
| 3214 | |
| 3215 | pub fn string(s: Set) String { |
| 3216 | return @fromBackingInt(@intCast(@backingInt(s))); |
| 3217 | } |
| 3218 | |
| 3219 | pub fn slice(s: Set, wasm: *const Wasm) [:sentinel]const Feature { |
| 3220 | return @ptrCast(string(s).slice(wasm)); |
| 3221 | } |
| 3222 | }; |
| 3223 | |
| 3224 | /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem. |
| 3225 | /// Additionally the name uses convention matching the wasm binary format. |
| 3226 | pub const Tag = enum(u6) { |
| 3227 | atomics, |
| 3228 | @"bulk-memory", |
| 3229 | @"bulk-memory-opt", |
| 3230 | @"call-indirect-overlong", |
| 3231 | @"exception-handling", |
| 3232 | @"extended-const", |
| 3233 | fp16, |
| 3234 | gc, |
| 3235 | memory64, |
| 3236 | multimemory, |
| 3237 | multivalue, |
| 3238 | @"mutable-globals", |
| 3239 | @"nontrapping-bulk-memory-len0", |
| 3240 | @"nontrapping-fptoint", |
| 3241 | @"reference-types", |
| 3242 | @"relaxed-simd", |
| 3243 | @"sign-ext", |
| 3244 | simd128, |
| 3245 | @"tail-call", |
| 3246 | @"shared-mem", |
| 3247 | @"wide-arithmetic", |
| 3248 | |
| 3249 | pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag { |
| 3250 | return switch (feature) { |
| 3251 | .atomics => .atomics, |
| 3252 | .bulk_memory => .@"bulk-memory", |
| 3253 | .bulk_memory_opt => .@"bulk-memory-opt", |
| 3254 | .call_indirect_overlong => .@"call-indirect-overlong", |
| 3255 | .exception_handling => .@"exception-handling", |
| 3256 | .extended_const => .@"extended-const", |
| 3257 | .fp16 => .fp16, |
| 3258 | .gc => .gc, |
| 3259 | .multimemory => .multimemory, |
| 3260 | .multivalue => .multivalue, |
| 3261 | .mutable_globals => .@"mutable-globals", |
| 3262 | .nontrapping_bulk_memory_len0 => .@"nontrapping-bulk-memory-len0", // Zig extension. |
| 3263 | .nontrapping_fptoint => .@"nontrapping-fptoint", |
| 3264 | .reference_types => .@"reference-types", |
| 3265 | .relaxed_simd => .@"relaxed-simd", |
| 3266 | .sign_ext => .@"sign-ext", |
| 3267 | .simd128 => .simd128, |
| 3268 | .tail_call => .@"tail-call", |
| 3269 | .wide_arithmetic => .@"wide-arithmetic", |
| 3270 | }; |
| 3271 | } |
| 3272 | |
| 3273 | pub fn toCpuFeature(tag: Tag) ?std.Target.wasm.Feature { |
| 3274 | return switch (tag) { |
| 3275 | .atomics => .atomics, |
| 3276 | .@"bulk-memory" => .bulk_memory, |
| 3277 | .@"bulk-memory-opt" => .bulk_memory_opt, |
| 3278 | .@"call-indirect-overlong" => .call_indirect_overlong, |
| 3279 | .@"exception-handling" => .exception_handling, |
| 3280 | .@"extended-const" => .extended_const, |
| 3281 | .fp16 => .fp16, |
| 3282 | .gc => .gc, |
| 3283 | .memory64 => null, // Linker-only feature. |
| 3284 | .multimemory => .multimemory, |
| 3285 | .multivalue => .multivalue, |
| 3286 | .@"mutable-globals" => .mutable_globals, |
| 3287 | .@"nontrapping-bulk-memory-len0" => .nontrapping_bulk_memory_len0, // Zig extension. |
| 3288 | .@"nontrapping-fptoint" => .nontrapping_fptoint, |
| 3289 | .@"reference-types" => .reference_types, |
| 3290 | .@"relaxed-simd" => .relaxed_simd, |
| 3291 | .@"sign-ext" => .sign_ext, |
| 3292 | .simd128 => .simd128, |
| 3293 | .@"tail-call" => .tail_call, |
| 3294 | .@"shared-mem" => null, // Linker-only feature. |
| 3295 | .@"wide-arithmetic" => .wide_arithmetic, |
| 3296 | }; |
| 3297 | } |
| 3298 | |
| 3299 | pub const format = @compileError("use @tagName instead"); |
| 3300 | }; |
| 3301 | |
| 3302 | /// Provides information about the usage of the feature. |
| 3303 | pub const Prefix = enum(u2) { |
| 3304 | /// Reserved so that a 0-byte Feature is invalid and therefore can be a sentinel. |
| 3305 | invalid, |
| 3306 | /// Object uses this feature, and the link fails if feature is not in |
| 3307 | /// the allowed set. |
| 3308 | @"+", |
| 3309 | /// Object does not use this feature, and the link fails if this |
| 3310 | /// feature is in the allowed set. |
| 3311 | @"-", |
| 3312 | /// Object uses this feature, and the link fails if this feature is not |
| 3313 | /// in the allowed set, or if any object does not use this feature. |
| 3314 | @"=", |
| 3315 | }; |
| 3316 | |
| 3317 | pub fn format(feature: Feature, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 3318 | try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) }); |
| 3319 | } |
| 3320 | |
| 3321 | pub fn lessThan(_: void, a: Feature, b: Feature) bool { |
| 3322 | assert(a != b); |
| 3323 | const a_int: u8 = @bitCast(a); |
| 3324 | const b_int: u8 = @bitCast(b); |
| 3325 | return a_int < b_int; |
| 3326 | } |
| 3327 | }; |
| 3328 | |
| 3329 | pub fn open( |
| 3330 | arena: Allocator, |
| 3331 | comp: *Compilation, |
| 3332 | emit: Path, |
| 3333 | options: link.File.OpenOptions, |
| 3334 | ) !*Wasm { |
| 3335 | // TODO: restore saved linker state, don't truncate the file, and |
| 3336 | // participate in incremental compilation. |
| 3337 | return createEmpty(arena, comp, emit, options); |
| 3338 | } |
| 3339 | |
| 3340 | pub fn createEmpty( |
| 3341 | arena: Allocator, |
| 3342 | comp: *Compilation, |
| 3343 | emit: Path, |
| 3344 | options: link.File.OpenOptions, |
| 3345 | ) !*Wasm { |
| 3346 | const target = &comp.root_mod.resolved_target.result; |
| 3347 | assert(target.ofmt == .wasm); |
| 3348 | |
| 3349 | const output_mode = comp.config.output_mode; |
| 3350 | const wasi_exec_model = comp.config.wasi_exec_model; |
| 3351 | |
| 3352 | const wasm = try arena.create(Wasm); |
| 3353 | wasm.* = .{ |
| 3354 | .base = .{ |
| 3355 | .tag = .wasm, |
| 3356 | .comp = comp, |
| 3357 | .emit = emit, |
| 3358 | // Garbage collection is so crucial to WebAssembly that we design |
| 3359 | // the linker around the assumption that it will be on in the vast |
| 3360 | // majority of cases, and therefore express "no garbage collection" |
| 3361 | // in terms of setting the no_strip and must_link flags on all |
| 3362 | // symbols. |
| 3363 | .gc_sections = options.gc_sections orelse (output_mode != .Obj), |
| 3364 | .print_gc_sections = options.print_gc_sections, |
| 3365 | .stack_size = options.stack_size orelse switch (target.os.tag) { |
| 3366 | .freestanding => 1 * 1024 * 1024, // 1 MiB |
| 3367 | else => 16 * 1024 * 1024, // 16 MiB |
| 3368 | }, |
| 3369 | .allow_shlib_undefined = options.allow_shlib_undefined orelse false, |
| 3370 | .file = null, |
| 3371 | .build_id = options.build_id, |
| 3372 | }, |
| 3373 | .name = undefined, |
| 3374 | .string_table = .empty, |
| 3375 | .string_bytes = .empty, |
| 3376 | .export_table = options.export_table, |
| 3377 | .growable_table = options.growable_table, |
| 3378 | .import_symbols = options.import_symbols, |
| 3379 | .export_symbol_names = options.export_symbol_names, |
| 3380 | .global_base = options.global_base, |
| 3381 | .initial_memory = options.initial_memory, |
| 3382 | .max_memory = options.max_memory, |
| 3383 | |
| 3384 | .entry_name = undefined, |
| 3385 | .dump_argv_list = .empty, |
| 3386 | .object_host_name = .none, |
| 3387 | .preloaded_strings = undefined, |
| 3388 | }; |
| 3389 | errdefer wasm.base.destroy(); |
| 3390 | |
| 3391 | if (options.object_host_name) |name| wasm.object_host_name = (try wasm.internString(name)).toOptional(); |
| 3392 | |
| 3393 | inline for (@typeInfo(PreloadedStrings).@"struct".field_names) |field_name| { |
| 3394 | @field(wasm.preloaded_strings, field_name) = try wasm.internString(field_name); |
| 3395 | } |
| 3396 | |
| 3397 | wasm.entry_name = switch (options.entry) { |
| 3398 | .disabled => .none, |
| 3399 | .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), |
| 3400 | .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), |
| 3401 | .named => |name| (try wasm.internString(name)).toOptional(), |
| 3402 | }; |
| 3403 | |
| 3404 | const io = comp.io; |
| 3405 | |
| 3406 | wasm.base.file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{ |
| 3407 | .truncate = true, |
| 3408 | .read = true, |
| 3409 | .permissions = if (Io.File.Permissions.has_executable_bit) |
| 3410 | if (target.os.tag == .wasi and output_mode == .Exe) |
| 3411 | .executable_file |
| 3412 | else |
| 3413 | .default_file |
| 3414 | else |
| 3415 | .default_file, |
| 3416 | }); |
| 3417 | wasm.name = emit.sub_path; |
| 3418 | |
| 3419 | return wasm; |
| 3420 | } |
| 3421 | |
| 3422 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { |
| 3423 | log.debug("parseObject {f}", .{obj.path}); |
| 3424 | const gpa = wasm.base.comp.gpa; |
| 3425 | const io = wasm.base.comp.io; |
| 3426 | const gc_sections = wasm.base.gc_sections; |
| 3427 | |
| 3428 | defer obj.file.close(io); |
| 3429 | |
| 3430 | var file_reader = obj.file.reader(io, &.{}); |
| 3431 | |
| 3432 | try wasm.objects.ensureUnusedCapacity(gpa, 1); |
| 3433 | const size = std.math.cast(usize, try file_reader.getSize()) orelse return error.FileTooBig; |
| 3434 | |
| 3435 | const file_contents = try gpa.alloc(u8, size); |
| 3436 | defer gpa.free(file_contents); |
| 3437 | |
| 3438 | const n = file_reader.interface.readSliceShort(file_contents) catch |err| switch (err) { |
| 3439 | error.ReadFailed => return file_reader.err.?, |
| 3440 | }; |
| 3441 | if (n != file_contents.len) return error.UnexpectedEndOfFile; |
| 3442 | |
| 3443 | var ss: Object.ScratchSpace = .{}; |
| 3444 | defer ss.deinit(gpa); |
| 3445 | |
| 3446 | const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections); |
| 3447 | wasm.objects.appendAssumeCapacity(object); |
| 3448 | } |
| 3449 | |
| 3450 | fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void { |
| 3451 | log.debug("parseArchive {f}", .{obj.path}); |
| 3452 | const gpa = wasm.base.comp.gpa; |
| 3453 | const io = wasm.base.comp.io; |
| 3454 | const gc_sections = wasm.base.gc_sections; |
| 3455 | |
| 3456 | defer obj.file.close(io); |
| 3457 | |
| 3458 | var file_reader = obj.file.reader(io, &.{}); |
| 3459 | |
| 3460 | const size = std.math.cast(usize, try file_reader.getSize()) orelse return error.FileTooBig; |
| 3461 | |
| 3462 | const file_contents = try gpa.alloc(u8, size); |
| 3463 | defer gpa.free(file_contents); |
| 3464 | |
| 3465 | const n = file_reader.interface.readSliceShort(file_contents) catch |err| switch (err) { |
| 3466 | error.ReadFailed => return file_reader.err.?, |
| 3467 | }; |
| 3468 | if (n != file_contents.len) return error.UnexpectedEndOfFile; |
| 3469 | |
| 3470 | var archive = try Archive.parse(gpa, file_contents); |
| 3471 | defer archive.deinit(gpa); |
| 3472 | |
| 3473 | // In this case we must force link all embedded object files within the archive |
| 3474 | // We loop over all symbols, and then group them by offset as the offset |
| 3475 | // notates where the object file starts. |
| 3476 | var offsets: std.array_hash_map.Auto(u32, void) = .empty; |
| 3477 | defer offsets.deinit(gpa); |
| 3478 | for (archive.toc.values()) |symbol_offsets| { |
| 3479 | for (symbol_offsets.items) |sym_offset| { |
| 3480 | try offsets.put(gpa, sym_offset, {}); |
| 3481 | } |
| 3482 | } |
| 3483 | |
| 3484 | var ss: Object.ScratchSpace = .{}; |
| 3485 | defer ss.deinit(gpa); |
| 3486 | |
| 3487 | try wasm.objects.ensureUnusedCapacity(gpa, offsets.count()); |
| 3488 | for (offsets.keys()) |file_offset| { |
| 3489 | const object = try archive.parseObject(wasm, file_contents, file_offset, obj.path, wasm.object_host_name, &ss, obj.must_link, gc_sections); |
| 3490 | wasm.objects.appendAssumeCapacity(object); |
| 3491 | } |
| 3492 | } |
| 3493 | |
| 3494 | pub fn deinit(wasm: *Wasm) void { |
| 3495 | const gpa = wasm.base.comp.gpa; |
| 3496 | |
| 3497 | wasm.navs_exe.deinit(gpa); |
| 3498 | wasm.navs_obj.deinit(gpa); |
| 3499 | wasm.uavs_exe.deinit(gpa); |
| 3500 | wasm.uavs_obj.deinit(gpa); |
| 3501 | wasm.overaligned_uavs.deinit(gpa); |
| 3502 | wasm.zcu_funcs.deinit(gpa); |
| 3503 | wasm.nav_exports.deinit(gpa); |
| 3504 | wasm.uav_exports.deinit(gpa); |
| 3505 | wasm.imports.deinit(gpa); |
| 3506 | |
| 3507 | wasm.flush_buffer.deinit(gpa); |
| 3508 | |
| 3509 | wasm.mir_instructions.deinit(gpa); |
| 3510 | wasm.mir_extra.deinit(gpa); |
| 3511 | wasm.mir_locals.deinit(gpa); |
| 3512 | |
| 3513 | if (wasm.dwarf) |*dwarf| dwarf.deinit(); |
| 3514 | |
| 3515 | wasm.object_function_imports.deinit(gpa); |
| 3516 | wasm.object_functions.deinit(gpa); |
| 3517 | wasm.object_global_imports.deinit(gpa); |
| 3518 | wasm.object_globals.deinit(gpa); |
| 3519 | wasm.object_table_imports.deinit(gpa); |
| 3520 | wasm.object_tables.deinit(gpa); |
| 3521 | wasm.object_memory_imports.deinit(gpa); |
| 3522 | wasm.object_memories.deinit(gpa); |
| 3523 | wasm.object_relocations.deinit(gpa); |
| 3524 | wasm.object_data_imports.deinit(gpa); |
| 3525 | wasm.object_data_segments.deinit(gpa); |
| 3526 | wasm.object_datas.deinit(gpa); |
| 3527 | wasm.object_custom_segments.deinit(gpa); |
| 3528 | wasm.object_init_funcs.deinit(gpa); |
| 3529 | wasm.object_comdats.deinit(gpa); |
| 3530 | wasm.object_relocations_table.deinit(gpa); |
| 3531 | wasm.object_comdat_symbols.deinit(gpa); |
| 3532 | wasm.objects.deinit(gpa); |
| 3533 | |
| 3534 | wasm.func_types.deinit(gpa); |
| 3535 | wasm.function_exports.deinit(gpa); |
| 3536 | wasm.hidden_function_exports.deinit(gpa); |
| 3537 | wasm.function_imports.deinit(gpa); |
| 3538 | wasm.functions.deinit(gpa); |
| 3539 | wasm.globals.deinit(gpa); |
| 3540 | wasm.global_exports.deinit(gpa); |
| 3541 | wasm.global_imports.deinit(gpa); |
| 3542 | wasm.table_imports.deinit(gpa); |
| 3543 | wasm.tables.deinit(gpa); |
| 3544 | wasm.data_imports.deinit(gpa); |
| 3545 | wasm.datas.deinit(gpa); |
| 3546 | wasm.data_segments.deinit(gpa); |
| 3547 | wasm.zcu_relocations.deinit(gpa); |
| 3548 | wasm.uav_fixups.deinit(gpa); |
| 3549 | wasm.nav_fixups.deinit(gpa); |
| 3550 | wasm.func_table_fixups.deinit(gpa); |
| 3551 | |
| 3552 | wasm.zcu_indirect_function_set.deinit(gpa); |
| 3553 | wasm.object_indirect_function_import_set.deinit(gpa); |
| 3554 | wasm.object_indirect_function_set.deinit(gpa); |
| 3555 | |
| 3556 | wasm.string_bytes.deinit(gpa); |
| 3557 | wasm.string_table.deinit(gpa); |
| 3558 | wasm.dump_argv_list.deinit(gpa); |
| 3559 | |
| 3560 | wasm.params_scratch.deinit(gpa); |
| 3561 | wasm.returns_scratch.deinit(gpa); |
| 3562 | |
| 3563 | wasm.error_name_bytes.deinit(gpa); |
| 3564 | wasm.error_name_offs.deinit(gpa); |
| 3565 | wasm.tag_name_bytes.deinit(gpa); |
| 3566 | wasm.tag_name_offs.deinit(gpa); |
| 3567 | |
| 3568 | wasm.missing_exports.deinit(gpa); |
| 3569 | } |
| 3570 | |
| 3571 | pub fn updateFunc( |
| 3572 | wasm: *Wasm, |
| 3573 | pt: Zcu.PerThread, |
| 3574 | func_index: InternPool.Index, |
| 3575 | any_mir: *const codegen.AnyMir, |
| 3576 | ) !void { |
| 3577 | // This linker implementation only works with codegen backend `.stage2_wasm`. |
| 3578 | const mir = &any_mir.wasm; |
| 3579 | const zcu = pt.zcu; |
| 3580 | const gpa = zcu.gpa; |
| 3581 | const ip = &zcu.intern_pool; |
| 3582 | const is_obj = zcu.comp.config.output_mode == .Obj; |
| 3583 | const target = &zcu.comp.root_mod.resolved_target.result; |
| 3584 | const owner_nav = zcu.funcInfo(func_index).owner_nav; |
| 3585 | log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); |
| 3586 | |
| 3587 | // For Wasm, we do not lower the MIR to code just yet. That lowering happens during `flush`, |
| 3588 | // after garbage collection, which can affect function and global indexes, which affects the |
| 3589 | // LEB integer encoding, which affects the output binary size. |
| 3590 | |
| 3591 | // However, we do move the MIR into a more efficient in-memory representation, where the arrays |
| 3592 | // for all functions are packed together rather than keeping them each in their own `Mir`. |
| 3593 | const mir_instructions_off: u32 = @intCast(wasm.mir_instructions.len); |
| 3594 | const mir_extra_off: u32 = @intCast(wasm.mir_extra.items.len); |
| 3595 | const mir_locals_off: u32 = @intCast(wasm.mir_locals.items.len); |
| 3596 | { |
| 3597 | // Copying MultiArrayList data is a little non-trivial. Resize, then memcpy both slices. |
| 3598 | const old_len = wasm.mir_instructions.len; |
| 3599 | try wasm.mir_instructions.resize(gpa, old_len + mir.instructions.len); |
| 3600 | const dest_slice = wasm.mir_instructions.slice().subslice(old_len, mir.instructions.len); |
| 3601 | const src_slice = mir.instructions; |
| 3602 | @memcpy(dest_slice.items(.tag), src_slice.items(.tag)); |
| 3603 | @memcpy(dest_slice.items(.data), src_slice.items(.data)); |
| 3604 | } |
| 3605 | try wasm.mir_extra.appendSlice(gpa, mir.extra); |
| 3606 | try wasm.mir_locals.appendSlice(gpa, mir.locals); |
| 3607 | |
| 3608 | // We also need to populate some global state from `mir`. |
| 3609 | try wasm.zcu_indirect_function_set.ensureUnusedCapacity(gpa, mir.indirect_function_set.count()); |
| 3610 | for (mir.indirect_function_set.keys()) |nav| wasm.zcu_indirect_function_set.putAssumeCapacity(nav, {}); |
| 3611 | for (mir.func_tys.keys()) |func_ty| { |
| 3612 | const fn_info = zcu.typeToFunc(.fromInterned(func_ty)).?; |
| 3613 | _ = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target); |
| 3614 | } |
| 3615 | wasm.error_name_table_ref_count += mir.error_name_table_ref_count; |
| 3616 | // We need to populate UAV data. In theory, we can lower the UAV values while we fill `mir.uavs`. |
| 3617 | // However, lowering the data might cause *more* UAVs to be created, and mixing them up would be |
| 3618 | // a headache. So instead, just write `undefined` placeholder code and use the `ZcuDataStarts`. |
| 3619 | const zds: ZcuDataStarts = .init(wasm); |
| 3620 | for (mir.uavs.keys(), mir.uavs.values()) |uav_val, uav_align| { |
| 3621 | if (uav_align != .none) { |
| 3622 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); |
| 3623 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(uav_align) else uav_align; |
| 3624 | } |
| 3625 | if (is_obj) { |
| 3626 | const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); |
| 3627 | if (!gop.found_existing) gop.value_ptr.* = undefined; // `zds` handles lowering |
| 3628 | } else { |
| 3629 | const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); |
| 3630 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 3631 | .code = undefined, // `zds` handles lowering |
| 3632 | .count = 0, |
| 3633 | }; |
| 3634 | gop.value_ptr.count += 1; |
| 3635 | } |
| 3636 | } |
| 3637 | try zds.finish(wasm, pt); // actually generates the UAVs |
| 3638 | |
| 3639 | try wasm.functions.ensureUnusedCapacity(gpa, 1); |
| 3640 | try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); |
| 3641 | |
| 3642 | // This converts AIR to MIR but does not yet lower to wasm code. |
| 3643 | wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{ |
| 3644 | .instructions_off = mir_instructions_off, |
| 3645 | .instructions_len = @intCast(mir.instructions.len), |
| 3646 | .extra_off = mir_extra_off, |
| 3647 | .extra_len = @intCast(mir.extra.len), |
| 3648 | .locals_off = mir_locals_off, |
| 3649 | .locals_len = @intCast(mir.locals.len), |
| 3650 | .prologue = mir.prologue, |
| 3651 | } }); |
| 3652 | wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @fromBackingInt(@intCast(wasm.zcu_funcs.entries.len - 1)) }), {}); |
| 3653 | } |
| 3654 | |
| 3655 | // Generate code for the "Nav", storing it in memory to be later written to |
| 3656 | // the file on flush(). |
| 3657 | pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| 3658 | const zcu = pt.zcu; |
| 3659 | const ip = &zcu.intern_pool; |
| 3660 | const nav = ip.getNav(nav_index); |
| 3661 | const comp = wasm.base.comp; |
| 3662 | const gpa = comp.gpa; |
| 3663 | const is_obj = comp.config.output_mode == .Obj; |
| 3664 | const target = &comp.root_mod.resolved_target.result; |
| 3665 | |
| 3666 | switch (ip.indexToKey(nav.resolved.?.value)) { |
| 3667 | else => {}, |
| 3668 | .func => return, // global const which is a function alias |
| 3669 | .@"extern" => |ext| { |
| 3670 | if (is_obj) { |
| 3671 | assert(!wasm.navs_obj.contains(ext.owner_nav)); |
| 3672 | } else { |
| 3673 | assert(!wasm.navs_exe.contains(ext.owner_nav)); |
| 3674 | } |
| 3675 | const name_slice = ext.name.toSlice(ip); |
| 3676 | const name = try wasm.internString(name_slice); |
| 3677 | const symbol_name = if (ip.isFunctionType(nav.resolved.?.type)) symbol_name: { |
| 3678 | const lib_name = ext.lib_name.toSlice(ip) orelse break :symbol_name name; |
| 3679 | _ = try wasm.internString(lib_name); |
| 3680 | // match llvm backend behavior |
| 3681 | if (mem.eql(u8, lib_name, "c")) break :symbol_name name; |
| 3682 | const qualified_name = try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name_slice, lib_name }); |
| 3683 | defer gpa.free(qualified_name); |
| 3684 | break :symbol_name try wasm.internString(qualified_name); |
| 3685 | } else name; |
| 3686 | try wasm.imports.ensureUnusedCapacity(gpa, 1); |
| 3687 | try wasm.function_imports.ensureUnusedCapacity(gpa, 1); |
| 3688 | try wasm.data_imports.ensureUnusedCapacity(gpa, 1); |
| 3689 | const zcu_import = wasm.addZcuImportReserved(ext.owner_nav, symbol_name); |
| 3690 | if (ip.isFunctionType(nav.resolved.?.type)) { |
| 3691 | wasm.function_imports.putAssumeCapacity(symbol_name, .fromZcuImport(zcu_import, wasm)); |
| 3692 | // Ensure there is a corresponding function type table entry. |
| 3693 | const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?; |
| 3694 | _ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target); |
| 3695 | } else { |
| 3696 | wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm)); |
| 3697 | } |
| 3698 | return; |
| 3699 | }, |
| 3700 | } |
| 3701 | //log.debug("updateNav {f} {d}", .{ nav.fqn.fmt(ip), nav_index }); |
| 3702 | assert(!wasm.imports.contains(nav_index)); |
| 3703 | |
| 3704 | if (!Zcu.Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) { |
| 3705 | if (is_obj) { |
| 3706 | assert(!wasm.navs_obj.contains(nav_index)); |
| 3707 | } else { |
| 3708 | assert(!wasm.navs_exe.contains(nav_index)); |
| 3709 | } |
| 3710 | return; |
| 3711 | } |
| 3712 | |
| 3713 | if (is_obj) { |
| 3714 | const zcu_data_starts: ZcuDataStarts = .initObj(wasm); |
| 3715 | const navs_i = try refNavObj(wasm, nav_index); |
| 3716 | const zcu_data = try lowerZcuData(wasm, pt, nav.resolved.?.value); |
| 3717 | navs_i.value(wasm).* = zcu_data; |
| 3718 | try zcu_data_starts.finishObj(wasm, pt); |
| 3719 | } else { |
| 3720 | const zcu_data_starts: ZcuDataStarts = .initExe(wasm); |
| 3721 | const navs_i = try refNavExe(wasm, nav_index); |
| 3722 | const zcu_data = try lowerZcuData(wasm, pt, nav.resolved.?.value); |
| 3723 | navs_i.value(wasm).code = zcu_data.code; |
| 3724 | try zcu_data_starts.finishExe(wasm, pt); |
| 3725 | } |
| 3726 | } |
| 3727 | |
| 3728 | pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) link.Error!void { |
| 3729 | const comp = wasm.base.comp; |
| 3730 | const diags = &comp.link_diags; |
| 3731 | if (wasm.dwarf) |*dw| { |
| 3732 | dw.updateLineNumber(pt.zcu, ti_id, line) catch |err| switch (err) { |
| 3733 | error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, |
| 3734 | else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), |
| 3735 | }; |
| 3736 | } |
| 3737 | } |
| 3738 | |
| 3739 | pub fn updateExports( |
| 3740 | wasm: *Wasm, |
| 3741 | pt: Zcu.PerThread, |
| 3742 | export_indices: []const Zcu.Export.Index, |
| 3743 | ) !void { |
| 3744 | const zcu = pt.zcu; |
| 3745 | const gpa = zcu.gpa; |
| 3746 | const ip = &zcu.intern_pool; |
| 3747 | const is_obj = wasm.base.comp.config.output_mode == .Obj; |
| 3748 | |
| 3749 | for (export_indices) |export_idx| { |
| 3750 | const exp = export_idx.ptr(zcu); |
| 3751 | const name_slice = exp.opts.name.toSlice(ip); |
| 3752 | const name = try wasm.internString(name_slice); |
| 3753 | switch (exp.exported) { |
| 3754 | .nav => |nav_index| { |
| 3755 | log.debug("updateExports '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) }); |
| 3756 | try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx); |
| 3757 | }, |
| 3758 | .uav => |uav_index| { |
| 3759 | // Lower the UAV, as the export may be the only reference. |
| 3760 | const zds: ZcuDataStarts = .init(wasm); |
| 3761 | if (is_obj) { |
| 3762 | const gop = try wasm.uavs_obj.getOrPut(gpa, uav_index); |
| 3763 | if (!gop.found_existing) gop.value_ptr.* = undefined; |
| 3764 | } else { |
| 3765 | const gop = try wasm.uavs_exe.getOrPut(gpa, uav_index); |
| 3766 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 3767 | .code = undefined, |
| 3768 | .count = 0, |
| 3769 | }; |
| 3770 | gop.value_ptr.count += 1; |
| 3771 | } |
| 3772 | try zds.finish(wasm, pt); |
| 3773 | try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx); |
| 3774 | }, |
| 3775 | } |
| 3776 | } |
| 3777 | } |
| 3778 | |
| 3779 | pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| 3780 | const comp = wasm.base.comp; |
| 3781 | const gpa = comp.gpa; |
| 3782 | const io = comp.io; |
| 3783 | |
| 3784 | if (comp.verbose_link) { |
| 3785 | comp.mutex.lockUncancelable(io); // protect comp.arena |
| 3786 | defer comp.mutex.unlock(io); |
| 3787 | |
| 3788 | const argv = &wasm.dump_argv_list; |
| 3789 | switch (input) { |
| 3790 | .res => unreachable, |
| 3791 | .dso_exact => unreachable, |
| 3792 | .dso => unreachable, |
| 3793 | .object, .archive => |obj| { |
| 3794 | try argv.append(gpa, try obj.path.toString(comp.arena)); |
| 3795 | }, |
| 3796 | } |
| 3797 | } |
| 3798 | |
| 3799 | switch (input) { |
| 3800 | .res => unreachable, |
| 3801 | .dso_exact => unreachable, |
| 3802 | .dso => unreachable, |
| 3803 | .object => |obj| try parseObject(wasm, obj), |
| 3804 | .archive => |obj| try parseArchive(wasm, obj), |
| 3805 | } |
| 3806 | } |
| 3807 | |
| 3808 | pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { |
| 3809 | const tracy = trace(@src()); |
| 3810 | defer tracy.end(); |
| 3811 | |
| 3812 | const sub_prog_node = prog_node.start("Wasm Prelink", 0); |
| 3813 | defer sub_prog_node.end(); |
| 3814 | |
| 3815 | const comp = wasm.base.comp; |
| 3816 | const gpa = comp.gpa; |
| 3817 | const rdynamic = comp.config.rdynamic; |
| 3818 | const is_obj = comp.config.output_mode == .Obj; |
| 3819 | |
| 3820 | assert(wasm.missing_exports.entries.len == 0); |
| 3821 | for (wasm.export_symbol_names) |exp_name| { |
| 3822 | const exp_name_interned = try wasm.internString(exp_name); |
| 3823 | if (wasm.object_function_imports.getPtr(exp_name_interned)) |import| { |
| 3824 | if (import.resolution != .unresolved) { |
| 3825 | import.flags.exported = true; |
| 3826 | continue; |
| 3827 | } |
| 3828 | } |
| 3829 | if (wasm.object_global_imports.getPtr(exp_name_interned)) |import| { |
| 3830 | if (import.resolution != .unresolved) { |
| 3831 | import.flags.exported = true; |
| 3832 | continue; |
| 3833 | } |
| 3834 | } |
| 3835 | if (wasm.object_table_imports.getPtr(exp_name_interned)) |import| { |
| 3836 | if (import.resolution != .unresolved) { |
| 3837 | import.flags.exported = true; |
| 3838 | continue; |
| 3839 | } |
| 3840 | } |
| 3841 | try wasm.missing_exports.put(gpa, exp_name_interned, {}); |
| 3842 | } |
| 3843 | |
| 3844 | if (wasm.entry_name.unwrap()) |entry_name| { |
| 3845 | if (wasm.object_function_imports.getPtr(entry_name)) |import| { |
| 3846 | if (import.resolution != .unresolved) { |
| 3847 | import.flags.exported = true; |
| 3848 | wasm.entry_resolution = import.resolution; |
| 3849 | } |
| 3850 | } |
| 3851 | } |
| 3852 | |
| 3853 | if (comp.zcu != null) { |
| 3854 | // Zig always depends on a stack pointer global. |
| 3855 | // If emitting an object, it's an import. Otherwise, the linker synthesizes it. |
| 3856 | if (is_obj) { |
| 3857 | try wasm.global_imports.putNoClobber( |
| 3858 | gpa, |
| 3859 | wasm.preloaded_strings.__stack_pointer, |
| 3860 | .__stack_pointer, |
| 3861 | ); |
| 3862 | } else { |
| 3863 | try wasm.globals.put(gpa, .__stack_pointer, {}); |
| 3864 | assert(wasm.globals.entries.len - 1 == @backingInt(GlobalIndex.stack_pointer)); |
| 3865 | } |
| 3866 | } |
| 3867 | |
| 3868 | // These loops do both recursive marking of alive symbols well as checking for undefined symbols. |
| 3869 | // At the end, output functions and globals will be populated. |
| 3870 | for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| { |
| 3871 | if (import.flags.isIncluded(rdynamic, is_obj)) { |
| 3872 | try markFunctionImport(wasm, name, import, @fromBackingInt(@intCast(i))); |
| 3873 | } |
| 3874 | } |
| 3875 | for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| { |
| 3876 | if (import.flags.isIncluded(rdynamic, is_obj)) { |
| 3877 | try markGlobalImport(wasm, name, import, @fromBackingInt(@intCast(i))); |
| 3878 | } |
| 3879 | } |
| 3880 | wasm.global_exports_len = @intCast(wasm.global_exports.items.len); |
| 3881 | |
| 3882 | for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| { |
| 3883 | if (import.flags.isIncluded(rdynamic, is_obj)) { |
| 3884 | try markTableImport(wasm, name, import, @fromBackingInt(@intCast(i))); |
| 3885 | } |
| 3886 | } |
| 3887 | |
| 3888 | for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| { |
| 3889 | if (import.flags.isIncluded(rdynamic, is_obj)) { |
| 3890 | try markDataImport(wasm, name, import, @fromBackingInt(@intCast(i))); |
| 3891 | } |
| 3892 | } |
| 3893 | |
| 3894 | // This is a wild ass guess at how to merge memories, haven't checked yet |
| 3895 | // what the proper way to do this is. |
| 3896 | for (wasm.object_memory_imports.values()) |*memory_import| { |
| 3897 | wasm.memories.limits.min = @min(wasm.memories.limits.min, memory_import.limits_min); |
| 3898 | wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max); |
| 3899 | wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max; |
| 3900 | } |
| 3901 | |
| 3902 | wasm.functions_end_prelink = @intCast(wasm.functions.entries.len); |
| 3903 | wasm.globals_end_prelink = @intCast(wasm.globals.entries.len); |
| 3904 | wasm.function_imports_len_prelink = @intCast(wasm.function_imports.entries.len); |
| 3905 | wasm.data_imports_len_prelink = @intCast(wasm.data_imports.entries.len); |
| 3906 | } |
| 3907 | |
| 3908 | pub fn markFunctionImport( |
| 3909 | wasm: *Wasm, |
| 3910 | name: String, |
| 3911 | import: *FunctionImport, |
| 3912 | func_index: FunctionImport.Index, |
| 3913 | ) link.Error!void { |
| 3914 | // import.flags.alive might be already true from a previous update. In such |
| 3915 | // case, we must still run the logic in this function, in case the item |
| 3916 | // being marked was reverted by the `flush` logic that resets the hash |
| 3917 | // table watermarks. |
| 3918 | import.flags.alive = true; |
| 3919 | |
| 3920 | const comp = wasm.base.comp; |
| 3921 | const gpa = comp.gpa; |
| 3922 | const is_obj = comp.config.output_mode == .Obj; |
| 3923 | |
| 3924 | try wasm.functions.ensureUnusedCapacity(gpa, 1); |
| 3925 | |
| 3926 | if (import.resolution == .unresolved) { |
| 3927 | if (!is_obj) { |
| 3928 | if (name == wasm.preloaded_strings.__wasm_init_memory) { |
| 3929 | try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{}); |
| 3930 | } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) { |
| 3931 | try wasm.resolveFunctionSynthetic(import, .__wasm_apply_global_tls_relocs, &.{}, &.{}); |
| 3932 | } else if (name == wasm.preloaded_strings.__wasm_call_ctors) { |
| 3933 | try wasm.resolveFunctionSynthetic(import, .__wasm_call_ctors, &.{}, &.{}); |
| 3934 | } else if (name == wasm.preloaded_strings.__wasm_init_tls) { |
| 3935 | try wasm.resolveFunctionSynthetic(import, .__wasm_init_tls, &.{.i32}, &.{}); |
| 3936 | } else { |
| 3937 | try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm)); |
| 3938 | } |
| 3939 | } else { |
| 3940 | try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm)); |
| 3941 | } |
| 3942 | } else switch (import.resolution.unpack(wasm)) { |
| 3943 | .object_function => try markFunction(wasm, import.resolution.unpack(wasm).object_function, import.flags.exported), |
| 3944 | else => return, |
| 3945 | } |
| 3946 | } |
| 3947 | |
| 3948 | /// Recursively mark alive everything referenced by the function. |
| 3949 | fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.Error!void { |
| 3950 | const comp = wasm.base.comp; |
| 3951 | const gpa = comp.gpa; |
| 3952 | const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i)); |
| 3953 | if (gop.found_existing) return; |
| 3954 | |
| 3955 | const rdynamic = comp.config.rdynamic; |
| 3956 | const is_obj = comp.config.output_mode == .Obj; |
| 3957 | const function = i.ptr(wasm); |
| 3958 | try markObject(wasm, function.object_index); |
| 3959 | |
| 3960 | if (!is_obj and (override_export or function.flags.isExported(rdynamic))) { |
| 3961 | const symbol_name = function.name.unwrap().?; |
| 3962 | if (!override_export and function.flags.visibility_hidden) { |
| 3963 | try wasm.hidden_function_exports.put(gpa, symbol_name, @fromBackingInt(@intCast(gop.index))); |
| 3964 | } else { |
| 3965 | try wasm.function_exports.put(gpa, symbol_name, @fromBackingInt(@intCast(gop.index))); |
| 3966 | } |
| 3967 | } |
| 3968 | |
| 3969 | try wasm.markRelocations(function.relocations(wasm)); |
| 3970 | } |
| 3971 | |
| 3972 | fn markObject(wasm: *Wasm, i: ObjectIndex) link.Error!void { |
| 3973 | const object = i.ptr(wasm); |
| 3974 | if (object.is_included) return; |
| 3975 | object.is_included = true; |
| 3976 | |
| 3977 | const init_funcs = wasm.object_init_funcs.items[object.init_funcs.off..][0..object.init_funcs.len]; |
| 3978 | for (init_funcs) |init_func| { |
| 3979 | try markFunction(wasm, init_func.function_index, false); |
| 3980 | } |
| 3981 | } |
| 3982 | |
| 3983 | /// Recursively mark alive everything referenced by the global. |
| 3984 | fn markGlobalImport( |
| 3985 | wasm: *Wasm, |
| 3986 | name: String, |
| 3987 | import: *GlobalImport, |
| 3988 | global_index: GlobalImport.Index, |
| 3989 | ) link.Error!void { |
| 3990 | // import.flags.alive might be already true from a previous update. In such |
| 3991 | // case, we must still run the logic in this function, in case the item |
| 3992 | // being marked was reverted by the `flush` logic that resets the hash |
| 3993 | // table watermarks. |
| 3994 | import.flags.alive = true; |
| 3995 | |
| 3996 | const comp = wasm.base.comp; |
| 3997 | const gpa = comp.gpa; |
| 3998 | const is_obj = comp.config.output_mode == .Obj; |
| 3999 | |
| 4000 | try wasm.globals.ensureUnusedCapacity(gpa, 1); |
| 4001 | |
| 4002 | if (import.resolution == .unresolved) { |
| 4003 | if (!is_obj) { |
| 4004 | if (name == wasm.preloaded_strings.__heap_base) { |
| 4005 | import.resolution = .__heap_base; |
| 4006 | wasm.globals.putAssumeCapacity(.__heap_base, {}); |
| 4007 | } else if (name == wasm.preloaded_strings.__heap_end) { |
| 4008 | import.resolution = .__heap_end; |
| 4009 | wasm.globals.putAssumeCapacity(.__heap_end, {}); |
| 4010 | } else if (name == wasm.preloaded_strings.__stack_pointer) { |
| 4011 | import.resolution = .__stack_pointer; |
| 4012 | wasm.globals.putAssumeCapacity(.__stack_pointer, {}); |
| 4013 | } else if (name == wasm.preloaded_strings.__tls_align) { |
| 4014 | import.resolution = .__tls_align; |
| 4015 | wasm.globals.putAssumeCapacity(.__tls_align, {}); |
| 4016 | } else if (name == wasm.preloaded_strings.__tls_base) { |
| 4017 | import.resolution = .__tls_base; |
| 4018 | wasm.globals.putAssumeCapacity(.__tls_base, {}); |
| 4019 | } else if (name == wasm.preloaded_strings.__tls_size) { |
| 4020 | import.resolution = .__tls_size; |
| 4021 | wasm.globals.putAssumeCapacity(.__tls_size, {}); |
| 4022 | } else { |
| 4023 | try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm)); |
| 4024 | } |
| 4025 | } else { |
| 4026 | try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm)); |
| 4027 | } |
| 4028 | } else switch (import.resolution.unpack(wasm)) { |
| 4029 | .object_global => try markGlobal(wasm, import.resolution.unpack(wasm).object_global, import.flags.exported), |
| 4030 | else => return, |
| 4031 | } |
| 4032 | } |
| 4033 | |
| 4034 | fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Error!void { |
| 4035 | const comp = wasm.base.comp; |
| 4036 | const gpa = comp.gpa; |
| 4037 | const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i)); |
| 4038 | if (gop.found_existing) return; |
| 4039 | |
| 4040 | const rdynamic = comp.config.rdynamic; |
| 4041 | const is_obj = comp.config.output_mode == .Obj; |
| 4042 | const global = i.ptr(wasm); |
| 4043 | try markObject(wasm, global.object_index); |
| 4044 | |
| 4045 | if (!is_obj and (override_export or global.flags.isExported(rdynamic))) try wasm.global_exports.append(gpa, .{ |
| 4046 | .name = global.name.unwrap().?, |
| 4047 | .global_index = @fromBackingInt(@intCast(gop.index)), |
| 4048 | }); |
| 4049 | |
| 4050 | try wasm.markRelocations(global.relocations(wasm)); |
| 4051 | } |
| 4052 | |
| 4053 | pub fn markTableImport( |
| 4054 | wasm: *Wasm, |
| 4055 | name: String, |
| 4056 | import: *TableImport, |
| 4057 | table_index: TableImport.Index, |
| 4058 | ) link.Error!void { |
| 4059 | if (import.flags.alive) return; |
| 4060 | import.flags.alive = true; |
| 4061 | |
| 4062 | const comp = wasm.base.comp; |
| 4063 | const gpa = comp.gpa; |
| 4064 | const is_obj = comp.config.output_mode == .Obj; |
| 4065 | |
| 4066 | try wasm.tables.ensureUnusedCapacity(gpa, 1); |
| 4067 | |
| 4068 | if (import.resolution == .unresolved) { |
| 4069 | if (!is_obj) { |
| 4070 | if (name == wasm.preloaded_strings.__indirect_function_table) { |
| 4071 | import.resolution = .__indirect_function_table; |
| 4072 | wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); |
| 4073 | } else { |
| 4074 | try wasm.table_imports.put(gpa, name, table_index); |
| 4075 | } |
| 4076 | } else { |
| 4077 | try wasm.table_imports.put(gpa, name, table_index); |
| 4078 | } |
| 4079 | } else { |
| 4080 | wasm.tables.putAssumeCapacity(import.resolution, {}); |
| 4081 | // Tables have no relocations. |
| 4082 | } |
| 4083 | } |
| 4084 | |
| 4085 | fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.Error!void { |
| 4086 | const comp = wasm.base.comp; |
| 4087 | const segment = segment_index.ptr(wasm); |
| 4088 | if (segment.flags.alive) return; |
| 4089 | segment.flags.alive = true; |
| 4090 | try markObject(wasm, segment.object_index); |
| 4091 | |
| 4092 | wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or |
| 4093 | (comp.config.import_memory and !wasm.isBss(segment.name)); |
| 4094 | |
| 4095 | try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {}); |
| 4096 | try wasm.markRelocations(segment.relocations(wasm)); |
| 4097 | } |
| 4098 | |
| 4099 | pub fn markDataImport( |
| 4100 | wasm: *Wasm, |
| 4101 | name: String, |
| 4102 | import: *ObjectDataImport, |
| 4103 | data_index: ObjectDataImport.Index, |
| 4104 | ) link.Error!void { |
| 4105 | if (import.flags.alive) return; |
| 4106 | import.flags.alive = true; |
| 4107 | |
| 4108 | const comp = wasm.base.comp; |
| 4109 | const gpa = comp.gpa; |
| 4110 | const is_obj = comp.config.output_mode == .Obj; |
| 4111 | |
| 4112 | if (import.resolution == .unresolved) { |
| 4113 | if (!is_obj) { |
| 4114 | if (name == wasm.preloaded_strings.__global_base) { |
| 4115 | import.resolution = .__global_base; |
| 4116 | } else if (name == wasm.preloaded_strings.__heap_base) { |
| 4117 | import.resolution = .__heap_base; |
| 4118 | } else if (name == wasm.preloaded_strings.__heap_end) { |
| 4119 | import.resolution = .__heap_end; |
| 4120 | } else if (name == wasm.preloaded_strings.__wasm_first_page_end) { |
| 4121 | import.resolution = .__wasm_first_page_end; |
| 4122 | } else { |
| 4123 | try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm)); |
| 4124 | } |
| 4125 | } else { |
| 4126 | try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm)); |
| 4127 | } |
| 4128 | } else switch (import.resolution.unpack(wasm)) { |
| 4129 | .object => |object_data_index| try markData(wasm, object_data_index), |
| 4130 | else => {}, |
| 4131 | } |
| 4132 | } |
| 4133 | |
| 4134 | fn markData(wasm: *Wasm, i: ObjectData.Index) link.Error!void { |
| 4135 | const gpa = wasm.base.comp.gpa; |
| 4136 | const gop = try wasm.datas.getOrPut(gpa, .fromObjectDataIndex(wasm, i)); |
| 4137 | if (gop.found_existing) return; |
| 4138 | |
| 4139 | try markDataSegment(wasm, i.ptr(wasm).segment); |
| 4140 | } |
| 4141 | |
| 4142 | fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Error!void { |
| 4143 | const gpa = wasm.base.comp.gpa; |
| 4144 | for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| { |
| 4145 | if (offset >= relocs.end) break; |
| 4146 | switch (tag) { |
| 4147 | .function_import_index_leb, |
| 4148 | .function_import_index_i32, |
| 4149 | .function_import_offset_i32, |
| 4150 | .function_import_offset_i64, |
| 4151 | => { |
| 4152 | const name = pointee.symbol_name; |
| 4153 | const i: FunctionImport.Index = @fromBackingInt(@intCast(wasm.object_function_imports.getIndex(name).?)); |
| 4154 | try markFunctionImport(wasm, name, i.value(wasm), i); |
| 4155 | }, |
| 4156 | .table_import_index_sleb, |
| 4157 | .table_import_index_i32, |
| 4158 | .table_import_index_sleb64, |
| 4159 | .table_import_index_i64, |
| 4160 | .table_import_index_rel_sleb, |
| 4161 | .table_import_index_rel_sleb64, |
| 4162 | => { |
| 4163 | const name = pointee.symbol_name; |
| 4164 | try wasm.object_indirect_function_import_set.put(gpa, name, {}); |
| 4165 | const i: FunctionImport.Index = @fromBackingInt(@intCast(wasm.object_function_imports.getIndex(name).?)); |
| 4166 | try markFunctionImport(wasm, name, i.value(wasm), i); |
| 4167 | }, |
| 4168 | .global_import_index_leb, .global_import_index_i32 => { |
| 4169 | const name = pointee.symbol_name; |
| 4170 | const i: GlobalImport.Index = @fromBackingInt(@intCast(wasm.object_global_imports.getIndex(name).?)); |
| 4171 | try markGlobalImport(wasm, name, i.value(wasm), i); |
| 4172 | }, |
| 4173 | .table_import_number_leb => { |
| 4174 | const name = pointee.symbol_name; |
| 4175 | const i: TableImport.Index = @fromBackingInt(@intCast(wasm.object_table_imports.getIndex(name).?)); |
| 4176 | try markTableImport(wasm, name, i.value(wasm), i); |
| 4177 | }, |
| 4178 | .memory_addr_import_leb, |
| 4179 | .memory_addr_import_sleb, |
| 4180 | .memory_addr_import_i32, |
| 4181 | .memory_addr_import_rel_sleb, |
| 4182 | .memory_addr_import_leb64, |
| 4183 | .memory_addr_import_sleb64, |
| 4184 | .memory_addr_import_i64, |
| 4185 | .memory_addr_import_rel_sleb64, |
| 4186 | .memory_addr_import_tls_sleb, |
| 4187 | .memory_addr_import_locrel_i32, |
| 4188 | .memory_addr_import_tls_sleb64, |
| 4189 | => { |
| 4190 | const name = pointee.symbol_name; |
| 4191 | const i = ObjectDataImport.Index.fromSymbolName(wasm, name).?; |
| 4192 | try markDataImport(wasm, name, i.value(wasm), i); |
| 4193 | }, |
| 4194 | |
| 4195 | .function_index_leb, |
| 4196 | .function_index_i32, |
| 4197 | .function_offset_i32, |
| 4198 | .function_offset_i64, |
| 4199 | => try markFunction(wasm, pointee.function.chaseWeak(wasm), false), |
| 4200 | .table_index_sleb, |
| 4201 | .table_index_i32, |
| 4202 | .table_index_sleb64, |
| 4203 | .table_index_i64, |
| 4204 | .table_index_rel_sleb, |
| 4205 | .table_index_rel_sleb64, |
| 4206 | => { |
| 4207 | const function = pointee.function; |
| 4208 | try wasm.object_indirect_function_set.put(gpa, function, {}); |
| 4209 | try markFunction(wasm, function.chaseWeak(wasm), false); |
| 4210 | }, |
| 4211 | .global_index_leb, |
| 4212 | .global_index_i32, |
| 4213 | => try markGlobal(wasm, pointee.global.chaseWeak(wasm), false), |
| 4214 | .table_number_leb, |
| 4215 | => try markTable(wasm, pointee.table.chaseWeak(wasm)), |
| 4216 | |
| 4217 | .section_offset_i32 => { |
| 4218 | log.warn("TODO: ensure section {d} is included in output", .{pointee.section}); |
| 4219 | }, |
| 4220 | |
| 4221 | .memory_addr_leb, |
| 4222 | .memory_addr_sleb, |
| 4223 | .memory_addr_i32, |
| 4224 | .memory_addr_rel_sleb, |
| 4225 | .memory_addr_leb64, |
| 4226 | .memory_addr_sleb64, |
| 4227 | .memory_addr_i64, |
| 4228 | .memory_addr_rel_sleb64, |
| 4229 | .memory_addr_tls_sleb, |
| 4230 | .memory_addr_locrel_i32, |
| 4231 | .memory_addr_tls_sleb64, |
| 4232 | => try markData(wasm, pointee.data), |
| 4233 | |
| 4234 | .type_index_leb => continue, |
| 4235 | } |
| 4236 | } |
| 4237 | } |
| 4238 | |
| 4239 | fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.Error!void { |
| 4240 | try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {}); |
| 4241 | } |
| 4242 | |
| 4243 | pub fn flush( |
| 4244 | wasm: *Wasm, |
| 4245 | arena: Allocator, |
| 4246 | tid: Zcu.PerThread.Id, |
| 4247 | prog_node: std.Progress.Node, |
| 4248 | ) link.Error!void { |
| 4249 | _ = arena; |
| 4250 | // The goal is to never use this because it's only needed if we need to |
| 4251 | // write to InternPool, but flush is too late to be writing to the |
| 4252 | // InternPool. |
| 4253 | _ = tid; |
| 4254 | const comp = wasm.base.comp; |
| 4255 | const diags = &comp.link_diags; |
| 4256 | const gpa = comp.gpa; |
| 4257 | const io = comp.io; |
| 4258 | |
| 4259 | if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items); |
| 4260 | |
| 4261 | const tracy = trace(@src()); |
| 4262 | defer tracy.end(); |
| 4263 | |
| 4264 | const sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 4265 | defer sub_prog_node.end(); |
| 4266 | |
| 4267 | const functions_end_zcu: u32 = @intCast(wasm.functions.entries.len); |
| 4268 | defer wasm.functions.shrinkRetainingCapacity(functions_end_zcu); |
| 4269 | |
| 4270 | const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len); |
| 4271 | defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu); |
| 4272 | |
| 4273 | const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len); |
| 4274 | defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu); |
| 4275 | |
| 4276 | const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len); |
| 4277 | defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu); |
| 4278 | |
| 4279 | const global_exports_end_zcu: u32 = @intCast(wasm.global_exports.items.len); |
| 4280 | defer wasm.global_exports.shrinkRetainingCapacity(global_exports_end_zcu); |
| 4281 | |
| 4282 | wasm.flush_buffer.clear(); |
| 4283 | wasm.tag_name_bytes.clearRetainingCapacity(); |
| 4284 | wasm.tag_name_offs.clearRetainingCapacity(); |
| 4285 | wasm.tag_name_table_ref_count = 0; |
| 4286 | try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{}); |
| 4287 | try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values()); |
| 4288 | try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values()); |
| 4289 | try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values()); |
| 4290 | |
| 4291 | return wasm.flush_buffer.finish(wasm) catch |err| switch (err) { |
| 4292 | error.OutOfMemory, error.AlreadyReported => |e| return e, |
| 4293 | else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}), |
| 4294 | }; |
| 4295 | } |
| 4296 | |
| 4297 | fn defaultEntrySymbolName( |
| 4298 | preloaded_strings: *const PreloadedStrings, |
| 4299 | wasi_exec_model: std.lang.WasiExecModel, |
| 4300 | ) String { |
| 4301 | return switch (wasi_exec_model) { |
| 4302 | .reactor => preloaded_strings._initialize, |
| 4303 | .command => preloaded_strings._start, |
| 4304 | }; |
| 4305 | } |
| 4306 | |
| 4307 | pub fn internOptionalString(wasm: *Wasm, optional_bytes: ?[]const u8) Allocator.Error!OptionalString { |
| 4308 | const bytes = optional_bytes orelse return .none; |
| 4309 | const string = try internString(wasm, bytes); |
| 4310 | return string.toOptional(); |
| 4311 | } |
| 4312 | |
| 4313 | pub fn internString(wasm: *Wasm, bytes: []const u8) Allocator.Error!String { |
| 4314 | assert(mem.findScalar(u8, bytes, 0) == null); |
| 4315 | wasm.string_bytes_lock.lock(); |
| 4316 | defer wasm.string_bytes_lock.unlock(); |
| 4317 | const gpa = wasm.base.comp.gpa; |
| 4318 | const gop = try wasm.string_table.getOrPutContextAdapted( |
| 4319 | gpa, |
| 4320 | @as([]const u8, bytes), |
| 4321 | @as(String.TableIndexAdapter, .{ .bytes = wasm.string_bytes.items }), |
| 4322 | @as(String.TableContext, .{ .bytes = wasm.string_bytes.items }), |
| 4323 | ); |
| 4324 | if (gop.found_existing) return gop.key_ptr.*; |
| 4325 | |
| 4326 | try wasm.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1); |
| 4327 | const new_off: String = @fromBackingInt(@intCast(wasm.string_bytes.items.len)); |
| 4328 | |
| 4329 | wasm.string_bytes.appendSliceAssumeCapacity(bytes); |
| 4330 | wasm.string_bytes.appendAssumeCapacity(0); |
| 4331 | |
| 4332 | gop.key_ptr.* = new_off; |
| 4333 | |
| 4334 | return new_off; |
| 4335 | } |
| 4336 | |
| 4337 | // TODO implement instead by appending to string_bytes |
| 4338 | pub fn internStringFmt(wasm: *Wasm, comptime format: []const u8, args: anytype) Allocator.Error!String { |
| 4339 | var buffer: [32]u8 = undefined; |
| 4340 | const slice = std.mem.print(&buffer, format, args) catch unreachable; |
| 4341 | return internString(wasm, slice); |
| 4342 | } |
| 4343 | |
| 4344 | pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String { |
| 4345 | assert(mem.findScalar(u8, bytes, 0) == null); |
| 4346 | return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{ |
| 4347 | .bytes = wasm.string_bytes.items, |
| 4348 | })); |
| 4349 | } |
| 4350 | |
| 4351 | pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) Allocator.Error!ValtypeList { |
| 4352 | return .fromString(try internString(wasm, @ptrCast(valtype_list))); |
| 4353 | } |
| 4354 | |
| 4355 | pub fn getExistingValtypeList(wasm: *const Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList { |
| 4356 | return .fromString(getExistingString(wasm, @ptrCast(valtype_list)) orelse return null); |
| 4357 | } |
| 4358 | |
| 4359 | pub fn addFuncType(wasm: *Wasm, ft: FunctionType) Allocator.Error!FunctionType.Index { |
| 4360 | const gpa = wasm.base.comp.gpa; |
| 4361 | const gop = try wasm.func_types.getOrPut(gpa, ft); |
| 4362 | return @fromBackingInt(@intCast(gop.index)); |
| 4363 | } |
| 4364 | |
| 4365 | pub fn getExistingFuncType(wasm: *const Wasm, ft: FunctionType) ?FunctionType.Index { |
| 4366 | const index = wasm.func_types.getIndex(ft) orelse return null; |
| 4367 | return @fromBackingInt(@intCast(index)); |
| 4368 | } |
| 4369 | |
| 4370 | pub fn getExistingFuncType2(wasm: *const Wasm, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype) FunctionType.Index { |
| 4371 | return getExistingFuncType(wasm, .{ |
| 4372 | .params = getExistingValtypeList(wasm, params).?, |
| 4373 | .returns = getExistingValtypeList(wasm, returns).?, |
| 4374 | }).?; |
| 4375 | } |
| 4376 | |
| 4377 | pub fn internFunctionType( |
| 4378 | wasm: *Wasm, |
| 4379 | cc: std.lang.CallingConvention, |
| 4380 | params: []const InternPool.Index, |
| 4381 | return_type: Zcu.Type, |
| 4382 | is_var_args: bool, |
| 4383 | target: *const std.Target, |
| 4384 | ) Allocator.Error!FunctionType.Index { |
| 4385 | try convertZcuFnType(wasm.base.comp, cc, params, return_type, is_var_args, target, &wasm.params_scratch, &wasm.returns_scratch); |
| 4386 | return wasm.addFuncType(.{ |
| 4387 | .params = try wasm.internValtypeList(wasm.params_scratch.items), |
| 4388 | .returns = try wasm.internValtypeList(wasm.returns_scratch.items), |
| 4389 | }); |
| 4390 | } |
| 4391 | |
| 4392 | pub fn getExistingFunctionType( |
| 4393 | wasm: *Wasm, |
| 4394 | cc: std.lang.CallingConvention, |
| 4395 | params: []const InternPool.Index, |
| 4396 | return_type: Zcu.Type, |
| 4397 | is_var_args: bool, |
| 4398 | target: *const std.Target, |
| 4399 | ) ?FunctionType.Index { |
| 4400 | convertZcuFnType(wasm.base.comp, cc, params, return_type, is_var_args, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) { |
| 4401 | error.OutOfMemory => return null, |
| 4402 | }; |
| 4403 | return wasm.getExistingFuncType(.{ |
| 4404 | .params = wasm.getExistingValtypeList(wasm.params_scratch.items) orelse return null, |
| 4405 | .returns = wasm.getExistingValtypeList(wasm.returns_scratch.items) orelse return null, |
| 4406 | }); |
| 4407 | } |
| 4408 | |
| 4409 | fn internIntrinsicType( |
| 4410 | wasm: *Wasm, |
| 4411 | params: []const InternPool.Index, |
| 4412 | return_type: Zcu.Type, |
| 4413 | ) Allocator.Error!FunctionType.Index { |
| 4414 | const target = &wasm.base.comp.root_mod.resolved_target.result; |
| 4415 | return wasm.internFunctionType(.{ .wasm_mvp = .{} }, params, return_type, false, target); |
| 4416 | } |
| 4417 | |
| 4418 | pub fn intrinsicFunctionType(wasm: *Wasm, intrinsic: Mir.Intrinsic) Allocator.Error!FunctionType.Index { |
| 4419 | return switch (intrinsic) { |
| 4420 | .__addhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4421 | .__addtf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4422 | .__addxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4423 | .__ashlti3 => internIntrinsicType(wasm, &.{ .i128_type, .i32_type }, .i128), |
| 4424 | .__ashrti3 => internIntrinsicType(wasm, &.{ .i128_type, .i32_type }, .i128), |
| 4425 | .__bitreversedi2 => internIntrinsicType(wasm, &.{.u64_type}, .u64), |
| 4426 | .__bitreversesi2 => internIntrinsicType(wasm, &.{.u32_type}, .u32), |
| 4427 | .__bswapdi2 => internIntrinsicType(wasm, &.{.u64_type}, .u64), |
| 4428 | .__bswapsi2 => internIntrinsicType(wasm, &.{.u32_type}, .u32), |
| 4429 | .__ceilh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4430 | .__ceilx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4431 | .__cosh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4432 | .__cosx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4433 | .__divei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), |
| 4434 | .__divhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4435 | .__divtf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4436 | .__divti3 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type }, .i128), |
| 4437 | .__divxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4438 | .__eqtf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), |
| 4439 | .__eqxf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), |
| 4440 | .__exp2h => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4441 | .__exp2x => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4442 | .__exph => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4443 | .__expx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4444 | .__extenddftf2 => internIntrinsicType(wasm, &.{.f64_type}, .f128), |
| 4445 | .__extenddfxf2 => internIntrinsicType(wasm, &.{.f64_type}, .f80), |
| 4446 | .__extendhfsf2 => internIntrinsicType(wasm, &.{.f16_type}, .f32), |
| 4447 | .__extendhftf2 => internIntrinsicType(wasm, &.{.f16_type}, .f128), |
| 4448 | .__extendhfxf2 => internIntrinsicType(wasm, &.{.f16_type}, .f80), |
| 4449 | .__extendsftf2 => internIntrinsicType(wasm, &.{.f32_type}, .f128), |
| 4450 | .__extendsfxf2 => internIntrinsicType(wasm, &.{.f32_type}, .f80), |
| 4451 | .__extendxftf2 => internIntrinsicType(wasm, &.{.f80_type}, .f128), |
| 4452 | .__fabsh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4453 | .__fabsx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4454 | .__fixdfdi => internIntrinsicType(wasm, &.{.f64_type}, .i64), |
| 4455 | .__fixdfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f64_type }, .void), |
| 4456 | .__fixdfsi => internIntrinsicType(wasm, &.{.f64_type}, .i32), |
| 4457 | .__fixdfti => internIntrinsicType(wasm, &.{.f64_type}, .i128), |
| 4458 | .__fixhfdi => internIntrinsicType(wasm, &.{.f16_type}, .i64), |
| 4459 | .__fixhfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f16_type }, .void), |
| 4460 | .__fixhfsi => internIntrinsicType(wasm, &.{.f16_type}, .i32), |
| 4461 | .__fixhfti => internIntrinsicType(wasm, &.{.f16_type}, .i128), |
| 4462 | .__fixsfdi => internIntrinsicType(wasm, &.{.f32_type}, .i64), |
| 4463 | .__fixsfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f32_type }, .void), |
| 4464 | .__fixsfsi => internIntrinsicType(wasm, &.{.f32_type}, .i32), |
| 4465 | .__fixsfti => internIntrinsicType(wasm, &.{.f32_type}, .i128), |
| 4466 | .__fixtfdi => internIntrinsicType(wasm, &.{.f128_type}, .i64), |
| 4467 | .__fixtfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f128_type }, .void), |
| 4468 | .__fixtfsi => internIntrinsicType(wasm, &.{.f128_type}, .i32), |
| 4469 | .__fixtfti => internIntrinsicType(wasm, &.{.f128_type}, .i128), |
| 4470 | .__fixunsdfdi => internIntrinsicType(wasm, &.{.f64_type}, .u64), |
| 4471 | .__fixunsdfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f64_type }, .void), |
| 4472 | .__fixunsdfsi => internIntrinsicType(wasm, &.{.f64_type}, .u32), |
| 4473 | .__fixunsdfti => internIntrinsicType(wasm, &.{.f64_type}, .u128), |
| 4474 | .__fixunshfdi => internIntrinsicType(wasm, &.{.f16_type}, .u64), |
| 4475 | .__fixunshfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f16_type }, .void), |
| 4476 | .__fixunshfsi => internIntrinsicType(wasm, &.{.f16_type}, .u32), |
| 4477 | .__fixunshfti => internIntrinsicType(wasm, &.{.f16_type}, .u128), |
| 4478 | .__fixunssfdi => internIntrinsicType(wasm, &.{.f32_type}, .u64), |
| 4479 | .__fixunssfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f32_type }, .void), |
| 4480 | .__fixunssfsi => internIntrinsicType(wasm, &.{.f32_type}, .u32), |
| 4481 | .__fixunssfti => internIntrinsicType(wasm, &.{.f32_type}, .u128), |
| 4482 | .__fixunstfdi => internIntrinsicType(wasm, &.{.f128_type}, .u64), |
| 4483 | .__fixunstfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f128_type }, .void), |
| 4484 | .__fixunstfsi => internIntrinsicType(wasm, &.{.f128_type}, .u32), |
| 4485 | .__fixunstfti => internIntrinsicType(wasm, &.{.f128_type}, .u128), |
| 4486 | .__fixunsxfdi => internIntrinsicType(wasm, &.{.f80_type}, .u64), |
| 4487 | .__fixunsxfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f80_type }, .void), |
| 4488 | .__fixunsxfsi => internIntrinsicType(wasm, &.{.f80_type}, .u32), |
| 4489 | .__fixunsxfti => internIntrinsicType(wasm, &.{.f80_type}, .u128), |
| 4490 | .__fixxfdi => internIntrinsicType(wasm, &.{.f80_type}, .i64), |
| 4491 | .__fixxfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f80_type }, .void), |
| 4492 | .__fixxfsi => internIntrinsicType(wasm, &.{.f80_type}, .i32), |
| 4493 | .__fixxfti => internIntrinsicType(wasm, &.{.f80_type}, .i128), |
| 4494 | .__floatdidf => internIntrinsicType(wasm, &.{.i64_type}, .f64), |
| 4495 | .__floatdihf => internIntrinsicType(wasm, &.{.i64_type}, .f16), |
| 4496 | .__floatdisf => internIntrinsicType(wasm, &.{.i64_type}, .f32), |
| 4497 | .__floatditf => internIntrinsicType(wasm, &.{.i64_type}, .f128), |
| 4498 | .__floatdixf => internIntrinsicType(wasm, &.{.i64_type}, .f80), |
| 4499 | .__floateidf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f64), |
| 4500 | .__floateihf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f16), |
| 4501 | .__floateisf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f32), |
| 4502 | .__floateitf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f128), |
| 4503 | .__floateixf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f80), |
| 4504 | .__floatsidf => internIntrinsicType(wasm, &.{.i32_type}, .f64), |
| 4505 | .__floatsihf => internIntrinsicType(wasm, &.{.i32_type}, .f16), |
| 4506 | .__floatsisf => internIntrinsicType(wasm, &.{.i32_type}, .f32), |
| 4507 | .__floatsitf => internIntrinsicType(wasm, &.{.i32_type}, .f128), |
| 4508 | .__floatsixf => internIntrinsicType(wasm, &.{.i32_type}, .f80), |
| 4509 | .__floattidf => internIntrinsicType(wasm, &.{.i128_type}, .f64), |
| 4510 | .__floattihf => internIntrinsicType(wasm, &.{.i128_type}, .f16), |
| 4511 | .__floattisf => internIntrinsicType(wasm, &.{.i128_type}, .f32), |
| 4512 | .__floattitf => internIntrinsicType(wasm, &.{.i128_type}, .f128), |
| 4513 | .__floattixf => internIntrinsicType(wasm, &.{.i128_type}, .f80), |
| 4514 | .__floatundidf => internIntrinsicType(wasm, &.{.u64_type}, .f64), |
| 4515 | .__floatundihf => internIntrinsicType(wasm, &.{.u64_type}, .f16), |
| 4516 | .__floatundisf => internIntrinsicType(wasm, &.{.u64_type}, .f32), |
| 4517 | .__floatunditf => internIntrinsicType(wasm, &.{.u64_type}, .f128), |
| 4518 | .__floatundixf => internIntrinsicType(wasm, &.{.u64_type}, .f80), |
| 4519 | .__floatuneidf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f64), |
| 4520 | .__floatuneihf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f16), |
| 4521 | .__floatuneisf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f32), |
| 4522 | .__floatuneitf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f128), |
| 4523 | .__floatuneixf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f80), |
| 4524 | .__floatunsidf => internIntrinsicType(wasm, &.{.u32_type}, .f64), |
| 4525 | .__floatunsihf => internIntrinsicType(wasm, &.{.u32_type}, .f16), |
| 4526 | .__floatunsisf => internIntrinsicType(wasm, &.{.u32_type}, .f32), |
| 4527 | .__floatunsitf => internIntrinsicType(wasm, &.{.u32_type}, .f128), |
| 4528 | .__floatunsixf => internIntrinsicType(wasm, &.{.u32_type}, .f80), |
| 4529 | .__floatuntidf => internIntrinsicType(wasm, &.{.u128_type}, .f64), |
| 4530 | .__floatuntihf => internIntrinsicType(wasm, &.{.u128_type}, .f16), |
| 4531 | .__floatuntisf => internIntrinsicType(wasm, &.{.u128_type}, .f32), |
| 4532 | .__floatuntitf => internIntrinsicType(wasm, &.{.u128_type}, .f128), |
| 4533 | .__floatuntixf => internIntrinsicType(wasm, &.{.u128_type}, .f80), |
| 4534 | .__floorh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4535 | .__floorx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4536 | .__fmah => internIntrinsicType(wasm, &.{ .f16_type, .f16_type, .f16_type }, .f16), |
| 4537 | .__fmax => internIntrinsicType(wasm, &.{ .f80_type, .f80_type, .f80_type }, .f80), |
| 4538 | .__fmaxh => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4539 | .__fmaxx => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4540 | .__fminh => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4541 | .__fminx => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4542 | .__fmodh => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4543 | .__fmodx => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4544 | .__getf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), |
| 4545 | .__gexf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), |
| 4546 | .__gttf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), |
| 4547 | .__gtxf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), |
| 4548 | .__letf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), |
| 4549 | .__lexf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), |
| 4550 | .__log10h => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4551 | .__log10x => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4552 | .__log2h => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4553 | .__log2x => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4554 | .__logh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4555 | .__logx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4556 | .__lshrti3 => internIntrinsicType(wasm, &.{ .i128_type, .i32_type }, .i128), |
| 4557 | .__lttf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), |
| 4558 | .__ltxf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), |
| 4559 | .__modei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), |
| 4560 | .__modti3 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type }, .i128), |
| 4561 | .__mulhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4562 | .__mulodi4 => internIntrinsicType(wasm, &.{ .i64_type, .i64_type, .usize_type }, .i64), |
| 4563 | .__muloti4 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type, .usize_type }, .i128), |
| 4564 | .__multf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4565 | .__multi3 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type }, .i128), |
| 4566 | .__mulxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4567 | .__netf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), |
| 4568 | .__nexf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), |
| 4569 | .__roundh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4570 | .__roundx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4571 | .__sinh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4572 | .__sinx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4573 | .__sqrth => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4574 | .__sqrtx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4575 | .__subhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), |
| 4576 | .__subtf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4577 | .__subxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), |
| 4578 | .__tanh => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4579 | .__tanx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4580 | .__trunch => internIntrinsicType(wasm, &.{.f16_type}, .f16), |
| 4581 | .__truncsfhf2 => internIntrinsicType(wasm, &.{.f32_type}, .f16), |
| 4582 | .__trunctfdf2 => internIntrinsicType(wasm, &.{.f128_type}, .f64), |
| 4583 | .__trunctfhf2 => internIntrinsicType(wasm, &.{.f128_type}, .f16), |
| 4584 | .__trunctfsf2 => internIntrinsicType(wasm, &.{.f128_type}, .f32), |
| 4585 | .__trunctfxf2 => internIntrinsicType(wasm, &.{.f128_type}, .f80), |
| 4586 | .__truncx => internIntrinsicType(wasm, &.{.f80_type}, .f80), |
| 4587 | .__truncxfdf2 => internIntrinsicType(wasm, &.{.f80_type}, .f64), |
| 4588 | .__truncxfhf2 => internIntrinsicType(wasm, &.{.f80_type}, .f16), |
| 4589 | .__truncxfsf2 => internIntrinsicType(wasm, &.{.f80_type}, .f32), |
| 4590 | .__udivei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), |
| 4591 | .__udivti3 => internIntrinsicType(wasm, &.{ .u128_type, .u128_type }, .u128), |
| 4592 | .__umodei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), |
| 4593 | .__umodti3 => internIntrinsicType(wasm, &.{ .u128_type, .u128_type }, .u128), |
| 4594 | .ceilf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4595 | .cos => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4596 | .cosf => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4597 | .cosf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4598 | .exp => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4599 | .exp2 => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4600 | .exp2f => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4601 | .exp2f128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4602 | .expf => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4603 | .expf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4604 | .fabsf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4605 | .floorf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4606 | .fma => internIntrinsicType(wasm, &.{ .f64_type, .f64_type, .f64_type }, .f64), |
| 4607 | .fmaf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type, .f32_type }, .f32), |
| 4608 | .fmaf128 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type, .f128_type }, .f128), |
| 4609 | .fmax => internIntrinsicType(wasm, &.{ .f64_type, .f64_type }, .f64), |
| 4610 | .fmaxf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type }, .f32), |
| 4611 | .fmaxf128 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4612 | .fmin => internIntrinsicType(wasm, &.{ .f64_type, .f64_type }, .f64), |
| 4613 | .fminf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type }, .f32), |
| 4614 | .fminf128 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4615 | .fmod => internIntrinsicType(wasm, &.{ .f64_type, .f64_type }, .f64), |
| 4616 | .fmodf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type }, .f32), |
| 4617 | .fmodf128 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), |
| 4618 | .log => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4619 | .log10 => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4620 | .log10f => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4621 | .log10f128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4622 | .log2 => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4623 | .log2f => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4624 | .log2f128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4625 | .logf => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4626 | .logf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4627 | .roundf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4628 | .sin => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4629 | .sinf => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4630 | .sinf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4631 | .sqrtf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4632 | .tan => internIntrinsicType(wasm, &.{.f64_type}, .f64), |
| 4633 | .tanf => internIntrinsicType(wasm, &.{.f32_type}, .f32), |
| 4634 | .tanf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4635 | .truncf128 => internIntrinsicType(wasm, &.{.f128_type}, .f128), |
| 4636 | .memcpy => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type }, .usize), |
| 4637 | .memmove => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type }, .usize), |
| 4638 | .memset => internIntrinsicType(wasm, &.{ .usize_type, .i32_type, .usize_type }, .usize), |
| 4639 | .__addo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .bool_type, .u16_type }, .bool), |
| 4640 | .__subo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .bool_type, .u16_type }, .bool), |
| 4641 | .__cmp_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .i8), |
| 4642 | .__and_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .u16_type }, .void), |
| 4643 | .__or_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .u16_type }, .void), |
| 4644 | .__xor_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .u16_type }, .void), |
| 4645 | .__not_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .void), |
| 4646 | .__shlo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .u16_type, .bool_type, .u16_type }, .bool), |
| 4647 | .__shr_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .u16_type, .bool_type, .u16_type }, .void), |
| 4648 | .__clz_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .u16_type }, .u16), |
| 4649 | .__ctz_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .u16_type }, .u16), |
| 4650 | .__popcount_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .u16_type }, .u16), |
| 4651 | .__bitreverse_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .void), |
| 4652 | .__byteswap_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .void), |
| 4653 | .__mulo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .bool_type, .u16_type }, .bool), |
| 4654 | .__abs_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .u16_type }, .void), |
| 4655 | }; |
| 4656 | } |
| 4657 | |
| 4658 | pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr { |
| 4659 | const gpa = wasm.base.comp.gpa; |
| 4660 | // We can't use string table deduplication here since these expressions can |
| 4661 | // have null bytes in them however it may be interesting to explore since |
| 4662 | // it is likely for globals to share initialization values. Then again |
| 4663 | // there may not be very many globals in total. |
| 4664 | try wasm.string_bytes.appendSlice(gpa, bytes); |
| 4665 | return @fromBackingInt(@intCast(wasm.string_bytes.items.len - bytes.len)); |
| 4666 | } |
| 4667 | |
| 4668 | pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataPayload { |
| 4669 | const gpa = wasm.base.comp.gpa; |
| 4670 | try wasm.string_bytes.appendSlice(gpa, bytes); |
| 4671 | return .{ |
| 4672 | .off = @fromBackingInt(@intCast(wasm.string_bytes.items.len - bytes.len)), |
| 4673 | .len = @intCast(bytes.len), |
| 4674 | }; |
| 4675 | } |
| 4676 | |
| 4677 | pub fn addNavReloc( |
| 4678 | wasm: *Wasm, |
| 4679 | reloc_offset: usize, |
| 4680 | nav_index: InternPool.Nav.Index, |
| 4681 | nav_ty: Zcu.Type, |
| 4682 | addend: u32, |
| 4683 | ) !void { |
| 4684 | const comp = wasm.base.comp; |
| 4685 | const zcu = comp.zcu.?; |
| 4686 | const ip = &zcu.intern_pool; |
| 4687 | const gpa = comp.gpa; |
| 4688 | |
| 4689 | const is_obj = comp.config.output_mode == .Obj; |
| 4690 | |
| 4691 | if (nav_ty.zigTypeTag(zcu) == .@"fn") { |
| 4692 | const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); |
| 4693 | if (!gop.found_existing) gop.value_ptr.* = {}; |
| 4694 | if (is_obj) { |
| 4695 | assert(addend == 0); |
| 4696 | try wasm.zcu_relocations.append(gpa, .{ |
| 4697 | .offset = @intCast(reloc_offset), |
| 4698 | .pointee = .{ .function_nav = nav_index }, |
| 4699 | .tag = switch (wasm.pointerSize()) { |
| 4700 | 4 => .table_index_i32, |
| 4701 | 8 => .table_index_i64, |
| 4702 | else => unreachable, |
| 4703 | }, |
| 4704 | .addend = 0, |
| 4705 | }); |
| 4706 | } else { |
| 4707 | try wasm.func_table_fixups.append(gpa, .{ |
| 4708 | .nav_index = nav_index, |
| 4709 | .offset = @intCast(reloc_offset), |
| 4710 | }); |
| 4711 | } |
| 4712 | } else { |
| 4713 | if (is_obj) { |
| 4714 | if (ip.getNav(nav_index).getExtern(ip) == null) _ = try wasm.refNavObj(nav_index); |
| 4715 | try wasm.zcu_relocations.append(gpa, .{ |
| 4716 | .offset = @intCast(reloc_offset), |
| 4717 | .pointee = .{ .data_nav = nav_index }, |
| 4718 | .tag = switch (wasm.pointerSize()) { |
| 4719 | 4 => .memory_addr_i32, |
| 4720 | 8 => .memory_addr_i64, |
| 4721 | else => unreachable, |
| 4722 | }, |
| 4723 | .addend = @intCast(addend), |
| 4724 | }); |
| 4725 | } else { |
| 4726 | try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1); |
| 4727 | wasm.nav_fixups.appendAssumeCapacity(.{ |
| 4728 | .nav_index = nav_index, |
| 4729 | .offset = @intCast(reloc_offset), |
| 4730 | .addend = addend, |
| 4731 | }); |
| 4732 | } |
| 4733 | } |
| 4734 | } |
| 4735 | |
| 4736 | pub fn addUavReloc( |
| 4737 | wasm: *Wasm, |
| 4738 | reloc_offset: usize, |
| 4739 | uav_val: InternPool.Index, |
| 4740 | orig_ptr_ty: InternPool.Index, |
| 4741 | addend: u32, |
| 4742 | ) !void { |
| 4743 | const comp = wasm.base.comp; |
| 4744 | const zcu = comp.zcu.?; |
| 4745 | const ip = &zcu.intern_pool; |
| 4746 | const gpa = comp.gpa; |
| 4747 | |
| 4748 | @"align": { |
| 4749 | const ptr_type = ip.indexToKey(orig_ptr_ty).ptr_type; |
| 4750 | const this_align = ptr_type.flags.alignment; |
| 4751 | if (this_align == .none) break :@"align"; |
| 4752 | const abi_align = Zcu.Type.fromInterned(ptr_type.child).abiAlignment(zcu); |
| 4753 | if (this_align.compare(.lte, abi_align)) break :@"align"; |
| 4754 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); |
| 4755 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align; |
| 4756 | } |
| 4757 | |
| 4758 | if (comp.config.output_mode == .Obj) { |
| 4759 | const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); |
| 4760 | if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later |
| 4761 | try wasm.zcu_relocations.append(gpa, .{ |
| 4762 | .offset = @intCast(reloc_offset), |
| 4763 | .pointee = .{ .data_uav = uav_val }, |
| 4764 | .tag = switch (wasm.pointerSize()) { |
| 4765 | 4 => .memory_addr_i32, |
| 4766 | 8 => .memory_addr_i64, |
| 4767 | else => unreachable, |
| 4768 | }, |
| 4769 | .addend = @intCast(addend), |
| 4770 | }); |
| 4771 | } else { |
| 4772 | const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); |
| 4773 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 4774 | .code = undefined, // to avoid recursion, `ZcuDataStarts` will lower the value later |
| 4775 | .count = 0, |
| 4776 | }; |
| 4777 | gop.value_ptr.count += 1; |
| 4778 | try wasm.uav_fixups.append(gpa, .{ |
| 4779 | .uavs_exe_index = @fromBackingInt(@intCast(gop.index)), |
| 4780 | .offset = @intCast(reloc_offset), |
| 4781 | .addend = addend, |
| 4782 | }); |
| 4783 | } |
| 4784 | } |
| 4785 | |
| 4786 | pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { |
| 4787 | const comp = wasm.base.comp; |
| 4788 | const gpa = comp.gpa; |
| 4789 | assert(comp.config.output_mode == .Obj); |
| 4790 | const gop = try wasm.navs_obj.getOrPut(gpa, nav_index); |
| 4791 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 4792 | // Lowering the value is delayed to avoid recursion. |
| 4793 | .code = undefined, |
| 4794 | .relocs = undefined, |
| 4795 | }; |
| 4796 | return @fromBackingInt(@intCast(gop.index)); |
| 4797 | } |
| 4798 | |
| 4799 | pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { |
| 4800 | const comp = wasm.base.comp; |
| 4801 | const gpa = comp.gpa; |
| 4802 | assert(comp.config.output_mode != .Obj); |
| 4803 | const gop = try wasm.navs_exe.getOrPut(gpa, nav_index); |
| 4804 | if (gop.found_existing) { |
| 4805 | gop.value_ptr.count += 1; |
| 4806 | } else { |
| 4807 | gop.value_ptr.* = .{ |
| 4808 | // Lowering the value is delayed to avoid recursion. |
| 4809 | .code = undefined, |
| 4810 | .count = 0, |
| 4811 | }; |
| 4812 | } |
| 4813 | return @fromBackingInt(@intCast(gop.index)); |
| 4814 | } |
| 4815 | |
| 4816 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4817 | pub fn uavAddr(wasm: *const Wasm, ip_index: InternPool.Index) u32 { |
| 4818 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4819 | const comp = wasm.base.comp; |
| 4820 | assert(comp.config.output_mode != .Obj); |
| 4821 | const uav_index: UavsExeIndex = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)); |
| 4822 | const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); |
| 4823 | return wasm.flush_buffer.data_segments.get(ds_id).?; |
| 4824 | } |
| 4825 | |
| 4826 | pub fn syntheticDataAddr(wasm: *const Wasm, resolution: ObjectDataImport.Resolution) ?u32 { |
| 4827 | const virtual_addrs = wasm.flush_buffer.virtual_addrs; |
| 4828 | return switch (resolution.unpack(wasm)) { |
| 4829 | .__global_base => virtual_addrs.global_base, |
| 4830 | .__heap_base => virtual_addrs.heap_base, |
| 4831 | .__heap_end => virtual_addrs.heap_end, |
| 4832 | .__wasm_first_page_end => virtual_addrs.wasm_first_page_end, |
| 4833 | else => null, |
| 4834 | }; |
| 4835 | } |
| 4836 | |
| 4837 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4838 | pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 { |
| 4839 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4840 | const comp = wasm.base.comp; |
| 4841 | assert(comp.config.output_mode != .Obj); |
| 4842 | if (wasm.navs_exe.getIndex(nav_index)) |i| { |
| 4843 | const navs_exe_index: NavsExeIndex = @fromBackingInt(@intCast(i)); |
| 4844 | log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index }); |
| 4845 | const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index }); |
| 4846 | return wasm.flush_buffer.data_segments.get(ds_id).?; |
| 4847 | } |
| 4848 | const zcu = comp.zcu.?; |
| 4849 | const ip = &zcu.intern_pool; |
| 4850 | switch (ip.indexToKey(ip.getNav(nav_index).resolved.?.value)) { |
| 4851 | .@"extern" => |ext| if (wasm.getExistingString(ext.name.toSlice(ip))) |symbol_name| { |
| 4852 | if (wasm.object_data_imports.getPtr(symbol_name)) |import| { |
| 4853 | switch (import.resolution.unpack(wasm)) { |
| 4854 | .unresolved => {}, |
| 4855 | .object => |object_data_index| { |
| 4856 | const object_data = object_data_index.ptr(wasm); |
| 4857 | const ds_id: DataSegmentId = .fromObjectDataSegment(wasm, object_data.segment); |
| 4858 | const segment_base_addr = wasm.flush_buffer.data_segments.get(ds_id).?; |
| 4859 | return segment_base_addr + object_data.offset; |
| 4860 | }, |
| 4861 | .__global_base, |
| 4862 | .__heap_base, |
| 4863 | .__heap_end, |
| 4864 | .__wasm_first_page_end, |
| 4865 | => return wasm.syntheticDataAddr(import.resolution).?, |
| 4866 | .uav_exe, |
| 4867 | .nav_exe, |
| 4868 | => { |
| 4869 | const data_loc = import.resolution.dataLoc(wasm); |
| 4870 | return wasm.flush_buffer.data_segments.get(data_loc.segment).? + data_loc.offset; |
| 4871 | }, |
| 4872 | .__zig_error_names, |
| 4873 | .__zig_error_name_table, |
| 4874 | .__zig_tag_names, |
| 4875 | .__zig_tag_name_table, |
| 4876 | .uav_obj, |
| 4877 | .nav_obj, |
| 4878 | => unreachable, |
| 4879 | } |
| 4880 | } |
| 4881 | if (wasm.flush_buffer.data_exports.get(symbol_name)) |symbol| { |
| 4882 | const data_loc = symbol.resolution.dataLoc(wasm); |
| 4883 | return wasm.flush_buffer.data_segments.get(data_loc.segment).? + data_loc.offset; |
| 4884 | } |
| 4885 | }, |
| 4886 | else => {}, |
| 4887 | } |
| 4888 | // Otherwise it's a zero bit type; any address will do. |
| 4889 | return 0; |
| 4890 | } |
| 4891 | |
| 4892 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4893 | pub fn errorNameTableAddr(wasm: *Wasm) u32 { |
| 4894 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4895 | const comp = wasm.base.comp; |
| 4896 | assert(comp.config.output_mode != .Obj); |
| 4897 | return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?; |
| 4898 | } |
| 4899 | |
| 4900 | pub fn tagIndexTableAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { |
| 4901 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4902 | const comp = wasm.base.comp; |
| 4903 | assert(comp.config.output_mode != .Obj); |
| 4904 | const f = &wasm.flush_buffer; |
| 4905 | const table_base_addr = f.data_segments.get(.__zig_tag_name_table).?; |
| 4906 | return table_base_addr + wasm.tagIndexTableOffset(ip_index); |
| 4907 | } |
| 4908 | |
| 4909 | pub fn tagIndexTableOffset(wasm: *const Wasm, ip_index: InternPool.Index) u32 { |
| 4910 | const table_index = wasm.flush_buffer.enum_tag_name_table.get(ip_index).?; |
| 4911 | return table_index * wasm.pointerSize() * 2; |
| 4912 | } |
| 4913 | |
| 4914 | fn convertZcuFnType( |
| 4915 | comp: *Compilation, |
| 4916 | cc: std.lang.CallingConvention, |
| 4917 | params: []const InternPool.Index, |
| 4918 | return_type: Zcu.Type, |
| 4919 | is_var_args: bool, |
| 4920 | target: *const std.Target, |
| 4921 | params_buffer: *std.ArrayList(std.wasm.Valtype), |
| 4922 | returns_buffer: *std.ArrayList(std.wasm.Valtype), |
| 4923 | ) Allocator.Error!void { |
| 4924 | params_buffer.clearRetainingCapacity(); |
| 4925 | returns_buffer.clearRetainingCapacity(); |
| 4926 | |
| 4927 | const gpa = comp.gpa; |
| 4928 | const zcu = comp.zcu.?; |
| 4929 | |
| 4930 | if (CodeGen.firstParamSRet(cc, return_type, zcu, target)) { |
| 4931 | try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle |
| 4932 | } else if (return_type.hasRuntimeBits(zcu)) { |
| 4933 | if (cc == .wasm_mvp) { |
| 4934 | switch (abi.classifyType(return_type, zcu, target)) { |
| 4935 | .direct => |scalar_type| { |
| 4936 | try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); |
| 4937 | }, |
| 4938 | .double_i64, .indirect => unreachable, |
| 4939 | .unrolled => |vector| { |
| 4940 | assert(vector.len == 1); |
| 4941 | try returns_buffer.append(gpa, CodeGen.typeToValtype(vector.elem_type, zcu, target)); |
| 4942 | }, |
| 4943 | } |
| 4944 | } else { |
| 4945 | try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target)); |
| 4946 | } |
| 4947 | } else if (return_type.isError(zcu)) { |
| 4948 | try returns_buffer.append(gpa, .i32); |
| 4949 | } |
| 4950 | |
| 4951 | // param types |
| 4952 | for (params) |param_type_ip| { |
| 4953 | const param_type = Zcu.Type.fromInterned(param_type_ip); |
| 4954 | if (!param_type.hasRuntimeBits(zcu)) continue; |
| 4955 | |
| 4956 | switch (cc) { |
| 4957 | .wasm_mvp => { |
| 4958 | switch (abi.classifyType(param_type, zcu, target)) { |
| 4959 | .direct => |scalar_type| { |
| 4960 | try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); |
| 4961 | }, |
| 4962 | .double_i64 => { |
| 4963 | try params_buffer.append(gpa, .i64); |
| 4964 | try params_buffer.append(gpa, .i64); |
| 4965 | }, |
| 4966 | .indirect => { |
| 4967 | try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)); |
| 4968 | }, |
| 4969 | .unrolled => |vector| { |
| 4970 | for (0..vector.len) |_| { |
| 4971 | try params_buffer.append(gpa, CodeGen.typeToValtype(vector.elem_type, zcu, target)); |
| 4972 | } |
| 4973 | }, |
| 4974 | } |
| 4975 | }, |
| 4976 | else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), |
| 4977 | } |
| 4978 | } |
| 4979 | |
| 4980 | if (is_var_args) { |
| 4981 | try params_buffer.append(gpa, .i32); |
| 4982 | } |
| 4983 | } |
| 4984 | |
| 4985 | pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool { |
| 4986 | const s = optional_name.slice(wasm) orelse return false; |
| 4987 | return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss."); |
| 4988 | } |
| 4989 | |
| 4990 | /// After this function is called, there may be additional entries in |
| 4991 | /// `Wasm.uavs_obj`, `Wasm.uavs_exe`, `Wasm.navs_obj`, and `Wasm.navs_exe` |
| 4992 | /// which have uninitialized code and relocations. This function is |
| 4993 | /// non-recursive, so callers must coordinate additional calls to populate |
| 4994 | /// those entries. |
| 4995 | fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !ZcuDataObj { |
| 4996 | const code_start: u32 = @intCast(wasm.string_bytes.items.len); |
| 4997 | const relocs_start: u32 = @intCast(wasm.zcu_relocations.len); |
| 4998 | const uav_fixups_start: u32 = @intCast(wasm.uav_fixups.items.len); |
| 4999 | const nav_fixups_start: u32 = @intCast(wasm.nav_fixups.items.len); |
| 5000 | const func_table_fixups_start: u32 = @intCast(wasm.func_table_fixups.items.len); |
| 5001 | wasm.string_bytes_lock.lock(); |
| 5002 | |
| 5003 | { |
| 5004 | var aw: std.Io.Writer.Allocating = .fromArrayList(wasm.base.comp.gpa, &wasm.string_bytes); |
| 5005 | defer wasm.string_bytes = aw.toArrayList(); |
| 5006 | codegen.generateSymbol(&wasm.base, pt, .fromInterned(ip_index), &aw.writer, .none) catch |err| switch (err) { |
| 5007 | error.WriteFailed => return error.OutOfMemory, |
| 5008 | else => |e| return e, |
| 5009 | }; |
| 5010 | } |
| 5011 | |
| 5012 | const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start); |
| 5013 | const relocs_len: u32 = @intCast(wasm.zcu_relocations.len - relocs_start); |
| 5014 | const any_fixups = |
| 5015 | relocs_len != 0 or |
| 5016 | uav_fixups_start != wasm.uav_fixups.items.len or |
| 5017 | nav_fixups_start != wasm.nav_fixups.items.len or |
| 5018 | func_table_fixups_start != wasm.func_table_fixups.items.len; |
| 5019 | wasm.string_bytes_lock.unlock(); |
| 5020 | |
| 5021 | const naive_code: DataPayload = .{ |
| 5022 | .off = @fromBackingInt(@intCast(code_start)), |
| 5023 | .len = code_len, |
| 5024 | }; |
| 5025 | |
| 5026 | // Only nonzero init values need to take up space in the output. |
| 5027 | // If any fixups are present, we still need the string bytes allocated since |
| 5028 | // that is the staging area for the fixups. |
| 5029 | const code: DataPayload = if (!any_fixups and std.mem.allEqual(u8, naive_code.slice(wasm), 0)) c: { |
| 5030 | wasm.string_bytes.shrinkRetainingCapacity(code_start); |
| 5031 | // Indicate empty by making off and len the same value, however, still |
| 5032 | // transmit the data size by using the size as that value. |
| 5033 | break :c .{ |
| 5034 | .off = .none, |
| 5035 | .len = naive_code.len, |
| 5036 | }; |
| 5037 | } else c: { |
| 5038 | wasm.any_passive_inits = wasm.any_passive_inits or wasm.base.comp.config.import_memory; |
| 5039 | break :c naive_code; |
| 5040 | }; |
| 5041 | |
| 5042 | return .{ |
| 5043 | .code = code, |
| 5044 | .relocs = .{ |
| 5045 | .off = relocs_start, |
| 5046 | .len = relocs_len, |
| 5047 | }, |
| 5048 | }; |
| 5049 | } |
| 5050 | |
| 5051 | fn pointerAlignment(wasm: *const Wasm) Alignment { |
| 5052 | const target = &wasm.base.comp.root_mod.resolved_target.result; |
| 5053 | return switch (target.cpu.arch) { |
| 5054 | .wasm32 => .@"4", |
| 5055 | .wasm64 => .@"8", |
| 5056 | else => unreachable, |
| 5057 | }; |
| 5058 | } |
| 5059 | |
| 5060 | fn pointerSize(wasm: *const Wasm) u32 { |
| 5061 | const target = &wasm.base.comp.root_mod.resolved_target.result; |
| 5062 | return switch (target.cpu.arch) { |
| 5063 | .wasm32 => 4, |
| 5064 | .wasm64 => 8, |
| 5065 | else => unreachable, |
| 5066 | }; |
| 5067 | } |
| 5068 | |
| 5069 | fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index, symbol_name: String) ZcuImportIndex { |
| 5070 | const gop = wasm.imports.getOrPutAssumeCapacity(nav_index); |
| 5071 | gop.value_ptr.* = symbol_name; |
| 5072 | return @fromBackingInt(@intCast(gop.index)); |
| 5073 | } |
| 5074 | |
| 5075 | fn resolveFunctionSynthetic( |
| 5076 | wasm: *Wasm, |
| 5077 | import: *FunctionImport, |
| 5078 | res: FunctionImport.Resolution, |
| 5079 | params: []const std.wasm.Valtype, |
| 5080 | returns: []const std.wasm.Valtype, |
| 5081 | ) link.Error!void { |
| 5082 | import.resolution = res; |
| 5083 | wasm.functions.putAssumeCapacity(res, {}); |
| 5084 | // This is not only used for type-checking but also ensures the function |
| 5085 | // type index is interned so that it is guaranteed to exist during `flush`. |
| 5086 | const correct_func_type = try addFuncType(wasm, .{ |
| 5087 | .params = try internValtypeList(wasm, params), |
| 5088 | .returns = try internValtypeList(wasm, returns), |
| 5089 | }); |
| 5090 | if (import.type != correct_func_type) { |
| 5091 | const diags = &wasm.base.comp.link_diags; |
| 5092 | return import.source_location.fail(diags, "synthetic function {s} {f} imported with incorrect signature {f}", .{ |
| 5093 | @tagName(res), correct_func_type.fmt(wasm), import.type.fmt(wasm), |
| 5094 | }); |
| 5095 | } |
| 5096 | } |
| 5097 | |
| 5098 | pub fn addFunction( |
| 5099 | wasm: *Wasm, |
| 5100 | resolution: FunctionImport.Resolution, |
| 5101 | params: []const std.wasm.Valtype, |
| 5102 | returns: []const std.wasm.Valtype, |
| 5103 | ) Allocator.Error!void { |
| 5104 | wasm.functions.putAssumeCapacity(resolution, {}); |
| 5105 | _ = try wasm.addFuncType(.{ |
| 5106 | .params = try wasm.internValtypeList(params), |
| 5107 | .returns = try wasm.internValtypeList(returns), |
| 5108 | }); |
| 5109 | } |