Skip to content

Commit

Permalink
Merge pull request ziglang#18155 from Luukdegram/wasm-gc
Browse files Browse the repository at this point in the history
wasm-linker: implement garbage-collection and performance improvements
  • Loading branch information
andrewrk authored Nov 29, 2023
2 parents 22d7c7d + 4115f70 commit cd7ac56
Show file tree
Hide file tree
Showing 8 changed files with 447 additions and 254 deletions.
392 changes: 287 additions & 105 deletions src/link/Wasm.zig

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions src/link/Wasm/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ alignment: Wasm.Alignment,
/// Offset into the section where the atom lives, this already accounts
/// for alignment.
offset: u32,
/// The original offset within the object file. This value is substracted from
/// relocation offsets to determine where in the `data` to rewrite the value
original_offset: u32,

/// Represents the index of the file this atom was generated from.
/// This is 'null' when the atom was generated by a Decl from Zig code.
file: ?u16,
Expand Down Expand Up @@ -50,11 +54,11 @@ pub const empty: Atom = .{
.prev = null,
.size = 0,
.sym_index = 0,
.original_offset = 0,
};

/// Frees all resources owned by this `Atom`.
pub fn deinit(atom: *Atom, wasm: *Wasm) void {
const gpa = wasm.base.allocator;
pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
atom.relocs.deinit(gpa);
atom.code.deinit(gpa);
atom.locals.deinit(gpa);
Expand Down Expand Up @@ -114,10 +118,10 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
.R_WASM_GLOBAL_INDEX_I32,
.R_WASM_MEMORY_ADDR_I32,
.R_WASM_SECTION_OFFSET_I32,
=> std.mem.writeInt(u32, atom.code.items[reloc.offset..][0..4], @as(u32, @intCast(value)), .little),
=> std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @intCast(value)), .little),
.R_WASM_TABLE_INDEX_I64,
.R_WASM_MEMORY_ADDR_I64,
=> std.mem.writeInt(u64, atom.code.items[reloc.offset..][0..8], value, .little),
=> std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little),
.R_WASM_GLOBAL_INDEX_LEB,
.R_WASM_EVENT_INDEX_LEB,
.R_WASM_FUNCTION_INDEX_LEB,
Expand All @@ -127,12 +131,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
.R_WASM_TABLE_NUMBER_LEB,
.R_WASM_TYPE_INDEX_LEB,
.R_WASM_MEMORY_ADDR_TLS_SLEB,
=> leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @as(u32, @intCast(value))),
=> leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @intCast(value))),
.R_WASM_MEMORY_ADDR_LEB64,
.R_WASM_MEMORY_ADDR_SLEB64,
.R_WASM_TABLE_INDEX_SLEB64,
.R_WASM_MEMORY_ADDR_TLS_SLEB64,
=> leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),
=> leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value),
}
}
}
Expand All @@ -150,7 +154,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
.R_WASM_TABLE_INDEX_I64,
.R_WASM_TABLE_INDEX_SLEB,
.R_WASM_TABLE_INDEX_SLEB64,
=> return wasm_bin.function_table.get(target_loc) orelse 0,
=> return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0,
.R_WASM_TYPE_INDEX_LEB => {
const file_index = atom.file orelse {
return relocation.index;
Expand Down
264 changes: 122 additions & 142 deletions src/link/Wasm/Object.zig

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions src/link/Wasm/Symbol.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pub const Flag = enum(u32) {
WASM_SYM_NO_STRIP = 0x80,
/// Indicates a symbol is TLS
WASM_SYM_TLS = 0x100,
/// Zig specific flag. Uses the most significant bit of the flag to annotate whether a symbol is
/// alive or not. Dead symbols are allowed to be garbage collected.
alive = 0x80000000,
};

/// Verifies if the given symbol should be imported from the
Expand All @@ -92,6 +95,23 @@ pub fn requiresImport(symbol: Symbol) bool {
return true;
}

/// Marks a symbol as 'alive', ensuring the garbage collector will not collect the trash.
pub fn mark(symbol: *Symbol) void {
symbol.flags |= @intFromEnum(Flag.alive);
}

pub fn unmark(symbol: *Symbol) void {
symbol.flags &= ~@intFromEnum(Flag.alive);
}

pub fn isAlive(symbol: Symbol) bool {
return symbol.flags & @intFromEnum(Flag.alive) != 0;
}

pub fn isDead(symbol: Symbol) bool {
return symbol.flags & @intFromEnum(Flag.alive) == 0;
}

pub fn isTLS(symbol: Symbol) bool {
return symbol.flags & @intFromEnum(Flag.WASM_SYM_TLS) != 0;
}
Expand Down
2 changes: 2 additions & 0 deletions test/link/wasm/bss/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
lib.strip = false;
// to make sure the bss segment is emitted, we must import memory
lib.import_memory = true;
lib.link_gc_sections = false;

const check_lib = lib.checkObject();

Expand Down Expand Up @@ -73,6 +74,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
lib.strip = false;
// to make sure the bss segment is emitted, we must import memory
lib.import_memory = true;
lib.link_gc_sections = false;

const check_lib = lib.checkObject();
check_lib.checkStart();
Expand Down
3 changes: 3 additions & 0 deletions test/link/wasm/function-table/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
import_table.use_llvm = false;
import_table.use_lld = false;
import_table.import_table = true;
import_table.link_gc_sections = false;

const export_table = b.addExecutable(.{
.name = "export_table",
Expand All @@ -34,6 +35,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
export_table.use_llvm = false;
export_table.use_lld = false;
export_table.export_table = true;
export_table.link_gc_sections = false;

const regular_table = b.addExecutable(.{
.name = "regular_table",
Expand All @@ -44,6 +46,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
regular_table.entry = .disabled;
regular_table.use_llvm = false;
regular_table.use_lld = false;
regular_table.link_gc_sections = false; // Ensure function table is not empty

const check_import = import_table.checkObject();
const check_export = export_table.checkObject();
Expand Down
1 change: 1 addition & 0 deletions test/link/wasm/segments/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
lib.use_llvm = false;
lib.use_lld = false;
lib.strip = false;
lib.link_gc_sections = false; // so data is not garbage collected and we can verify data section
b.installArtifact(lib);

const check_lib = lib.checkObject();
Expand Down
1 change: 1 addition & 0 deletions test/link/wasm/stack_pointer/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
lib.use_lld = false;
lib.strip = false;
lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size
lib.link_gc_sections = false;
b.installArtifact(lib);

const check_lib = lib.checkObject();
Expand Down

0 comments on commit cd7ac56

Please sign in to comment.