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