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.
This commit is contained in:
Luuk de Gram
2024-01-14 17:24:18 +01:00
parent e54177e852
commit 9b3c8fd3a8
2 changed files with 85 additions and 33 deletions

View File

@@ -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 {

View File

@@ -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;
}