authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-01-14 17:24:18+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-29 15:23:02+01:00
log9b3c8fd3a8aef81f3a6face78f9e0b34508edc1b
tree7026f5e6baf349298971e6dd3aa68cf0d8edb452
parente54177e852e5674ff14a850586b7517697e52297
signaturelock-open Commit is signed but in an unrecognized format.

wasm: initialize a `ZigObject` when required

When we have a ZigCompileUnit and don't use LLVM, we initialize the ZigObject which will encapsulate the Zig Module as an object file in- memory. During initialization we also create symbols which the object will need such as the stack pointer.

2 files changed, 82 insertions(+), 30 deletions(-)

src/link/Wasm.zig+48-21
......@@ -1,37 +1,41 @@
11const Wasm = @This();
22
33const std = @import("std");
4const builtin = @import("builtin");
5const mem = std.mem;
6const Allocator = std.mem.Allocator;
4
75const assert = std.debug.assert;
6const build_options = @import("build_options");
7const builtin = @import("builtin");
8const codegen = @import("../codegen.zig");
89const fs = std.fs;
910const leb = std.leb;
10const log = std.log.scoped(.link);
11
12pub const Atom = @import("Wasm/Atom.zig");
13const Dwarf = @import("Dwarf.zig");
14const Module = @import("../Module.zig");
15const InternPool = @import("../InternPool.zig");
16const Compilation = @import("../Compilation.zig");
17const CodeGen = @import("../arch/wasm/CodeGen.zig");
18const codegen = @import("../codegen.zig");
1911const link = @import("../link.zig");
2012const lldMain = @import("../main.zig").lldMain;
13const log = std.log.scoped(.link);
14const mem = std.mem;
2115const trace = @import("../tracy.zig").trace;
22const build_options = @import("build_options");
16const types = @import("Wasm/types.zig");
2317const wasi_libc = @import("../wasi_libc.zig");
24const Cache = std.Build.Cache;
25const Type = @import("../type.zig").Type;
26const Value = @import("../Value.zig");
27const TypedValue = @import("../TypedValue.zig");
28const LlvmObject = @import("../codegen/llvm.zig").Object;
18
2919const Air = @import("../Air.zig");
20const Allocator = std.mem.Allocator;
21const Archive = @import("Wasm/Archive.zig");
22const Cache = std.Build.Cache;
23const CodeGen = @import("../arch/wasm/CodeGen.zig");
24const Compilation = @import("../Compilation.zig");
25const Dwarf = @import("Dwarf.zig");
26const File = @import("Wasm/file.zig").File;
27const InternPool = @import("../InternPool.zig");
3028const Liveness = @import("../Liveness.zig");
31const Symbol = @import("Wasm/Symbol.zig");
29const LlvmObject = @import("../codegen/llvm.zig").Object;
30const Module = @import("../Module.zig");
3231const Object = @import("Wasm/Object.zig");
33const Archive = @import("Wasm/Archive.zig");
34const types = @import("Wasm/types.zig");
32const Symbol = @import("Wasm/Symbol.zig");
33const Type = @import("../type.zig").Type;
34const TypedValue = @import("../TypedValue.zig");
35const Value = @import("../value.zig").Value;
36const ZigObject = @import("Wasm/ZigObject.zig");
37
38pub const Atom = @import("Wasm/Atom.zig");
3539pub const Relocation = types.Relocation;
3640
3741pub const base_tag: link.File.Tag = .wasm;
......@@ -57,6 +61,11 @@ export_table: bool,
5761name: []const u8,
5862/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
5963llvm_object: ?*LlvmObject = null,
64/// The file index of a `ZigObject`. This will only contain a valid index when a zcu exists,
65/// and the chosen backend is the Wasm backend.
66zig_object_index: File.Index = .null,
67/// List of relocatable files to be linked into the final binary.
68files: std.MultiArrayList(File.Entry) = .{},
6069/// When importing objects from the host environment, a name must be supplied.
6170/// LLVM uses "env" by default when none is given. This would be a good default for Zig
6271/// to support existing code.
......@@ -556,9 +565,27 @@ pub fn createEmpty(
556565 }
557566 }
558567
568 if (comp.module) |zcu| {
569 if (!use_llvm) {
570 const index: File.Index = @enumFromInt(wasm.files.len);
571 var zig_object: ZigObject = .{
572 .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}),
573 .stack_pointer_sym = undefined,
574 };
575 try zig_object.init(wasm);
576 try wasm.files.append(gpa, .{ .zig_object = zig_object });
577 wasm.zig_object_index = index;
578 }
579 }
580
559581 return wasm;
560582}
561583
584fn zigObjectPtr(wasm: *Wasm) ?*ZigObject {
585 if (wasm.zig_object_index == .null) return null;
586 return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object;
587}
588
562589/// For a given name, creates a new global synthetic symbol.
563590/// Leaves index undefined and the default flags (0).
564591fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc {
src/link/Wasm/ZigObject.zig+34-9
......@@ -3,6 +3,7 @@
33//! and any relocations that may have been emitted.
44//! Think about this as fake in-memory Object file for the Zig module.
55
6path: []const u8,
67/// List of all `Decl` that are currently alive.
78/// Each index maps to the corresponding `Atom.Index`.
89decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{},
......@@ -22,7 +23,7 @@ global_syms: std.AutoHashMapUnmanaged(u32, u32) = .{},
2223/// List of symbol indexes which are free to be used.
2324symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
2425/// Extra metadata about the linking section, such as alignment of segments and their name.
25segment_info: std.ArrayListUnmanage(types.Segment) = &.{},
26segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
2627/// File encapsulated string table, used to deduplicate strings within the generated file.
2728string_table: StringTable = .{},
2829/// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
......@@ -72,6 +73,30 @@ debug_str_index: ?u32 = null,
7273/// The index of the segment representing the custom '.debug_pubtypes' section.
7374debug_abbrev_index: ?u32 = null,
7475
76/// Initializes the `ZigObject` with initial symbols.
77pub fn init(zig_object: *ZigObject, wasm_file: *Wasm) !void {
78 // Initialize an undefined global with the name __stack_pointer. Codegen will use
79 // this to generate relocations when moving the stack pointer. This symbol will be
80 // resolved automatically by the final linking stage.
81 try zig_object.createStackPointer(wasm_file);
82
83 // TODO: Initialize debug information when we reimplement Dwarf support.
84}
85
86fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void {
87 const gpa = wasm_file.base.comp.gpa;
88 const sym_index = try zig_object.getGlobalSymbol(gpa, "__stack_pointer", .global);
89 zig_object.symbols.items[sym_index].index = zig_object.imported_globals_count;
90 const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
91 try zig_object.imports.putNoClobber(gpa, sym_index, .{
92 .name = zig_object.symbols.items[sym_index].name,
93 .module_name = try zig_object.string_table.insert(gpa, wasm_file.host_name),
94 .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } },
95 });
96 zig_object.imported_globals_count += 1;
97 zig_object.stack_pointer_sym = sym_index;
98}
99
75100/// Frees and invalidates all memory of the incrementally compiled Zig module.
76101/// It is illegal behavior to access the `ZigObject` after calling `deinit`.
77102pub fn deinit(zig_object: *ZigObject, gpa: std.mem.Allocator) void {
......@@ -113,6 +138,7 @@ pub fn deinit(zig_object: *ZigObject, gpa: std.mem.Allocator) void {
113138 if (zig_object.dwarf) |*dwarf| {
114139 dwarf.deinit();
115140 }
141 gpa.free(zig_object.path);
116142 zig_object.* = undefined;
117143}
118144
......@@ -531,32 +557,31 @@ pub fn addOrUpdateImport(
531557/// such as an exported or imported symbol.
532558/// If the symbol does not yet exist, creates a new one symbol instead
533559/// and then returns the index to it.
534pub fn getGlobalSymbol(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8) !u32 {
535 const gpa = wasm_file.base.comp.gpa;
560pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8, tag: Symbol.Tag) !u32 {
536561 const name_index = try zig_object.string_table.insert(gpa, name);
537562 const gop = try zig_object.global_syms.getOrPut(gpa, name_index);
538563 if (gop.found_existing) {
539 return gop.value_ptr.index;
564 return gop.value_ptr.*;
540565 }
541566
542567 var symbol: Symbol = .{
543568 .name = name_index,
544569 .flags = 0,
545 .index = undefined, // index to type will be set after merging function symbols
546 .tag = .function,
547 .virtual_address = undefined,
570 .index = undefined, // index to type will be set after merging symbols
571 .tag = tag,
572 .virtual_address = std.math.maxInt(u32),
548573 };
549574 symbol.setGlobal(true);
550575 symbol.setUndefined(true);
551576
552 const sym_index = if (zig_object.symbol.popOrNull()) |index| index else blk: {
577 const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: {
553578 const index: u32 = @intCast(zig_object.symbols.items.len);
554579 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
555580 zig_object.symbols.items.len += 1;
556581 break :blk index;
557582 };
558583 zig_object.symbols.items[sym_index] = symbol;
559 gop.value_ptr.* = .{ .index = sym_index, .file = null };
584 gop.value_ptr.* = sym_index;
560585 return sym_index;
561586}
562587