aboutsummaryrefslogtreecommitdiff
path: root/src/link/Wasm/Symbol.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-11-04 17:26:17 -0800
committerAndrew Kelley <andrew@ziglang.org>2025-01-15 15:11:35 -0800
commit795e7c64d5f67006246d172e5cd58233cb76f05e (patch)
tree7d376eeb11a443a0861a360c9447f28a58bb618e /src/link/Wasm/Symbol.zig
parent77273103a8f9895ceab28287dffcf4d4c6fcb91b (diff)
downloadzig-795e7c64d5f67006246d172e5cd58233cb76f05e.tar.gz
zig-795e7c64d5f67006246d172e5cd58233cb76f05e.zip
wasm linker: aggressive DODification
The goals of this branch are to: * compile faster when using the wasm linker and backend * enable saving compiler state by directly copying in-memory linker state to disk. * more efficient compiler memory utilization * introduce integer type safety to wasm linker code * generate better WebAssembly code * fully participate in incremental compilation * do as much work as possible outside of flush(), while continuing to do linker garbage collection. * avoid unnecessary heap allocations * avoid unnecessary indirect function calls In order to accomplish this goals, this removes the ZigObject abstraction, as well as Symbol and Atom. These abstractions resulted in overly generic code, doing unnecessary work, and needless complications that simply go away by creating a better in-memory data model and emitting more things lazily. For example, this makes wasm codegen emit MIR which is then lowered to wasm code during linking, with optimal function indexes etc, or relocations are emitted if outputting an object. Previously, this would always emit relocations, which are fully unnecessary when emitting an executable, and required all function calls to use the maximum size LEB encoding. This branch introduces the concept of the "prelink" phase which occurs after all object files have been parsed, but before any Zcu updates are sent to the linker. This allows the linker to fully parse all objects into a compact memory model, which is guaranteed to be complete when Zcu code is generated. This commit is not a complete implementation of all these goals; it is not even passing semantic analysis.
Diffstat (limited to 'src/link/Wasm/Symbol.zig')
-rw-r--r--src/link/Wasm/Symbol.zig210
1 files changed, 0 insertions, 210 deletions
diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig
deleted file mode 100644
index b60b73c46f..0000000000
--- a/src/link/Wasm/Symbol.zig
+++ /dev/null
@@ -1,210 +0,0 @@
-//! Represents a WebAssembly symbol. Containing all of its properties,
-//! as well as providing helper methods to determine its functionality
-//! and how it will/must be linked.
-//! The name of the symbol can be found by providing the offset, found
-//! on the `name` field, to a string table in the wasm binary or object file.
-
-/// Bitfield containings flags for a symbol
-/// Can contain any of the flags defined in `Flag`
-flags: u32,
-/// Symbol name, when the symbol is undefined the name will be taken from the import.
-/// Note: This is an index into the wasm string table.
-name: wasm.String,
-/// Index into the list of objects based on set `tag`
-/// NOTE: This will be set to `undefined` when `tag` is `data`
-/// and the symbol is undefined.
-index: u32,
-/// Represents the kind of the symbol, such as a function or global.
-tag: Tag,
-/// Contains the virtual address of the symbol, relative to the start of its section.
-/// This differs from the offset of an `Atom` which is relative to the start of a segment.
-virtual_address: u32,
-
-/// Represents a symbol index where `null` represents an invalid index.
-pub const Index = enum(u32) {
- null,
- _,
-};
-
-pub const Tag = enum {
- function,
- data,
- global,
- section,
- event,
- table,
- /// synthetic kind used by the wasm linker during incremental compilation
- /// to notate a symbol has been freed, but still lives in the symbol list.
- dead,
- undefined,
-
- /// From a given symbol tag, returns the `ExternalType`
- /// Asserts the given tag can be represented as an external type.
- pub fn externalType(tag: Tag) std.wasm.ExternalKind {
- return switch (tag) {
- .function => .function,
- .global => .global,
- .data => unreachable, // Data symbols will generate a global
- .section => unreachable, // Not an external type
- .event => unreachable, // Not an external type
- .dead => unreachable, // Dead symbols should not be referenced
- .undefined => unreachable,
- .table => .table,
- };
- }
-};
-
-pub const Flag = enum(u32) {
- /// Indicates a weak symbol.
- /// When linking multiple modules defining the same symbol, all weak definitions are discarded
- /// in favourite of the strong definition. When no strong definition exists, all weak but one definition is discarded.
- /// If multiple definitions remain, we get an error: symbol collision.
- WASM_SYM_BINDING_WEAK = 0x1,
- /// Indicates a local, non-exported, non-module-linked symbol.
- /// The names of local symbols are not required to be unique, unlike non-local symbols.
- WASM_SYM_BINDING_LOCAL = 0x2,
- /// Represents the binding of a symbol, indicating if it's local or not, and weak or not.
- WASM_SYM_BINDING_MASK = 0x3,
- /// Indicates a hidden symbol. Hidden symbols will not be exported to the link result, but may
- /// link to other modules.
- WASM_SYM_VISIBILITY_HIDDEN = 0x4,
- /// Indicates an undefined symbol. For non-data symbols, this must match whether the symbol is
- /// an import or is defined. For data symbols however, determines whether a segment is specified.
- WASM_SYM_UNDEFINED = 0x10,
- /// Indicates a symbol of which its intention is to be exported from the wasm module to the host environment.
- /// This differs from the visibility flag as this flag affects the static linker.
- WASM_SYM_EXPORTED = 0x20,
- /// Indicates the symbol uses an explicit symbol name, rather than reusing the name from a wasm import.
- /// Allows remapping imports from foreign WASM modules into local symbols with a different name.
- WASM_SYM_EXPLICIT_NAME = 0x40,
- /// Indicates the symbol is to be included in the linker output, regardless of whether it is used or has any references to it.
- 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
-/// host environment or not
-pub fn requiresImport(symbol: Symbol) bool {
- if (symbol.tag == .data) return false;
- if (!symbol.isUndefined()) return false;
- if (symbol.isWeak()) return false;
- // if (symbol.isDefined() and symbol.isWeak()) return true; //TODO: Only when building shared lib
-
- 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;
-}
-
-pub fn hasFlag(symbol: Symbol, flag: Flag) bool {
- return symbol.flags & @intFromEnum(flag) != 0;
-}
-
-pub fn setFlag(symbol: *Symbol, flag: Flag) void {
- symbol.flags |= @intFromEnum(flag);
-}
-
-pub fn isUndefined(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_UNDEFINED) != 0;
-}
-
-pub fn setUndefined(symbol: *Symbol, is_undefined: bool) void {
- if (is_undefined) {
- symbol.setFlag(.WASM_SYM_UNDEFINED);
- } else {
- symbol.flags &= ~@intFromEnum(Flag.WASM_SYM_UNDEFINED);
- }
-}
-
-pub fn setGlobal(symbol: *Symbol, is_global: bool) void {
- if (is_global) {
- symbol.flags &= ~@intFromEnum(Flag.WASM_SYM_BINDING_LOCAL);
- } else {
- symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
- }
-}
-
-pub fn isDefined(symbol: Symbol) bool {
- return !symbol.isUndefined();
-}
-
-pub fn isVisible(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) == 0;
-}
-
-pub fn isLocal(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) != 0;
-}
-
-pub fn isGlobal(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) == 0;
-}
-
-pub fn isHidden(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) != 0;
-}
-
-pub fn isNoStrip(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_NO_STRIP) != 0;
-}
-
-pub fn isExported(symbol: Symbol, is_dynamic: bool) bool {
- if (symbol.isUndefined() or symbol.isLocal()) return false;
- if (is_dynamic and symbol.isVisible()) return true;
- return symbol.hasFlag(.WASM_SYM_EXPORTED);
-}
-
-pub fn isWeak(symbol: Symbol) bool {
- return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_WEAK) != 0;
-}
-
-/// Formats the symbol into human-readable text
-pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
-
- const kind_fmt: u8 = switch (symbol.tag) {
- .function => 'F',
- .data => 'D',
- .global => 'G',
- .section => 'S',
- .event => 'E',
- .table => 'T',
- .dead => '-',
- .undefined => unreachable,
- };
- const visible: []const u8 = if (symbol.isVisible()) "yes" else "no";
- const binding: []const u8 = if (symbol.isLocal()) "local" else "global";
- const undef: []const u8 = if (symbol.isUndefined()) "undefined" else "";
-
- try writer.print(
- "{c} binding={s} visible={s} id={d} name_offset={d} {s}",
- .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
- );
-}
-
-const std = @import("std");
-const Symbol = @This();
-const wasm = @import("../Wasm.zig");