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