From 9b3c8fd3a8aef81f3a6face78f9e0b34508edc1b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 14 Jan 2024 17:24:18 +0100 Subject: [PATCH] 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. --- src/link/Wasm.zig | 75 +++++++++++++++++++++++++------------ src/link/Wasm/ZigObject.zig | 43 ++++++++++++++++----- 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index cdc019c4fc..d9e1432b63 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1,37 +1,41 @@ const Wasm = @This(); const std = @import("std"); -const builtin = @import("builtin"); -const mem = std.mem; -const Allocator = std.mem.Allocator; + const assert = std.debug.assert; +const build_options = @import("build_options"); +const builtin = @import("builtin"); +const codegen = @import("../codegen.zig"); const fs = std.fs; const leb = std.leb; -const log = std.log.scoped(.link); - -pub const Atom = @import("Wasm/Atom.zig"); -const Dwarf = @import("Dwarf.zig"); -const Module = @import("../Module.zig"); -const InternPool = @import("../InternPool.zig"); -const Compilation = @import("../Compilation.zig"); -const CodeGen = @import("../arch/wasm/CodeGen.zig"); -const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const lldMain = @import("../main.zig").lldMain; +const log = std.log.scoped(.link); +const mem = std.mem; const trace = @import("../tracy.zig").trace; -const build_options = @import("build_options"); -const wasi_libc = @import("../wasi_libc.zig"); -const Cache = std.Build.Cache; -const Type = @import("../type.zig").Type; -const Value = @import("../Value.zig"); -const TypedValue = @import("../TypedValue.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; -const Air = @import("../Air.zig"); -const Liveness = @import("../Liveness.zig"); -const Symbol = @import("Wasm/Symbol.zig"); -const Object = @import("Wasm/Object.zig"); -const Archive = @import("Wasm/Archive.zig"); const types = @import("Wasm/types.zig"); +const wasi_libc = @import("../wasi_libc.zig"); + +const Air = @import("../Air.zig"); +const Allocator = std.mem.Allocator; +const Archive = @import("Wasm/Archive.zig"); +const Cache = std.Build.Cache; +const CodeGen = @import("../arch/wasm/CodeGen.zig"); +const Compilation = @import("../Compilation.zig"); +const Dwarf = @import("Dwarf.zig"); +const File = @import("Wasm/file.zig").File; +const InternPool = @import("../InternPool.zig"); +const Liveness = @import("../Liveness.zig"); +const LlvmObject = @import("../codegen/llvm.zig").Object; +const Module = @import("../Module.zig"); +const Object = @import("Wasm/Object.zig"); +const Symbol = @import("Wasm/Symbol.zig"); +const Type = @import("../type.zig").Type; +const TypedValue = @import("../TypedValue.zig"); +const Value = @import("../value.zig").Value; +const ZigObject = @import("Wasm/ZigObject.zig"); + +pub const Atom = @import("Wasm/Atom.zig"); pub const Relocation = types.Relocation; pub const base_tag: link.File.Tag = .wasm; @@ -57,6 +61,11 @@ export_table: bool, name: []const u8, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. llvm_object: ?*LlvmObject = null, +/// The file index of a `ZigObject`. This will only contain a valid index when a zcu exists, +/// and the chosen backend is the Wasm backend. +zig_object_index: File.Index = .null, +/// List of relocatable files to be linked into the final binary. +files: std.MultiArrayList(File.Entry) = .{}, /// When importing objects from the host environment, a name must be supplied. /// LLVM uses "env" by default when none is given. This would be a good default for Zig /// to support existing code. @@ -556,9 +565,27 @@ pub fn createEmpty( } } + if (comp.module) |zcu| { + if (!use_llvm) { + const index: File.Index = @enumFromInt(wasm.files.len); + var zig_object: ZigObject = .{ + .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}), + .stack_pointer_sym = undefined, + }; + try zig_object.init(wasm); + try wasm.files.append(gpa, .{ .zig_object = zig_object }); + wasm.zig_object_index = index; + } + } + return wasm; } +fn zigObjectPtr(wasm: *Wasm) ?*ZigObject { + if (wasm.zig_object_index == .null) return null; + return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object; +} + /// For a given name, creates a new global synthetic symbol. /// Leaves index undefined and the default flags (0). fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc { diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index 505d73a630..4f32f6891e 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/src/link/Wasm/ZigObject.zig @@ -3,6 +3,7 @@ //! and any relocations that may have been emitted. //! Think about this as fake in-memory Object file for the Zig module. +path: []const u8, /// List of all `Decl` that are currently alive. /// Each index maps to the corresponding `Atom.Index`. decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{}, @@ -22,7 +23,7 @@ global_syms: std.AutoHashMapUnmanaged(u32, u32) = .{}, /// List of symbol indexes which are free to be used. symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, /// Extra metadata about the linking section, such as alignment of segments and their name. -segment_info: std.ArrayListUnmanage(types.Segment) = &.{}, +segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, /// File encapsulated string table, used to deduplicate strings within the generated file. string_table: StringTable = .{}, /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index. @@ -72,6 +73,30 @@ debug_str_index: ?u32 = null, /// The index of the segment representing the custom '.debug_pubtypes' section. debug_abbrev_index: ?u32 = null, +/// Initializes the `ZigObject` with initial symbols. +pub fn init(zig_object: *ZigObject, wasm_file: *Wasm) !void { + // Initialize an undefined global with the name __stack_pointer. Codegen will use + // this to generate relocations when moving the stack pointer. This symbol will be + // resolved automatically by the final linking stage. + try zig_object.createStackPointer(wasm_file); + + // TODO: Initialize debug information when we reimplement Dwarf support. +} + +fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void { + const gpa = wasm_file.base.comp.gpa; + const sym_index = try zig_object.getGlobalSymbol(gpa, "__stack_pointer", .global); + zig_object.symbols.items[sym_index].index = zig_object.imported_globals_count; + const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32; + try zig_object.imports.putNoClobber(gpa, sym_index, .{ + .name = zig_object.symbols.items[sym_index].name, + .module_name = try zig_object.string_table.insert(gpa, wasm_file.host_name), + .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } }, + }); + zig_object.imported_globals_count += 1; + zig_object.stack_pointer_sym = sym_index; +} + /// Frees and invalidates all memory of the incrementally compiled Zig module. /// It is illegal behavior to access the `ZigObject` after calling `deinit`. pub 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 { if (zig_object.dwarf) |*dwarf| { dwarf.deinit(); } + gpa.free(zig_object.path); zig_object.* = undefined; } @@ -531,32 +557,31 @@ pub fn addOrUpdateImport( /// such as an exported or imported symbol. /// If the symbol does not yet exist, creates a new one symbol instead /// and then returns the index to it. -pub fn getGlobalSymbol(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8) !u32 { - const gpa = wasm_file.base.comp.gpa; +pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8, tag: Symbol.Tag) !u32 { const name_index = try zig_object.string_table.insert(gpa, name); const gop = try zig_object.global_syms.getOrPut(gpa, name_index); if (gop.found_existing) { - return gop.value_ptr.index; + return gop.value_ptr.*; } var symbol: Symbol = .{ .name = name_index, .flags = 0, - .index = undefined, // index to type will be set after merging function symbols - .tag = .function, - .virtual_address = undefined, + .index = undefined, // index to type will be set after merging symbols + .tag = tag, + .virtual_address = std.math.maxInt(u32), }; symbol.setGlobal(true); symbol.setUndefined(true); - const sym_index = if (zig_object.symbol.popOrNull()) |index| index else blk: { + const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: { const index: u32 = @intCast(zig_object.symbols.items.len); try zig_object.symbols.ensureUnusedCapacity(gpa, 1); zig_object.symbols.items.len += 1; break :blk index; }; zig_object.symbols.items[sym_index] = symbol; - gop.value_ptr.* = .{ .index = sym_index, .file = null }; + gop.value_ptr.* = sym_index; return sym_index; }