Rather than using the optional, we now directly use `File.Index` which can already represent an unknown file due to its `.null` value. This means we do not pay for the memory cost. This type of index is now used for: - SymbolLoc - Key of the functions map - InitFunc Now we can simply pass things like atom.file, object.file, loc.file etc whenever we need to access its representing object file which makes it a lot easier.
1214 lines
50 KiB
Zig
1214 lines
50 KiB
Zig
//! ZigObject encapsulates the state of the incrementally compiled Zig module.
|
|
//! It stores the associated input local and global symbols, allocated atoms,
|
|
//! and any relocations that may have been emitted.
|
|
//! Think about this as fake in-memory Object file for the Zig module.
|
|
|
|
path: []const u8,
|
|
/// Index within the list of relocatable objects of the linker driver.
|
|
index: File.Index,
|
|
/// List of all `Decl` that are currently alive.
|
|
/// Each index maps to the corresponding `Atom.Index`.
|
|
decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{},
|
|
/// List of function type signatures for this Zig module.
|
|
func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
|
|
/// List of `std.wasm.Func`. Each entry contains the function signature,
|
|
/// rather than the actual body.
|
|
functions: std.ArrayListUnmanaged(std.wasm.Func) = .{},
|
|
/// Map of symbol locations, represented by its `types.Import`.
|
|
imports: std.AutoHashMapUnmanaged(u32, types.Import) = .{},
|
|
/// List of WebAssembly globals.
|
|
globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
|
|
/// Mapping between an `Atom` and its type index representing the Wasm
|
|
/// type of the function signature.
|
|
atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
|
|
/// List of all symbols generated by Zig code.
|
|
symbols: std.ArrayListUnmanaged(Symbol) = .{},
|
|
/// Map from symbol name offset to their index into the `symbols` list.
|
|
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.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.
|
|
anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{},
|
|
/// Represents the symbol index of the error name table
|
|
/// When this is `null`, no code references an error using runtime `@errorName`.
|
|
/// During initializion, a symbol with corresponding atom will be created that is
|
|
/// used to perform relocations to the pointer of this table.
|
|
/// The actual table is populated during `flush`.
|
|
error_table_symbol: ?u32 = null,
|
|
/// Amount of functions in the `import` sections.
|
|
imported_functions_count: u32 = 0,
|
|
/// Amount of globals in the `import` section.
|
|
imported_globals_count: u32 = 0,
|
|
/// Symbol index representing the stack pointer. This will be set upon initializion
|
|
/// of a new `ZigObject`. Codegen will make calls into this to create relocations for
|
|
/// this symbol each time the stack pointer is moved.
|
|
stack_pointer_sym: u32,
|
|
/// Debug information for the Zig module.
|
|
dwarf: ?Dwarf = null,
|
|
// Debug section atoms. These are only set when the current compilation
|
|
// unit contains Zig code. The lifetime of these atoms are extended
|
|
// until the end of the compiler's lifetime. Meaning they're not freed
|
|
// during `flush()` in incremental-mode.
|
|
debug_info_atom: ?Atom.Index = null,
|
|
debug_line_atom: ?Atom.Index = null,
|
|
debug_loc_atom: ?Atom.Index = null,
|
|
debug_ranges_atom: ?Atom.Index = null,
|
|
debug_abbrev_atom: ?Atom.Index = null,
|
|
debug_str_atom: ?Atom.Index = null,
|
|
debug_pubnames_atom: ?Atom.Index = null,
|
|
debug_pubtypes_atom: ?Atom.Index = null,
|
|
/// The index of the segment representing the custom '.debug_info' section.
|
|
debug_info_index: ?u32 = null,
|
|
/// The index of the segment representing the custom '.debug_line' section.
|
|
debug_line_index: ?u32 = null,
|
|
/// The index of the segment representing the custom '.debug_loc' section.
|
|
debug_loc_index: ?u32 = null,
|
|
/// The index of the segment representing the custom '.debug_ranges' section.
|
|
debug_ranges_index: ?u32 = null,
|
|
/// The index of the segment representing the custom '.debug_pubnames' section.
|
|
debug_pubnames_index: ?u32 = null,
|
|
/// The index of the segment representing the custom '.debug_pubtypes' section.
|
|
debug_pubtypes_index: ?u32 = null,
|
|
/// The index of the segment representing the custom '.debug_pubtypes' section.
|
|
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");
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.index = zig_object.imported_globals_count;
|
|
sym.tag = .global;
|
|
const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
|
|
try zig_object.imports.putNoClobber(gpa, sym_index, .{
|
|
.name = sym.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;
|
|
}
|
|
|
|
fn symbol(zig_object: *const ZigObject, index: u32) *Symbol {
|
|
return &zig_object.symbols.items[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, wasm_file: *Wasm) void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
for (zig_object.segment_info.items) |segment_info| {
|
|
gpa.free(segment_info.name);
|
|
}
|
|
|
|
// For decls and anon decls we free the memory of its atoms.
|
|
// The memory of atoms parsed from object files is managed by
|
|
// the object file itself, and therefore we can skip those.
|
|
{
|
|
var it = zig_object.decls.valueIterator();
|
|
while (it.next()) |atom_index_ptr| {
|
|
const atom = wasm_file.getAtomPtr(atom_index_ptr.*);
|
|
for (atom.locals.items) |local_index| {
|
|
const local_atom = wasm_file.getAtomPtr(local_index);
|
|
local_atom.deinit(gpa);
|
|
}
|
|
atom.deinit(gpa);
|
|
}
|
|
}
|
|
{
|
|
for (zig_object.anon_decls.values()) |atom_index| {
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
for (atom.locals.items) |local_index| {
|
|
const local_atom = wasm_file.getAtomPtr(local_index);
|
|
local_atom.deinit(gpa);
|
|
}
|
|
atom.deinit(gpa);
|
|
}
|
|
}
|
|
zig_object.decls.deinit(gpa);
|
|
zig_object.anon_decls.deinit(gpa);
|
|
zig_object.symbols.deinit(gpa);
|
|
zig_object.symbols_free_list.deinit(gpa);
|
|
zig_object.segment_info.deinit(gpa);
|
|
|
|
zig_object.string_table.deinit(gpa);
|
|
if (zig_object.dwarf) |*dwarf| {
|
|
dwarf.deinit();
|
|
}
|
|
gpa.free(zig_object.path);
|
|
zig_object.* = undefined;
|
|
}
|
|
|
|
/// Allocates a new symbol and returns its index.
|
|
/// Will re-use slots when a symbol was freed at an earlier stage.
|
|
pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !u32 {
|
|
try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
|
|
const sym: Symbol = .{
|
|
.name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
.tag = .undefined, // will be set after updateDecl
|
|
.index = std.math.maxInt(u32), // will be set during atom parsing
|
|
.virtual_address = std.math.maxInt(u32), // will be set during atom allocation
|
|
};
|
|
if (zig_object.symbols_free_list.popOrNull()) |index| {
|
|
zig_object.symbols.items[index] = sym;
|
|
return index;
|
|
}
|
|
const index = @as(u32, @intCast(zig_object.symbols.items.len));
|
|
zig_object.symbols.appendAssumeCapacity(sym);
|
|
return index;
|
|
}
|
|
|
|
// Generate code for the Decl, storing it in memory to be later written to
|
|
// the file on flush().
|
|
pub fn updateDecl(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
mod: *Module,
|
|
decl_index: InternPool.DeclIndex,
|
|
) !void {
|
|
const decl = mod.declPtr(decl_index);
|
|
if (decl.val.getFunction(mod)) |_| {
|
|
return;
|
|
} else if (decl.val.getExternFunc(mod)) |_| {
|
|
return;
|
|
}
|
|
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.clear();
|
|
|
|
if (decl.isExtern(mod)) {
|
|
const variable = decl.getOwnedVariable(mod).?;
|
|
const name = mod.intern_pool.stringToSlice(decl.name);
|
|
const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
|
|
return zig_object.addOrUpdateImport(wasm_file, name, atom.sym_index, lib_name, null);
|
|
}
|
|
const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val;
|
|
|
|
var code_writer = std.ArrayList(u8).init(gpa);
|
|
defer code_writer.deinit();
|
|
|
|
const res = try codegen.generateSymbol(
|
|
&wasm_file.base,
|
|
decl.srcLoc(mod),
|
|
.{ .ty = decl.ty, .val = val },
|
|
&code_writer,
|
|
.none,
|
|
.{ .parent_atom_index = atom.sym_index },
|
|
);
|
|
|
|
const code = switch (res) {
|
|
.ok => code_writer.items,
|
|
.fail => |em| {
|
|
decl.analysis = .codegen_failure;
|
|
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
|
return;
|
|
},
|
|
};
|
|
|
|
return zig_object.finishUpdateDecl(wasm_file, decl_index, code, .data);
|
|
}
|
|
|
|
pub fn updateFunc(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
mod: *Module,
|
|
func_index: InternPool.Index,
|
|
air: Air,
|
|
liveness: Liveness,
|
|
) !void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const func = mod.funcInfo(func_index);
|
|
const decl_index = func.owner_decl;
|
|
const decl = mod.declPtr(decl_index);
|
|
const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.clear();
|
|
|
|
var code_writer = std.ArrayList(u8).init(gpa);
|
|
defer code_writer.deinit();
|
|
const result = try codegen.generateFunction(
|
|
&wasm_file.base,
|
|
decl.srcLoc(mod),
|
|
func_index,
|
|
air,
|
|
liveness,
|
|
&code_writer,
|
|
.none,
|
|
);
|
|
|
|
const code = switch (result) {
|
|
.ok => code_writer.items,
|
|
.fail => |em| {
|
|
decl.analysis = .codegen_failure;
|
|
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
|
return;
|
|
},
|
|
};
|
|
|
|
return zig_object.finishUpdateDecl(wasm_file, decl_index, code, .function);
|
|
}
|
|
|
|
fn finishUpdateDecl(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
decl_index: InternPool.DeclIndex,
|
|
code: []const u8,
|
|
symbol_tag: Symbol.Tag,
|
|
) !void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const decl = mod.declPtr(decl_index);
|
|
const atom_index = zig_object.decls.get(decl_index).?;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
const sym = zig_object.symbol(atom.getSymbolIndex().?);
|
|
const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
|
sym.name = try zig_object.string_table.insert(gpa, full_name);
|
|
sym.tag = symbol_tag;
|
|
try atom.code.appendSlice(gpa, code);
|
|
try wasm_file.resolved_symbols.put(gpa, atom.symbolLoc(), {});
|
|
|
|
atom.size = @intCast(code.len);
|
|
if (code.len == 0) return;
|
|
atom.alignment = decl.getAlignment(mod);
|
|
}
|
|
|
|
/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
|
|
/// When the index was not found, a new `Atom` will be created, and its index will be returned.
|
|
/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
|
|
pub fn getOrCreateAtomForDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const gop = try zig_object.decls.getOrPut(gpa, decl_index);
|
|
if (!gop.found_existing) {
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
gop.value_ptr.* = try wasm_file.createAtom(sym_index, zig_object.index);
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const decl = mod.declPtr(decl_index);
|
|
const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.name = try zig_object.string_table.insert(gpa, full_name);
|
|
}
|
|
return gop.value_ptr.*;
|
|
}
|
|
|
|
pub fn lowerAnonDecl(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
decl_val: InternPool.Index,
|
|
explicit_alignment: InternPool.Alignment,
|
|
src_loc: Module.SrcLoc,
|
|
) !codegen.Result {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const gop = try zig_object.anon_decls.getOrPut(gpa, decl_val);
|
|
if (!gop.found_existing) {
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
|
|
const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) };
|
|
var name_buf: [32]u8 = undefined;
|
|
const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
|
|
@intFromEnum(decl_val),
|
|
}) catch unreachable;
|
|
|
|
switch (try zig_object.lowerConst(wasm_file, name, tv, src_loc)) {
|
|
.ok => |atom_index| zig_object.anon_decls.values()[gop.index] = atom_index,
|
|
.fail => |em| return .{ .fail = em },
|
|
}
|
|
}
|
|
|
|
const atom = wasm_file.getAtomPtr(zig_object.anon_decls.values()[gop.index]);
|
|
atom.alignment = switch (atom.alignment) {
|
|
.none => explicit_alignment,
|
|
else => switch (explicit_alignment) {
|
|
.none => atom.alignment,
|
|
else => atom.alignment.maxStrict(explicit_alignment),
|
|
},
|
|
};
|
|
return .ok;
|
|
}
|
|
|
|
/// Lowers a constant typed value to a local symbol and atom.
|
|
/// Returns the symbol index of the local
|
|
/// The given `decl` is the parent decl whom owns the constant.
|
|
pub fn lowerUnnamedConst(zig_object: *ZigObject, wasm_file: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
std.debug.assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions
|
|
const decl = mod.declPtr(decl_index);
|
|
|
|
const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
|
|
const parent_atom = wasm_file.getAtom(parent_atom_index);
|
|
const local_index = parent_atom.locals.items.len;
|
|
const fqn = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
|
const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{
|
|
fqn, local_index,
|
|
});
|
|
defer gpa.free(name);
|
|
|
|
switch (try zig_object.lowerConst(wasm_file, name, tv, decl.srcLoc(mod))) {
|
|
.ok => |atom_index| {
|
|
try wasm_file.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index);
|
|
return wasm_file.getAtom(atom_index).getSymbolIndex().?;
|
|
},
|
|
.fail => |em| {
|
|
decl.analysis = .codegen_failure;
|
|
try mod.failed_decls.put(mod.gpa, decl_index, em);
|
|
return error.CodegenFail;
|
|
},
|
|
}
|
|
}
|
|
|
|
const LowerConstResult = union(enum) {
|
|
ok: Atom.Index,
|
|
fail: *Module.ErrorMsg,
|
|
};
|
|
|
|
fn lowerConst(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
|
|
// Create and initialize a new local symbol and atom
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
|
|
var value_bytes = std.ArrayList(u8).init(gpa);
|
|
defer value_bytes.deinit();
|
|
|
|
const code = code: {
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.alignment = tv.ty.abiAlignment(mod);
|
|
zig_object.symbols.items[sym_index] = .{
|
|
.name = try zig_object.string_table.insert(gpa, name),
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
.tag = .data,
|
|
.index = undefined,
|
|
.virtual_address = undefined,
|
|
};
|
|
|
|
const result = try codegen.generateSymbol(
|
|
&wasm_file.base,
|
|
src_loc,
|
|
tv,
|
|
&value_bytes,
|
|
.none,
|
|
.{
|
|
.parent_atom_index = atom.sym_index,
|
|
.addend = null,
|
|
},
|
|
);
|
|
break :code switch (result) {
|
|
.ok => value_bytes.items,
|
|
.fail => |em| {
|
|
return .{ .fail = em };
|
|
},
|
|
};
|
|
};
|
|
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.size = @intCast(code.len);
|
|
try atom.code.appendSlice(gpa, code);
|
|
return .{ .ok = atom_index };
|
|
}
|
|
|
|
/// Returns the symbol index of the error name table.
|
|
///
|
|
/// When the symbol does not yet exist, it will create a new one instead.
|
|
pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !u32 {
|
|
if (zig_object.error_table_symbol) |sym| {
|
|
return sym;
|
|
}
|
|
|
|
// no error was referenced yet, so create a new symbol and atom for it
|
|
// and then return said symbol's index. The final table will be populated
|
|
// during `flush` when we know all possible error names.
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
const slice_ty = Type.slice_const_u8_sentinel_0;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
atom.alignment = slice_ty.abiAlignment(mod);
|
|
|
|
const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_name_table");
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.* = .{
|
|
.name = sym_name,
|
|
.tag = .data,
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
.index = 0,
|
|
.virtual_address = undefined,
|
|
};
|
|
// TODO: can we remove this?
|
|
// sym.mark();
|
|
|
|
log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
|
|
zig_object.error_table_symbol = sym_index;
|
|
return sym_index;
|
|
}
|
|
|
|
/// Populates the error name table, when `error_table_symbol` is not null.
|
|
///
|
|
/// This creates a table that consists of pointers and length to each error name.
|
|
/// The table is what is being pointed to within the runtime bodies that are generated.
|
|
fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void {
|
|
const symbol_index = zig_object.error_table_symbol orelse return;
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = symbol_index }).?;
|
|
|
|
// Rather than creating a symbol for each individual error name,
|
|
// we create a symbol for the entire region of error names. We then calculate
|
|
// the pointers into the list using addends which are appended to the relocation.
|
|
const names_sym_index = try zig_object.allocateSymbol(gpa);
|
|
const names_atom_index = try wasm_file.createAtom(names_sym_index, zig_object.index);
|
|
const names_atom = wasm_file.getAtomPtr(names_atom_index);
|
|
names_atom.alignment = .@"1";
|
|
const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_names");
|
|
const names_symbol = &zig_object.symbols.items[names_sym_index];
|
|
names_symbol.* = .{
|
|
.name = sym_name,
|
|
.tag = .data,
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
.index = 0,
|
|
.virtual_address = undefined,
|
|
};
|
|
names_symbol.mark();
|
|
|
|
log.debug("Populating error names", .{});
|
|
|
|
// Addend for each relocation to the table
|
|
var addend: u32 = 0;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
for (mod.global_error_set.keys()) |error_name_nts| {
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
|
|
const error_name = mod.intern_pool.stringToSlice(error_name_nts);
|
|
const len = @as(u32, @intCast(error_name.len + 1)); // names are 0-termianted
|
|
|
|
const slice_ty = Type.slice_const_u8_sentinel_0;
|
|
const offset = @as(u32, @intCast(atom.code.items.len));
|
|
// first we create the data for the slice of the name
|
|
try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated
|
|
try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
|
|
// create relocation to the error name
|
|
try atom.relocs.append(gpa, .{
|
|
.index = names_atom.sym_index,
|
|
.relocation_type = .R_WASM_MEMORY_ADDR_I32,
|
|
.offset = offset,
|
|
.addend = @as(i32, @intCast(addend)),
|
|
});
|
|
atom.size += @as(u32, @intCast(slice_ty.abiSize(mod)));
|
|
addend += len;
|
|
|
|
// as we updated the error name table, we now store the actual name within the names atom
|
|
try names_atom.code.ensureUnusedCapacity(gpa, len);
|
|
names_atom.code.appendSliceAssumeCapacity(error_name);
|
|
names_atom.code.appendAssumeCapacity(0);
|
|
|
|
log.debug("Populated error name: '{s}'", .{error_name});
|
|
}
|
|
names_atom.size = addend;
|
|
|
|
// link the atoms with the rest of the binary so they can be allocated
|
|
// and relocations will be performed.
|
|
try wasm_file.parseAtom(atom_index, .{ .data = .read_only });
|
|
try wasm_file.parseAtom(names_atom_index, .{ .data = .read_only });
|
|
}
|
|
|
|
/// Either creates a new import, or updates one if existing.
|
|
/// When `type_index` is non-null, we assume an external function.
|
|
/// In all other cases, a data-symbol will be created instead.
|
|
pub fn addOrUpdateImport(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
/// Name of the import
|
|
name: []const u8,
|
|
/// Symbol index that is external
|
|
symbol_index: u32,
|
|
/// Optional library name (i.e. `extern "c" fn foo() void`
|
|
lib_name: ?[:0]const u8,
|
|
/// The index of the type that represents the function signature
|
|
/// when the extern is a function. When this is null, a data-symbol
|
|
/// is asserted instead.
|
|
type_index: ?u32,
|
|
) !void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
std.debug.assert(symbol_index != 0);
|
|
// For the import name, we use the decl's name, rather than the fully qualified name
|
|
// Also mangle the name when the lib name is set and not equal to "C" so imports with the same
|
|
// name but different module can be resolved correctly.
|
|
const mangle_name = lib_name != null and
|
|
!std.mem.eql(u8, lib_name.?, "c");
|
|
const full_name = if (mangle_name) full_name: {
|
|
break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? });
|
|
} else name;
|
|
defer if (mangle_name) gpa.free(full_name);
|
|
|
|
const decl_name_index = try zig_object.string_table.insert(gpa, full_name);
|
|
const sym: *Symbol = &zig_object.symbols.items[symbol_index];
|
|
sym.setUndefined(true);
|
|
sym.setGlobal(true);
|
|
sym.name = decl_name_index;
|
|
if (mangle_name) {
|
|
// we specified a specific name for the symbol that does not match the import name
|
|
sym.setFlag(.WASM_SYM_EXPLICIT_NAME);
|
|
}
|
|
|
|
if (type_index) |ty_index| {
|
|
const gop = try zig_object.imports.getOrPut(gpa, symbol_index);
|
|
const module_name = if (lib_name) |l_name| blk: {
|
|
break :blk l_name;
|
|
} else wasm_file.host_name;
|
|
if (!gop.found_existing) {
|
|
gop.value_ptr.* = .{
|
|
.module_name = try zig_object.string_table.insert(gpa, module_name),
|
|
.name = try zig_object.string_table.insert(gpa, name),
|
|
.kind = .{ .function = ty_index },
|
|
};
|
|
zig_object.imported_functions_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns the symbol index from a symbol of which its flag is set global,
|
|
/// 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, gpa: std.mem.Allocator, name: []const u8) !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.*;
|
|
}
|
|
|
|
var sym: Symbol = .{
|
|
.name = name_index,
|
|
.flags = 0,
|
|
.index = undefined, // index to type will be set after merging symbols
|
|
.tag = .function,
|
|
.virtual_address = std.math.maxInt(u32),
|
|
};
|
|
sym.setGlobal(true);
|
|
sym.setUndefined(true);
|
|
|
|
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] = sym;
|
|
gop.value_ptr.* = sym_index;
|
|
return sym_index;
|
|
}
|
|
|
|
/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
|
|
/// Returns the given pointer address
|
|
pub fn getDeclVAddr(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
decl_index: InternPool.DeclIndex,
|
|
reloc_info: link.File.RelocInfo,
|
|
) !u64 {
|
|
const target = wasm_file.base.comp.root_mod.resolved_target.result;
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const decl = mod.declPtr(decl_index);
|
|
|
|
const target_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
|
|
const target_symbol_index = wasm_file.getAtom(target_atom_index).sym_index;
|
|
|
|
std.debug.assert(reloc_info.parent_atom_index != 0);
|
|
const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = reloc_info.parent_atom_index }).?;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
const is_wasm32 = target.cpu.arch == .wasm32;
|
|
if (decl.ty.zigTypeTag(mod) == .Fn) {
|
|
std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
|
|
try atom.relocs.append(gpa, .{
|
|
.index = target_symbol_index,
|
|
.offset = @intCast(reloc_info.offset),
|
|
.relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
|
|
});
|
|
} else {
|
|
try atom.relocs.append(gpa, .{
|
|
.index = target_symbol_index,
|
|
.offset = @intCast(reloc_info.offset),
|
|
.relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
|
|
.addend = @intCast(reloc_info.addend),
|
|
});
|
|
}
|
|
|
|
// we do not know the final address at this point,
|
|
// as atom allocation will determine the address and relocations
|
|
// will calculate and rewrite this. Therefore, we simply return the symbol index
|
|
// that was targeted.
|
|
return target_symbol_index;
|
|
}
|
|
|
|
pub fn getAnonDeclVAddr(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
decl_val: InternPool.Index,
|
|
reloc_info: link.File.RelocInfo,
|
|
) !u64 {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const target = wasm_file.base.comp.root_mod.resolved_target.result;
|
|
const atom_index = zig_object.anon_decls.get(decl_val).?;
|
|
const target_symbol_index = wasm_file.getAtom(atom_index).getSymbolIndex().?;
|
|
|
|
const parent_atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = reloc_info.parent_atom_index }).?;
|
|
const parent_atom = wasm_file.getAtomPtr(parent_atom_index);
|
|
const is_wasm32 = target.cpu.arch == .wasm32;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
|
|
if (ty.zigTypeTag(mod) == .Fn) {
|
|
std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
|
|
try parent_atom.relocs.append(gpa, .{
|
|
.index = target_symbol_index,
|
|
.offset = @intCast(reloc_info.offset),
|
|
.relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
|
|
});
|
|
} else {
|
|
try parent_atom.relocs.append(gpa, .{
|
|
.index = target_symbol_index,
|
|
.offset = @intCast(reloc_info.offset),
|
|
.relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
|
|
.addend = @intCast(reloc_info.addend),
|
|
});
|
|
}
|
|
|
|
// we do not know the final address at this point,
|
|
// as atom allocation will determine the address and relocations
|
|
// will calculate and rewrite this. Therefore, we simply return the symbol index
|
|
// that was targeted.
|
|
return target_symbol_index;
|
|
}
|
|
|
|
pub fn deleteDeclExport(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
decl_index: InternPool.DeclIndex,
|
|
) void {
|
|
const atom_index = zig_object.decls.get(decl_index) orelse return;
|
|
const sym_index = wasm_file.getAtom(atom_index).sym_index;
|
|
const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = sym_index };
|
|
const sym = loc.getSymbol(wasm_file);
|
|
std.debug.assert(zig_object.global_syms.remove(sym.name));
|
|
}
|
|
|
|
pub fn updateExports(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
mod: *Module,
|
|
exported: Module.Exported,
|
|
exports: []const *Module.Export,
|
|
) !void {
|
|
const decl_index = switch (exported) {
|
|
.decl_index => |i| i,
|
|
.value => |val| {
|
|
_ = val;
|
|
@panic("TODO: implement Wasm linker code for exporting a constant value");
|
|
},
|
|
};
|
|
const decl = mod.declPtr(decl_index);
|
|
const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
|
|
const atom = wasm_file.getAtom(atom_index);
|
|
const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*;
|
|
const gpa = mod.gpa;
|
|
|
|
for (exports) |exp| {
|
|
if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| {
|
|
try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
|
|
gpa,
|
|
decl.srcLoc(mod),
|
|
"Unimplemented: ExportOptions.section '{s}'",
|
|
.{section},
|
|
));
|
|
continue;
|
|
}
|
|
|
|
const exported_decl_index = switch (exp.exported) {
|
|
.value => {
|
|
try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
|
|
gpa,
|
|
decl.srcLoc(mod),
|
|
"Unimplemented: exporting a named constant value",
|
|
.{},
|
|
));
|
|
continue;
|
|
},
|
|
.decl_index => |i| i,
|
|
};
|
|
const exported_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, exported_decl_index);
|
|
const exported_atom = wasm_file.getAtom(exported_atom_index);
|
|
// const export_name = try zig_object.string_table.put(gpa, mod.intern_pool.stringToSlice(exp.opts.name));
|
|
const sym_loc = exported_atom.symbolLoc();
|
|
const sym = sym_loc.getSymbol(wasm_file);
|
|
sym.setGlobal(true);
|
|
sym.setUndefined(false);
|
|
sym.index = atom_sym.index;
|
|
sym.tag = atom_sym.tag;
|
|
sym.name = atom_sym.name;
|
|
|
|
switch (exp.opts.linkage) {
|
|
.Internal => {
|
|
sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
|
|
sym.setFlag(.WASM_SYM_BINDING_WEAK);
|
|
},
|
|
.Weak => {
|
|
sym.setFlag(.WASM_SYM_BINDING_WEAK);
|
|
},
|
|
.Strong => {}, // symbols are strong by default
|
|
.LinkOnce => {
|
|
try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
|
|
gpa,
|
|
decl.srcLoc(mod),
|
|
"Unimplemented: LinkOnce",
|
|
.{},
|
|
));
|
|
continue;
|
|
},
|
|
}
|
|
|
|
// TODO: Revisit this
|
|
// if (zig_object.global_syms.get(export_name)) |existing_loc| {
|
|
// if (existing_loc.index == atom.sym_index) continue;
|
|
// const existing_sym: Symbol = existing_loc.getSymbol(wasm_file).*;
|
|
|
|
// if (!existing_sym.isUndefined()) blk: {
|
|
// if (symbol.isWeak()) {
|
|
// try wasm_file.discarded.put(gpa, existing_loc, sym_loc);
|
|
// continue; // to-be-exported symbol is weak, so we keep the existing symbol
|
|
// }
|
|
|
|
// // new symbol is not weak while existing is, replace existing symbol
|
|
// if (existing_sym.isWeak()) {
|
|
// break :blk;
|
|
// }
|
|
// // When both the to-be-exported symbol and the already existing symbol
|
|
// // are strong symbols, we have a linker error.
|
|
// // In the other case we replace one with the other.
|
|
// try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create(
|
|
// gpa,
|
|
// decl.srcLoc(mod),
|
|
// \\LinkError: symbol '{}' defined multiple times
|
|
// \\ first definition in '{s}'
|
|
// \\ next definition in '{s}'
|
|
// ,
|
|
// .{ exp.opts.name.fmt(&mod.intern_pool), wasm_file.name, wasm_file.name },
|
|
// ));
|
|
// continue;
|
|
// }
|
|
|
|
// // in this case the existing symbol must be replaced either because it's weak or undefined.
|
|
// try wasm.discarded.put(gpa, existing_loc, sym_loc);
|
|
// _ = wasm.imports.remove(existing_loc);
|
|
// _ = wasm.undefs.swapRemove(existing_sym.name);
|
|
// }
|
|
|
|
// // Ensure the symbol will be exported using the given name
|
|
// if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) {
|
|
// try wasm.export_names.put(gpa, sym_loc, export_name);
|
|
// }
|
|
|
|
// try wasm.globals.put(
|
|
// gpa,
|
|
// export_name,
|
|
// sym_loc,
|
|
// );
|
|
}
|
|
}
|
|
|
|
pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex) void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const decl = mod.declPtr(decl_index);
|
|
const atom_index = zig_object.decls.get(decl_index).?;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
|
|
_ = zig_object.decls.remove(decl_index);
|
|
zig_object.symbols.items[atom.sym_index].tag = .dead;
|
|
for (atom.locals.items) |local_atom_index| {
|
|
const local_atom = wasm_file.getAtom(local_atom_index);
|
|
const local_symbol = &zig_object.symbols.items[local_atom.sym_index];
|
|
local_symbol.tag = .dead; // also for any local symbol
|
|
zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
|
|
std.denug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc()));
|
|
}
|
|
|
|
if (decl.isExtern(mod)) {
|
|
_ = zig_object.imports.remove(atom.getSymbolIndex().?);
|
|
}
|
|
_ = wasm_file.symbol_atom.remove(atom.symbolLoc());
|
|
|
|
// if (wasm.dwarf) |*dwarf| {
|
|
// dwarf.freeDecl(decl_index);
|
|
// }
|
|
|
|
if (atom.next) |next_atom_index| {
|
|
const next_atom = wasm_file.getAtomPtr(next_atom_index);
|
|
next_atom.prev = atom.prev;
|
|
atom.next = null;
|
|
}
|
|
if (atom.prev) |prev_index| {
|
|
const prev_atom = wasm_file.getAtomPtr(prev_index);
|
|
prev_atom.next = atom.next;
|
|
atom.prev = null;
|
|
}
|
|
}
|
|
|
|
fn getTypeIndex(zig_object: *const ZigObject, func_type: std.wasm.Type) ?u32 {
|
|
var index: u32 = 0;
|
|
while (index < zig_object.func_types.items.len) : (index += 1) {
|
|
if (zig_object.func_types.items[index].eql(func_type)) return index;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Searches for a matching function signature. When no matching signature is found,
|
|
/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
|
|
pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_type: std.wasm.Type) !u32 {
|
|
if (zig_object.getTypeIndex(func_type)) |index| {
|
|
return index;
|
|
}
|
|
|
|
// functype does not exist.
|
|
const index: u32 = @intCast(zig_object.func_types.items.len);
|
|
const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
|
|
errdefer gpa.free(params);
|
|
const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
|
|
errdefer gpa.free(returns);
|
|
try zig_object.func_types.append(gpa, .{
|
|
.params = params,
|
|
.returns = returns,
|
|
});
|
|
return index;
|
|
}
|
|
|
|
/// Kind represents the type of an Atom, which is only
|
|
/// used to parse a decl into an Atom to define in which section
|
|
/// or segment it should be placed.
|
|
const Kind = union(enum) {
|
|
/// Represents the segment the data symbol should
|
|
/// be inserted into.
|
|
/// TODO: Add TLS segments
|
|
data: enum {
|
|
read_only,
|
|
uninitialized,
|
|
initialized,
|
|
},
|
|
function: void,
|
|
|
|
/// Returns the segment name the data kind represents.
|
|
/// Asserts `kind` has its active tag set to `data`.
|
|
fn segmentName(kind: Kind) []const u8 {
|
|
switch (kind.data) {
|
|
.read_only => return ".rodata.",
|
|
.uninitialized => return ".bss.",
|
|
.initialized => return ".data.",
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Parses an Atom and inserts its metadata into the corresponding sections.
|
|
pub fn parseAtom(zig_object: *ZigObject, wasm_file: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
|
|
// TODO: Revisit
|
|
_ = zig_object;
|
|
_ = wasm_file;
|
|
_ = atom_index;
|
|
_ = kind;
|
|
// const comp = wasm.base.comp;
|
|
// const gpa = comp.gpa;
|
|
// const shared_memory = comp.config.shared_memory;
|
|
// const import_memory = comp.config.import_memory;
|
|
// const atom = wasm.getAtomPtr(atom_index);
|
|
// const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
|
|
// const do_garbage_collect = wasm.base.gc_sections;
|
|
|
|
// if (symbol.isDead() and do_garbage_collect) {
|
|
// // Prevent unreferenced symbols from being parsed.
|
|
// return;
|
|
// }
|
|
|
|
// const final_index: u32 = switch (kind) {
|
|
// .function => result: {
|
|
// const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);
|
|
// const type_index = wasm.atom_types.get(atom_index).?;
|
|
// try wasm.functions.putNoClobber(
|
|
// gpa,
|
|
// .{ .file = null, .index = index },
|
|
// .{ .func = .{ .type_index = type_index }, .sym_index = atom.sym_index },
|
|
// );
|
|
// symbol.tag = .function;
|
|
// symbol.index = index;
|
|
|
|
// if (wasm.code_section_index == null) {
|
|
// wasm.code_section_index = @intCast(wasm.segments.items.len);
|
|
// try wasm.segments.append(gpa, .{
|
|
// .alignment = atom.alignment,
|
|
// .size = atom.size,
|
|
// .offset = 0,
|
|
// .flags = 0,
|
|
// });
|
|
// }
|
|
|
|
// break :result wasm.code_section_index.?;
|
|
// },
|
|
// .data => result: {
|
|
// const segment_name = try std.mem.concat(gpa, u8, &.{
|
|
// kind.segmentName(),
|
|
// wasm.string_table.get(symbol.name),
|
|
// });
|
|
// errdefer gpa.free(segment_name);
|
|
// const segment_info: types.Segment = .{
|
|
// .name = segment_name,
|
|
// .alignment = atom.alignment,
|
|
// .flags = 0,
|
|
// };
|
|
// symbol.tag = .data;
|
|
|
|
// // when creating an object file, or importing memory and the data belongs in the .bss segment
|
|
// // we set the entire region of it to zeroes.
|
|
// // We do not have to do this when exporting the memory (the default) because the runtime
|
|
// // will do it for us, and we do not emit the bss segment at all.
|
|
// if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) {
|
|
// @memset(atom.code.items, 0);
|
|
// }
|
|
|
|
// const should_merge = wasm.base.comp.config.output_mode != .Obj;
|
|
// const gop = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(should_merge));
|
|
// if (gop.found_existing) {
|
|
// const index = gop.value_ptr.*;
|
|
// wasm.segments.items[index].size += atom.size;
|
|
|
|
// symbol.index = @intCast(wasm.segment_info.getIndex(index).?);
|
|
// // segment info already exists, so free its memory
|
|
// gpa.free(segment_name);
|
|
// break :result index;
|
|
// } else {
|
|
// const index: u32 = @intCast(wasm.segments.items.len);
|
|
// var flags: u32 = 0;
|
|
// if (shared_memory) {
|
|
// flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
|
|
// }
|
|
// try wasm.segments.append(gpa, .{
|
|
// .alignment = atom.alignment,
|
|
// .size = 0,
|
|
// .offset = 0,
|
|
// .flags = flags,
|
|
// });
|
|
// gop.value_ptr.* = index;
|
|
|
|
// const info_index: u32 = @intCast(wasm.segment_info.count());
|
|
// try wasm.segment_info.put(gpa, index, segment_info);
|
|
// symbol.index = info_index;
|
|
// break :result index;
|
|
// }
|
|
// },
|
|
// };
|
|
|
|
// const segment: *Segment = &wasm.segments.items[final_index];
|
|
// segment.alignment = segment.alignment.max(atom.alignment);
|
|
|
|
// try wasm.appendAtomAtIndex(final_index, atom_index);
|
|
}
|
|
|
|
/// Generates an atom containing the global error set' size.
|
|
/// This will only be generated if the symbol exists.
|
|
fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const loc = zig_object.findGlobalSymbol("__zig_errors_len") orelse return;
|
|
|
|
const errors_len = wasm_file.base.comp.module.?.global_error_set.count();
|
|
// overwrite existing atom if it already exists (maybe the error set has increased)
|
|
// if not, allcoate a new atom.
|
|
const atom_index = if (wasm_file.symbol_atom.get(loc)) |index| blk: {
|
|
const atom = wasm_file.getAtomPtr(index);
|
|
if (atom.next) |next_atom_index| {
|
|
const next_atom = wasm_file.getAtomPtr(next_atom_index);
|
|
next_atom.prev = atom.prev;
|
|
atom.next = null;
|
|
}
|
|
if (atom.prev) |prev_index| {
|
|
const prev_atom = wasm_file.getAtomPtr(prev_index);
|
|
prev_atom.next = atom.next;
|
|
atom.prev = null;
|
|
}
|
|
atom.deinit(gpa);
|
|
break :blk index;
|
|
} else new_atom: {
|
|
const atom_index: Atom.Index = @intCast(wasm_file.managed_atoms.items.len);
|
|
try wasm_file.symbol_atom.put(gpa, loc, atom_index);
|
|
try wasm_file.managed_atoms.append(gpa, undefined);
|
|
break :new_atom atom_index;
|
|
};
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.* = Atom.empty;
|
|
atom.sym_index = loc.index;
|
|
atom.size = 2;
|
|
try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
|
|
|
|
// try wasm.parseAtom(atom_index, .{ .data = .read_only });
|
|
}
|
|
|
|
/// Initializes symbols and atoms for the debug sections
|
|
/// Initialization is only done when compiling Zig code.
|
|
/// When Zig is invoked as a linker instead, the atoms
|
|
/// and symbols come from the object files instead.
|
|
pub fn initDebugSections(zig_object: *ZigObject) !void {
|
|
if (zig_object.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
|
|
std.debug.assert(zig_object.debug_info_index == null);
|
|
// this will create an Atom and set the index for us.
|
|
zig_object.debug_info_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_info_index, ".debug_info");
|
|
zig_object.debug_line_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_line_index, ".debug_line");
|
|
zig_object.debug_loc_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_loc_index, ".debug_loc");
|
|
zig_object.debug_abbrev_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_abbrev_index, ".debug_abbrev");
|
|
zig_object.debug_ranges_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_ranges_index, ".debug_ranges");
|
|
zig_object.debug_str_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_str_index, ".debug_str");
|
|
zig_object.debug_pubnames_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubnames_index, ".debug_pubnames");
|
|
zig_object.debug_pubtypes_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubtypes_index, ".debug_pubtypes");
|
|
}
|
|
|
|
/// From a given index variable, creates a new debug section.
|
|
/// This initializes the index, appends a new segment,
|
|
/// and finally, creates a managed `Atom`.
|
|
pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const new_index: u32 = @intCast(zig_object.segments.items.len);
|
|
index.* = new_index;
|
|
try zig_object.appendDummySegment();
|
|
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
zig_object.symbols.items[sym_index] = .{
|
|
.tag = .section,
|
|
.name = try zig_object.string_table.put(gpa, name),
|
|
.index = 0,
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
};
|
|
|
|
atom.alignment = .@"1"; // debug sections are always 1-byte-aligned
|
|
return atom_index;
|
|
}
|
|
|
|
pub fn updateDeclLineNumber(zig_object: *ZigObject, mod: *Module, decl_index: InternPool.DeclIndex) !void {
|
|
if (zig_object.dwarf) |*dw| {
|
|
const decl = mod.declPtr(decl_index);
|
|
const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
|
|
|
|
log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
|
|
try dw.updateDeclLineNumber(mod, decl_index);
|
|
}
|
|
}
|
|
|
|
/// Allocates debug atoms into their respective debug sections
|
|
/// to merge them with maybe-existing debug atoms from object files.
|
|
fn allocateDebugAtoms(zig_object: *ZigObject) !void {
|
|
if (zig_object.dwarf == null) return;
|
|
|
|
const allocAtom = struct {
|
|
fn f(ctx: *ZigObject, maybe_index: *?u32, atom_index: Atom.Index) !void {
|
|
const index = maybe_index.* orelse idx: {
|
|
const index = @as(u32, @intCast(ctx.segments.items.len));
|
|
try ctx.appendDummySegment();
|
|
maybe_index.* = index;
|
|
break :idx index;
|
|
};
|
|
const atom = ctx.getAtomPtr(atom_index);
|
|
atom.size = @as(u32, @intCast(atom.code.items.len));
|
|
ctx.symbols.items[atom.sym_index].index = index;
|
|
try ctx.appendAtomAtIndex(index, atom_index);
|
|
}
|
|
}.f;
|
|
|
|
try allocAtom(zig_object, &zig_object.debug_info_index, zig_object.debug_info_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_line_index, zig_object.debug_line_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_loc_index, zig_object.debug_loc_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_str_index, zig_object.debug_str_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_ranges_index, zig_object.debug_ranges_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_abbrev_index, zig_object.debug_abbrev_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_pubnames_index, zig_object.debug_pubnames_atom.?);
|
|
try allocAtom(zig_object, &zig_object.debug_pubtypes_index, zig_object.debug_pubtypes_atom.?);
|
|
}
|
|
|
|
/// For the given `decl_index`, stores the corresponding type representing the function signature.
|
|
/// Asserts declaration has an associated `Atom`.
|
|
/// Returns the index into the list of types.
|
|
pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 {
|
|
const atom_index = zig_object.decls.get(decl_index).?;
|
|
const index = try zig_object.putOrGetFuncType(gpa, func_type);
|
|
try zig_object.atom_types.put(gpa, atom_index, index);
|
|
return index;
|
|
}
|
|
|
|
/// The symbols in ZigObject are already represented by an atom as we need to store its data.
|
|
/// So rather than creating a new Atom and returning its index, we use this oppertunity to scan
|
|
/// its relocations and create any GOT symbols or function table indexes it may require.
|
|
pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: u32) !Atom.Index {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index };
|
|
const final_index = try wasm_file.getMatchingSegment(zig_object.index, index);
|
|
const atom_index = wasm_file.symbol_atom.get(loc).?;
|
|
try wasm_file.appendAtomAtIndex(final_index, atom_index);
|
|
const atom = wasm_file.getAtom(atom_index);
|
|
for (atom.relocs.items) |reloc| {
|
|
switch (reloc.relocation_type) {
|
|
.R_WASM_TABLE_INDEX_I32,
|
|
.R_WASM_TABLE_INDEX_I64,
|
|
.R_WASM_TABLE_INDEX_SLEB,
|
|
.R_WASM_TABLE_INDEX_SLEB64,
|
|
=> {
|
|
try wasm_file.function_table.put(gpa, loc, 0);
|
|
},
|
|
.R_WASM_GLOBAL_INDEX_I32,
|
|
.R_WASM_GLOBAL_INDEX_LEB,
|
|
=> {
|
|
const sym = zig_object.symbol(reloc.index);
|
|
if (sym.tag != .global) {
|
|
try wasm_file.got_symbols.append(gpa, loc);
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
return atom_index;
|
|
}
|
|
|
|
const build_options = @import("build_options");
|
|
const builtin = @import("builtin");
|
|
const codegen = @import("../../codegen.zig");
|
|
const link = @import("../../link.zig");
|
|
const log = std.log.scoped(.zig_object);
|
|
const std = @import("std");
|
|
const types = @import("types.zig");
|
|
|
|
const Air = @import("../../Air.zig");
|
|
const Atom = @import("Atom.zig");
|
|
const Dwarf = @import("../Dwarf.zig");
|
|
const File = @import("file.zig").File;
|
|
const InternPool = @import("../../InternPool.zig");
|
|
const Liveness = @import("../../Liveness.zig");
|
|
const Module = @import("../../Module.zig");
|
|
const StringTable = @import("../StringTable.zig");
|
|
const Symbol = @import("Symbol.zig");
|
|
const Type = @import("../../type.zig").Type;
|
|
const TypedValue = @import("../../TypedValue.zig");
|
|
const Value = @import("../../value.zig").Value;
|
|
const Wasm = @import("../Wasm.zig");
|
|
const ZigObject = @This();
|