mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-21 16:41:56 +03:00
548a087faf
The type `Zcu.Decl` in the compiler is problematic: over time it has gained many responsibilities. Every source declaration, container type, generic instantiation, and `@extern` has a `Decl`. The functions of these `Decl`s are in some cases entirely disjoint. After careful analysis, I determined that the two main responsibilities of `Decl` are as follows: * A `Decl` acts as the "subject" of semantic analysis at comptime. A single unit of analysis is either a runtime function body, or a `Decl`. It registers incremental dependencies, tracks analysis errors, etc. * A `Decl` acts as a "global variable": a pointer to it is consistent, and it may be lowered to a specific symbol by the codegen backend. This commit eliminates `Decl` and introduces new types to model these responsibilities: `Cau` (Comptime Analysis Unit) and `Nav` (Named Addressable Value). Every source declaration, and every container type requiring resolution (so *not* including `opaque`), has a `Cau`. For a source declaration, this `Cau` performs the resolution of its value. (When #131 is implemented, it is unsolved whether type and value resolution will share a `Cau` or have two distinct `Cau`s.) For a type, this `Cau` is the context in which type resolution occurs. Every non-`comptime` source declaration, every generic instantiation, and every distinct `extern` has a `Nav`. These are sent to codegen/link: the backends by definition do not care about `Cau`s. This commit has some minor technically-breaking changes surrounding `usingnamespace`. I don't think they'll impact anyone, since the changes are fixes around semantics which were previously inconsistent (the behavior changed depending on hashmap iteration order!). Aside from that, this changeset has no significant user-facing changes. Instead, it is an internal refactor which makes it easier to correctly model the responsibilities of different objects, particularly regarding incremental compilation. The performance impact should be negligible, but I will take measurements before merging this work into `master`. Co-authored-by: Jacob Young <jacobly0@users.noreply.github.com> Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
1252 lines
51 KiB
Zig
1252 lines
51 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,
|
|
/// Map of all `Nav` that are currently alive.
|
|
/// Each index maps to the corresponding `NavInfo`.
|
|
navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavInfo) = .{},
|
|
/// 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) = .{},
|
|
/// List of indexes pointing to an entry within the `functions` list which has been removed.
|
|
functions_free_list: std.ArrayListUnmanaged(u32) = .{},
|
|
/// Map of symbol locations, represented by its `types.Import`.
|
|
imports: std.AutoHashMapUnmanaged(Symbol.Index, 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, Symbol.Index) = .{},
|
|
/// List of symbol indexes which are free to be used.
|
|
symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
|
/// Extra metadata about the linking section, such as alignment of segments and their name.
|
|
segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
|
|
/// List of indexes which contain a free slot in the `segment_info` list.
|
|
segment_free_list: std.ArrayListUnmanaged(u32) = .{},
|
|
/// 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.
|
|
uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{},
|
|
/// List of atom indexes of functions that are generated by the backend.
|
|
synthetic_functions: std.ArrayListUnmanaged(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: Symbol.Index = .null,
|
|
/// Atom index of the table of symbol names. This is stored so we can clean up the atom.
|
|
error_names_atom: Atom.Index = .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: Symbol.Index,
|
|
/// 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,
|
|
|
|
const NavInfo = struct {
|
|
atom: Atom.Index = .null,
|
|
exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
|
|
|
|
fn @"export"(ni: NavInfo, zig_object: *const ZigObject, name: []const u8) ?Symbol.Index {
|
|
for (ni.exports.items) |sym_index| {
|
|
const sym_name_index = zig_object.symbol(sym_index).name;
|
|
const sym_name = zig_object.string_table.getAssumeExists(sym_name_index);
|
|
if (std.mem.eql(u8, name, sym_name)) {
|
|
return sym_index;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
fn appendExport(ni: *NavInfo, gpa: std.mem.Allocator, sym_index: Symbol.Index) !void {
|
|
return ni.exports.append(gpa, sym_index);
|
|
}
|
|
|
|
fn deleteExport(ni: *NavInfo, sym_index: Symbol.Index) void {
|
|
for (ni.exports.items, 0..) |idx, index| {
|
|
if (idx == sym_index) {
|
|
_ = ni.exports.swapRemove(index);
|
|
return;
|
|
}
|
|
}
|
|
unreachable; // invalid sym_index
|
|
}
|
|
};
|
|
|
|
/// 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: Symbol.Index) *Symbol {
|
|
return &zig_object.symbols.items[@intFromEnum(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);
|
|
}
|
|
|
|
{
|
|
var it = zig_object.navs.valueIterator();
|
|
while (it.next()) |nav_info| {
|
|
const atom = wasm_file.getAtomPtr(nav_info.atom);
|
|
for (atom.locals.items) |local_index| {
|
|
const local_atom = wasm_file.getAtomPtr(local_index);
|
|
local_atom.deinit(gpa);
|
|
}
|
|
atom.deinit(gpa);
|
|
nav_info.exports.deinit(gpa);
|
|
}
|
|
}
|
|
{
|
|
for (zig_object.uavs.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);
|
|
}
|
|
}
|
|
if (zig_object.findGlobalSymbol("__zig_errors_len")) |sym_index| {
|
|
const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index }).?;
|
|
wasm_file.getAtomPtr(atom_index).deinit(gpa);
|
|
}
|
|
if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol })) |atom_index| {
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.deinit(gpa);
|
|
}
|
|
for (zig_object.synthetic_functions.items) |atom_index| {
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.deinit(gpa);
|
|
}
|
|
zig_object.synthetic_functions.deinit(gpa);
|
|
for (zig_object.func_types.items) |*ty| {
|
|
ty.deinit(gpa);
|
|
}
|
|
if (zig_object.error_names_atom != .null) {
|
|
const atom = wasm_file.getAtomPtr(zig_object.error_names_atom);
|
|
atom.deinit(gpa);
|
|
}
|
|
zig_object.global_syms.deinit(gpa);
|
|
zig_object.func_types.deinit(gpa);
|
|
zig_object.atom_types.deinit(gpa);
|
|
zig_object.functions.deinit(gpa);
|
|
zig_object.imports.deinit(gpa);
|
|
zig_object.navs.deinit(gpa);
|
|
zig_object.uavs.deinit(gpa);
|
|
zig_object.symbols.deinit(gpa);
|
|
zig_object.symbols_free_list.deinit(gpa);
|
|
zig_object.segment_info.deinit(gpa);
|
|
zig_object.segment_free_list.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) !Symbol.Index {
|
|
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[@intFromEnum(index)] = sym;
|
|
return index;
|
|
}
|
|
const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
|
|
zig_object.symbols.appendAssumeCapacity(sym);
|
|
return index;
|
|
}
|
|
|
|
// Generate code for the `Nav`, storing it in memory to be later written to
|
|
// the file on flush().
|
|
pub fn updateNav(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
) !void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const nav = ip.getNav(nav_index);
|
|
|
|
const is_extern, const lib_name, const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
|
|
.variable => |variable| .{ false, variable.lib_name, variable.init },
|
|
.func => return,
|
|
.@"extern" => |@"extern"| if (ip.isFunctionType(nav.typeOf(ip)))
|
|
return
|
|
else
|
|
.{ true, @"extern".lib_name, nav.status.resolved.val },
|
|
else => .{ false, .none, nav.status.resolved.val },
|
|
};
|
|
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index);
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.clear();
|
|
|
|
if (is_extern)
|
|
return zig_object.addOrUpdateImport(wasm_file, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null);
|
|
|
|
var code_writer = std.ArrayList(u8).init(gpa);
|
|
defer code_writer.deinit();
|
|
|
|
const res = try codegen.generateSymbol(
|
|
&wasm_file.base,
|
|
pt,
|
|
zcu.navSrcLoc(nav_index),
|
|
Value.fromInterned(nav_init),
|
|
&code_writer,
|
|
.none,
|
|
.{ .parent_atom_index = @intFromEnum(atom.sym_index) },
|
|
);
|
|
|
|
const code = switch (res) {
|
|
.ok => code_writer.items,
|
|
.fail => |em| {
|
|
try zcu.failed_codegen.put(zcu.gpa, nav_index, em);
|
|
return;
|
|
},
|
|
};
|
|
|
|
return zig_object.finishUpdateNav(wasm_file, pt, nav_index, code);
|
|
}
|
|
|
|
pub fn updateFunc(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
func_index: InternPool.Index,
|
|
air: Air,
|
|
liveness: Liveness,
|
|
) !void {
|
|
const zcu = pt.zcu;
|
|
const gpa = zcu.gpa;
|
|
const func = pt.zcu.funcInfo(func_index);
|
|
const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, func.owner_nav);
|
|
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,
|
|
pt,
|
|
zcu.navSrcLoc(func.owner_nav),
|
|
func_index,
|
|
air,
|
|
liveness,
|
|
&code_writer,
|
|
.none,
|
|
);
|
|
|
|
const code = switch (result) {
|
|
.ok => code_writer.items,
|
|
.fail => |em| {
|
|
try pt.zcu.failed_codegen.put(gpa, func.owner_nav, em);
|
|
return;
|
|
},
|
|
};
|
|
|
|
return zig_object.finishUpdateNav(wasm_file, pt, func.owner_nav, code);
|
|
}
|
|
|
|
fn finishUpdateNav(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
code: []const u8,
|
|
) !void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const gpa = zcu.gpa;
|
|
const nav = ip.getNav(nav_index);
|
|
const nav_val = zcu.navValue(nav_index);
|
|
const nav_info = zig_object.navs.get(nav_index).?;
|
|
const atom_index = nav_info.atom;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
const sym = zig_object.symbol(atom.sym_index);
|
|
sym.name = try zig_object.string_table.insert(gpa, nav.fqn.toSlice(ip));
|
|
try atom.code.appendSlice(gpa, code);
|
|
atom.size = @intCast(code.len);
|
|
|
|
if (ip.isFunctionType(nav.typeOf(ip))) {
|
|
sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? });
|
|
sym.tag = .function;
|
|
} else {
|
|
const is_const, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
|
|
.variable => |variable| .{ false, variable.init },
|
|
.@"extern" => |@"extern"| .{ @"extern".is_const, .none },
|
|
else => .{ true, nav_val.toIntern() },
|
|
};
|
|
const segment_name = name: {
|
|
if (is_const) break :name ".rodata.";
|
|
|
|
if (nav_init != .none and Value.fromInterned(nav_init).isUndefDeep(zcu)) {
|
|
break :name switch (zcu.navFileScope(nav_index).mod.optimize_mode) {
|
|
.Debug, .ReleaseSafe => ".data.",
|
|
.ReleaseFast, .ReleaseSmall => ".bss.",
|
|
};
|
|
}
|
|
// when the decl is all zeroes, we store the atom in the bss segment,
|
|
// in all other cases it will be in the data segment.
|
|
for (atom.code.items) |byte| {
|
|
if (byte != 0) break :name ".data.";
|
|
}
|
|
break :name ".bss.";
|
|
};
|
|
if ((wasm_file.base.isObject() or wasm_file.base.comp.config.import_memory) and
|
|
std.mem.startsWith(u8, segment_name, ".bss"))
|
|
{
|
|
@memset(atom.code.items, 0);
|
|
}
|
|
// Will be freed upon freeing of decl or after cleanup of Wasm binary.
|
|
const full_segment_name = try std.mem.concat(gpa, u8, &.{
|
|
segment_name,
|
|
nav.fqn.toSlice(ip),
|
|
});
|
|
errdefer gpa.free(full_segment_name);
|
|
sym.tag = .data;
|
|
sym.index = try zig_object.createDataSegment(gpa, full_segment_name, pt.navAlignment(nav_index));
|
|
}
|
|
if (code.len == 0) return;
|
|
atom.alignment = pt.navAlignment(nav_index);
|
|
}
|
|
|
|
/// Creates and initializes a new segment in the 'Data' section.
|
|
/// Reuses free slots in the list of segments and returns the index.
|
|
fn createDataSegment(
|
|
zig_object: *ZigObject,
|
|
gpa: std.mem.Allocator,
|
|
name: []const u8,
|
|
alignment: InternPool.Alignment,
|
|
) !u32 {
|
|
const segment_index: u32 = if (zig_object.segment_free_list.popOrNull()) |index|
|
|
index
|
|
else index: {
|
|
const idx: u32 = @intCast(zig_object.segment_info.items.len);
|
|
_ = try zig_object.segment_info.addOne(gpa);
|
|
break :index idx;
|
|
};
|
|
zig_object.segment_info.items[segment_index] = .{
|
|
.alignment = alignment,
|
|
.flags = 0,
|
|
.name = name,
|
|
};
|
|
return segment_index;
|
|
}
|
|
|
|
/// For a given `InternPool.Nav.Index` 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 getOrCreateAtomForNav(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
) !Atom.Index {
|
|
const ip = &pt.zcu.intern_pool;
|
|
const gpa = pt.zcu.gpa;
|
|
const gop = try zig_object.navs.getOrPut(gpa, nav_index);
|
|
if (!gop.found_existing) {
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) };
|
|
const nav = ip.getNav(nav_index);
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.name = try zig_object.string_table.insert(gpa, nav.fqn.toSlice(ip));
|
|
}
|
|
return gop.value_ptr.atom;
|
|
}
|
|
|
|
pub fn lowerUav(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
uav: InternPool.Index,
|
|
explicit_alignment: InternPool.Alignment,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
) !codegen.GenResult {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const gop = try zig_object.uavs.getOrPut(gpa, uav);
|
|
if (!gop.found_existing) {
|
|
var name_buf: [32]u8 = undefined;
|
|
const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
|
|
@intFromEnum(uav),
|
|
}) catch unreachable;
|
|
|
|
switch (try zig_object.lowerConst(wasm_file, pt, name, Value.fromInterned(uav), src_loc)) {
|
|
.ok => |atom_index| zig_object.uavs.values()[gop.index] = atom_index,
|
|
.fail => |em| return .{ .fail = em },
|
|
}
|
|
}
|
|
|
|
const atom = wasm_file.getAtomPtr(zig_object.uavs.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 .{ .mcv = .{ .load_symbol = @intFromEnum(atom.sym_index) } };
|
|
}
|
|
|
|
const LowerConstResult = union(enum) {
|
|
ok: Atom.Index,
|
|
fail: *Zcu.ErrorMsg,
|
|
};
|
|
|
|
fn lowerConst(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
name: []const u8,
|
|
val: Value,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
) !LowerConstResult {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
|
|
const ty = val.typeOf(mod);
|
|
|
|
// 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 = ty.abiAlignment(pt);
|
|
const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name });
|
|
errdefer gpa.free(segment_name);
|
|
zig_object.symbol(sym_index).* = .{
|
|
.name = try zig_object.string_table.insert(gpa, name),
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
.tag = .data,
|
|
.index = try zig_object.createDataSegment(
|
|
gpa,
|
|
segment_name,
|
|
ty.abiAlignment(pt),
|
|
),
|
|
.virtual_address = undefined,
|
|
};
|
|
|
|
const result = try codegen.generateSymbol(
|
|
&wasm_file.base,
|
|
pt,
|
|
src_loc,
|
|
val,
|
|
&value_bytes,
|
|
.none,
|
|
.{
|
|
.parent_atom_index = @intFromEnum(atom.sym_index),
|
|
},
|
|
);
|
|
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, pt: Zcu.PerThread) !Symbol.Index {
|
|
if (zig_object.error_table_symbol != .null) {
|
|
return zig_object.error_table_symbol;
|
|
}
|
|
|
|
// 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;
|
|
atom.alignment = slice_ty.abiAlignment(pt);
|
|
|
|
const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_name_table");
|
|
const segment_name = try gpa.dupe(u8, ".rodata.__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 = try zig_object.createDataSegment(gpa, segment_name, atom.alignment),
|
|
.virtual_address = undefined,
|
|
};
|
|
|
|
log.debug("Error name table was created with symbol index: ({d})", .{@intFromEnum(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, tid: Zcu.PerThread.Id) !void {
|
|
if (zig_object.error_table_symbol == .null) return;
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol }).?;
|
|
|
|
// 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 segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names");
|
|
const names_symbol = zig_object.symbol(names_sym_index);
|
|
names_symbol.* = .{
|
|
.name = sym_name,
|
|
.tag = .data,
|
|
.flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
|
|
.index = try zig_object.createDataSegment(gpa, segment_name, names_atom.alignment),
|
|
.virtual_address = undefined,
|
|
};
|
|
|
|
log.debug("Populating error names", .{});
|
|
|
|
// Addend for each relocation to the table
|
|
var addend: u32 = 0;
|
|
const pt: Zcu.PerThread = .{ .zcu = wasm_file.base.comp.module.?, .tid = tid };
|
|
const slice_ty = Type.slice_const_u8_sentinel_0;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
{
|
|
// TODO: remove this unreachable entry
|
|
try atom.code.appendNTimes(gpa, 0, 4);
|
|
try atom.code.writer(gpa).writeInt(u32, 0, .little);
|
|
atom.size += @intCast(slice_ty.abiSize(pt));
|
|
addend += 1;
|
|
|
|
try names_atom.code.append(gpa, 0);
|
|
}
|
|
const ip = &pt.zcu.intern_pool;
|
|
for (ip.global_error_set.getNamesFromMainThread()) |error_name| {
|
|
const error_name_slice = error_name.toSlice(ip);
|
|
const len: u32 = @intCast(error_name_slice.len + 1); // names are 0-terminated
|
|
|
|
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 = @intFromEnum(names_atom.sym_index),
|
|
.relocation_type = .R_WASM_MEMORY_ADDR_I32,
|
|
.offset = offset,
|
|
.addend = @intCast(addend),
|
|
});
|
|
atom.size += @intCast(slice_ty.abiSize(pt));
|
|
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_slice[0..len]);
|
|
|
|
log.debug("Populated error name: '{}'", .{error_name.fmt(ip)});
|
|
}
|
|
names_atom.size = addend;
|
|
zig_object.error_names_atom = names_atom_index;
|
|
}
|
|
|
|
/// 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: Symbol.Index,
|
|
/// 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 != .null);
|
|
// 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[@intFromEnum(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| l_name else wasm_file.host_name;
|
|
if (!gop.found_existing) {
|
|
zig_object.imported_functions_count += 1;
|
|
}
|
|
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 },
|
|
};
|
|
sym.tag = .function;
|
|
} else {
|
|
sym.tag = .data;
|
|
}
|
|
}
|
|
|
|
/// 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) !Symbol.Index {
|
|
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: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
|
|
try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
|
|
zig_object.symbols.items.len += 1;
|
|
break :blk index;
|
|
};
|
|
zig_object.symbol(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 getNavVAddr(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
reloc_info: link.File.RelocInfo,
|
|
) !u64 {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const gpa = zcu.gpa;
|
|
const nav = ip.getNav(nav_index);
|
|
const target = &zcu.navFileScope(nav_index).mod.resolved_target.result;
|
|
|
|
const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index);
|
|
const target_atom = wasm_file.getAtom(target_atom_index);
|
|
const target_symbol_index = @intFromEnum(target_atom.sym_index);
|
|
switch (ip.indexToKey(nav.status.resolved.val)) {
|
|
.@"extern" => |@"extern"| try zig_object.addOrUpdateImport(
|
|
wasm_file,
|
|
nav.name.toSlice(ip),
|
|
target_atom.sym_index,
|
|
@"extern".lib_name.toSlice(ip),
|
|
null,
|
|
),
|
|
else => {},
|
|
}
|
|
|
|
std.debug.assert(reloc_info.parent_atom_index != 0);
|
|
const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
const is_wasm32 = target.cpu.arch == .wasm32;
|
|
if (ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) {
|
|
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 getUavVAddr(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
uav: 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.uavs.get(uav).?;
|
|
const target_symbol_index = @intFromEnum(wasm_file.getAtom(atom_index).sym_index);
|
|
|
|
const parent_atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(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(uav));
|
|
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 deleteExport(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
exported: Zcu.Exported,
|
|
name: InternPool.NullTerminatedString,
|
|
) void {
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const nav_index = switch (exported) {
|
|
.nav => |nav_index| nav_index,
|
|
.uav => @panic("TODO: implement Wasm linker code for exporting a constant value"),
|
|
};
|
|
const nav_info = zig_object.navs.getPtr(nav_index) orelse return;
|
|
if (nav_info.@"export"(zig_object, name.toSlice(&mod.intern_pool))) |sym_index| {
|
|
const sym = zig_object.symbol(sym_index);
|
|
nav_info.deleteExport(sym_index);
|
|
std.debug.assert(zig_object.global_syms.remove(sym.name));
|
|
std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index }));
|
|
zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {};
|
|
sym.tag = .dead;
|
|
}
|
|
}
|
|
|
|
pub fn updateExports(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
pt: Zcu.PerThread,
|
|
exported: Zcu.Exported,
|
|
export_indices: []const u32,
|
|
) !void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const nav_index = switch (exported) {
|
|
.nav => |nav| nav,
|
|
.uav => |uav| {
|
|
_ = uav;
|
|
@panic("TODO: implement Wasm linker code for exporting a constant value");
|
|
},
|
|
};
|
|
const nav = ip.getNav(nav_index);
|
|
const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index);
|
|
const nav_info = zig_object.navs.getPtr(nav_index).?;
|
|
const atom = wasm_file.getAtom(atom_index);
|
|
const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*;
|
|
const gpa = zcu.gpa;
|
|
log.debug("Updating exports for decl '{}'", .{nav.name.fmt(ip)});
|
|
|
|
for (export_indices) |export_idx| {
|
|
const exp = zcu.all_exports.items[export_idx];
|
|
if (exp.opts.section.toSlice(ip)) |section| {
|
|
try zcu.failed_exports.putNoClobber(gpa, export_idx, try Zcu.ErrorMsg.create(
|
|
gpa,
|
|
zcu.navSrcLoc(nav_index),
|
|
"Unimplemented: ExportOptions.section '{s}'",
|
|
.{section},
|
|
));
|
|
continue;
|
|
}
|
|
|
|
const export_string = exp.opts.name.toSlice(ip);
|
|
const sym_index = if (nav_info.@"export"(zig_object, export_string)) |idx| idx else index: {
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
try nav_info.appendExport(gpa, sym_index);
|
|
break :index sym_index;
|
|
};
|
|
|
|
const export_name = try zig_object.string_table.insert(gpa, export_string);
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.setGlobal(true);
|
|
sym.setUndefined(false);
|
|
sym.index = atom_sym.index;
|
|
sym.tag = atom_sym.tag;
|
|
sym.name = export_name;
|
|
|
|
switch (exp.opts.linkage) {
|
|
.internal => {
|
|
sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
|
|
},
|
|
.weak => {
|
|
sym.setFlag(.WASM_SYM_BINDING_WEAK);
|
|
},
|
|
.strong => {}, // symbols are strong by default
|
|
.link_once => {
|
|
try zcu.failed_exports.putNoClobber(gpa, export_idx, try Zcu.ErrorMsg.create(
|
|
gpa,
|
|
zcu.navSrcLoc(nav_index),
|
|
"Unimplemented: LinkOnce",
|
|
.{},
|
|
));
|
|
continue;
|
|
},
|
|
}
|
|
if (exp.opts.visibility == .hidden) {
|
|
sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
|
|
}
|
|
log.debug(" with name '{s}' - {}", .{ export_string, sym });
|
|
try zig_object.global_syms.put(gpa, export_name, sym_index);
|
|
try wasm_file.symbol_atom.put(gpa, .{ .file = zig_object.index, .index = sym_index }, atom_index);
|
|
}
|
|
}
|
|
|
|
pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.Nav.Index) void {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const mod = wasm_file.base.comp.module.?;
|
|
const ip = &mod.intern_pool;
|
|
const nav_info = zig_object.navs.getPtr(nav_index).?;
|
|
const atom_index = nav_info.atom;
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
|
|
for (nav_info.exports.items) |exp_sym_index| {
|
|
const exp_sym = zig_object.symbol(exp_sym_index);
|
|
exp_sym.tag = .dead;
|
|
zig_object.symbols_free_list.append(exp_sym_index) catch {};
|
|
}
|
|
nav_info.exports.deinit(gpa);
|
|
std.debug.assert(zig_object.navs.remove(nav_index));
|
|
const sym = &zig_object.symbols.items[atom.sym_index];
|
|
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];
|
|
std.debug.assert(local_symbol.tag == .data);
|
|
zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
|
|
std.debug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc()));
|
|
local_symbol.tag = .dead; // also for any local symbol
|
|
const segment = &zig_object.segment_info.items[local_atom.sym_index];
|
|
gpa.free(segment.name);
|
|
segment.name = &.{}; // Ensure no accidental double free
|
|
}
|
|
|
|
const nav_val = mod.navValue(nav_index).toIntern();
|
|
if (ip.indexToKey(nav_val) == .@"extern") {
|
|
std.debug.assert(zig_object.imports.remove(atom.sym_index));
|
|
}
|
|
std.debug.assert(wasm_file.symbol_atom.remove(atom.symbolLoc()));
|
|
|
|
// if (wasm.dwarf) |*dwarf| {
|
|
// dwarf.freeDecl(decl_index);
|
|
// }
|
|
|
|
atom.prev = null;
|
|
sym.tag = .dead;
|
|
if (sym.isGlobal()) {
|
|
std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
|
|
}
|
|
if (ip.isFunctionType(ip.typeOf(nav_val))) {
|
|
zig_object.functions_free_list.append(gpa, sym.index) catch {};
|
|
std.debug.assert(zig_object.atom_types.remove(atom_index));
|
|
} else {
|
|
zig_object.segment_free_list.append(gpa, sym.index) catch {};
|
|
const segment = &zig_object.segment_info.items[sym.index];
|
|
gpa.free(segment.name);
|
|
segment.name = &.{}; // Prevent accidental double free
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// 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 sym_index = zig_object.findGlobalSymbol("__zig_errors_len") orelse return;
|
|
|
|
const errors_len = 1 + wasm_file.base.comp.module.?.intern_pool.global_error_set.getNamesFromMainThread().len;
|
|
// overwrite existing atom if it already exists (maybe the error set has increased)
|
|
// if not, allocate a new atom.
|
|
const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: {
|
|
const atom = wasm_file.getAtomPtr(index);
|
|
atom.prev = .null;
|
|
atom.deinit(gpa);
|
|
break :blk index;
|
|
} else idx: {
|
|
// We found a call to __zig_errors_len so make the symbol a local symbol
|
|
// and define it, so the final binary or resulting object file will not attempt
|
|
// to resolve it.
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.setGlobal(false);
|
|
sym.setUndefined(false);
|
|
sym.tag = .data;
|
|
const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len");
|
|
sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2");
|
|
break :idx try wasm_file.createAtom(sym_index, zig_object.index);
|
|
};
|
|
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.code.clearRetainingCapacity();
|
|
atom.sym_index = sym_index;
|
|
atom.size = 2;
|
|
atom.alignment = .@"2";
|
|
try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
|
|
}
|
|
|
|
fn findGlobalSymbol(zig_object: *ZigObject, name: []const u8) ?Symbol.Index {
|
|
const offset = zig_object.string_table.getOffset(name) orelse return null;
|
|
return zig_object.global_syms.get(offset);
|
|
}
|
|
|
|
/// 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,
|
|
pt: Zcu.PerThread,
|
|
decl_index: InternPool.DeclIndex,
|
|
) !void {
|
|
if (zig_object.dwarf) |*dw| {
|
|
const decl = pt.zcu.declPtr(decl_index);
|
|
log.debug("updateDeclLineNumber {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl });
|
|
try dw.updateDeclLineNumber(pt.zcu, 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, nav_index: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {
|
|
const nav_info = zig_object.navs.get(nav_index).?;
|
|
const index = try zig_object.putOrGetFuncType(gpa, func_type);
|
|
try zig_object.atom_types.put(gpa, nav_info.atom, 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 opportunity 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: Symbol.Index) !Atom.Index {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index };
|
|
const atom_index = wasm_file.symbol_atom.get(loc).?;
|
|
const final_index = try wasm_file.getMatchingSegment(zig_object.index, index);
|
|
try wasm_file.appendAtomAtIndex(final_index, atom_index);
|
|
const atom = wasm_file.getAtom(atom_index);
|
|
for (atom.relocs.items) |reloc| {
|
|
const reloc_index: Symbol.Index = @enumFromInt(reloc.index);
|
|
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, .{
|
|
.file = zig_object.index,
|
|
.index = reloc_index,
|
|
}, 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, .{
|
|
.file = zig_object.index,
|
|
.index = reloc_index,
|
|
});
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
return atom_index;
|
|
}
|
|
|
|
/// Creates a new Wasm function with a given symbol name and body.
|
|
/// Returns the symbol index of the new function.
|
|
pub fn createFunction(
|
|
zig_object: *ZigObject,
|
|
wasm_file: *Wasm,
|
|
symbol_name: []const u8,
|
|
func_ty: std.wasm.Type,
|
|
function_body: *std.ArrayList(u8),
|
|
relocations: *std.ArrayList(types.Relocation),
|
|
) !Symbol.Index {
|
|
const gpa = wasm_file.base.comp.gpa;
|
|
const sym_index = try zig_object.allocateSymbol(gpa);
|
|
const sym = zig_object.symbol(sym_index);
|
|
sym.tag = .function;
|
|
sym.name = try zig_object.string_table.insert(gpa, symbol_name);
|
|
const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
|
|
sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
|
|
|
|
const atom_index = try wasm_file.createAtom(sym_index, zig_object.index);
|
|
const atom = wasm_file.getAtomPtr(atom_index);
|
|
atom.size = @intCast(function_body.items.len);
|
|
atom.code = function_body.moveToUnmanaged();
|
|
atom.relocs = relocations.moveToUnmanaged();
|
|
|
|
try zig_object.synthetic_functions.append(gpa, atom_index);
|
|
return sym_index;
|
|
}
|
|
|
|
/// Appends a new `std.wasm.Func` to the list of functions and returns its index.
|
|
fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 {
|
|
const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx|
|
|
idx
|
|
else idx: {
|
|
const len: u32 = @intCast(zig_object.functions.items.len);
|
|
_ = try zig_object.functions.addOne(gpa);
|
|
break :idx len;
|
|
};
|
|
zig_object.functions.items[index] = func;
|
|
|
|
return index;
|
|
}
|
|
|
|
pub fn flushModule(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.PerThread.Id) !void {
|
|
try zig_object.populateErrorNameTable(wasm_file, tid);
|
|
try zig_object.setupErrorsLen(wasm_file);
|
|
}
|
|
|
|
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 Zcu = @import("../../Zcu.zig");
|
|
const StringTable = @import("../StringTable.zig");
|
|
const Symbol = @import("Symbol.zig");
|
|
const Type = @import("../../Type.zig");
|
|
const Value = @import("../../Value.zig");
|
|
const Wasm = @import("../Wasm.zig");
|
|
const AnalUnit = InternPool.AnalUnit;
|
|
const ZigObject = @This();
|