aboutsummaryrefslogtreecommitdiff
path: root/src/codegen.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/codegen.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/codegen.zig')
-rw-r--r--src/codegen.zig71
1 files changed, 55 insertions, 16 deletions
diff --git a/src/codegen.zig b/src/codegen.zig
index a70fc8642d..8ab6fb33b6 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -25,18 +25,19 @@ const Alignment = InternPool.Alignment;
const dev = @import("dev.zig");
pub const Result = union(enum) {
- /// The `code` parameter passed to `generateSymbol` has the value ok.
+ /// The `code` parameter passed to `generateSymbol` has the value.
ok,
-
/// There was a codegen error.
fail: *ErrorMsg,
};
pub const CodeGenError = error{
OutOfMemory,
+ /// Compiler was asked to operate on a number larger than supported.
Overflow,
+ /// Indicates the error is already stored in Zcu `failed_codegen`.
CodegenFail,
-} || link.File.UpdateDebugInfoError;
+};
fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature {
comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_"));
@@ -49,7 +50,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type {
.stage2_arm => @import("arch/arm/CodeGen.zig"),
.stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),
.stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),
- .stage2_wasm => @import("arch/wasm/CodeGen.zig"),
.stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"),
else => unreachable,
};
@@ -74,7 +74,6 @@ pub fn generateFunction(
.stage2_arm,
.stage2_riscv64,
.stage2_sparc64,
- .stage2_wasm,
.stage2_x86_64,
=> |backend| {
dev.check(devFeatureForBackend(backend));
@@ -96,9 +95,7 @@ pub fn generateLazyFunction(
const target = zcu.fileByIndex(file).mod.resolved_target.result;
switch (target_util.zigBackend(target, false)) {
else => unreachable,
- inline .stage2_x86_64,
- .stage2_riscv64,
- => |backend| {
+ inline .stage2_x86_64, .stage2_riscv64 => |backend| {
dev.check(devFeatureForBackend(backend));
return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
},
@@ -694,6 +691,7 @@ fn lowerUavRef(
offset: u64,
) CodeGenError!Result {
const zcu = pt.zcu;
+ const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
const target = lf.comp.root_mod.resolved_target.result;
@@ -704,7 +702,7 @@ fn lowerUavRef(
const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {
try code.appendNTimes(0xaa, ptr_width_bytes);
- return Result.ok;
+ return .ok;
}
const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;
@@ -714,6 +712,26 @@ fn lowerUavRef(
.fail => |em| return .{ .fail = em },
}
+ switch (lf.tag) {
+ .c => unreachable,
+ .spirv => unreachable,
+ .nvptx => unreachable,
+ .wasm => {
+ dev.check(link.File.Tag.wasm.devFeature());
+ const wasm = lf.cast(.wasm).?;
+ assert(reloc_parent == .none);
+ try wasm.relocations.append(gpa, .{
+ .tag = .uav_index,
+ .addend = @intCast(offset),
+ .offset = @intCast(code.items.len),
+ .pointee = .{ .uav_index = uav.val },
+ });
+ try code.appendNTimes(0, ptr_width_bytes);
+ return .ok;
+ },
+ else => {},
+ }
+
const vaddr = try lf.getUavVAddr(uav_val, .{
.parent = reloc_parent,
.offset = code.items.len,
@@ -741,31 +759,52 @@ fn lowerNavRef(
) CodeGenError!Result {
_ = src_loc;
const zcu = pt.zcu;
+ const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
const target = zcu.navFileScope(nav_index).mod.resolved_target.result;
- const ptr_width = target.ptrBitWidth();
+ const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));
const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {
- try code.appendNTimes(0xaa, @divExact(ptr_width, 8));
+ try code.appendNTimes(0xaa, ptr_width_bytes);
return Result.ok;
}
+ switch (lf.tag) {
+ .c => unreachable,
+ .spirv => unreachable,
+ .nvptx => unreachable,
+ .wasm => {
+ dev.check(link.File.Tag.wasm.devFeature());
+ const wasm = lf.cast(.wasm).?;
+ assert(reloc_parent == .none);
+ try wasm.relocations.append(gpa, .{
+ .tag = .nav_index,
+ .addend = @intCast(offset),
+ .offset = @intCast(code.items.len),
+ .pointee = .{ .nav_index = nav_index },
+ });
+ try code.appendNTimes(0, ptr_width_bytes);
+ return .ok;
+ },
+ else => {},
+ }
+
const vaddr = try lf.getNavVAddr(pt, nav_index, .{
.parent = reloc_parent,
.offset = code.items.len,
.addend = @intCast(offset),
});
const endian = target.cpu.arch.endian();
- switch (ptr_width) {
- 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),
- 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),
- 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
+ switch (ptr_width_bytes) {
+ 2 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),
+ 4 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),
+ 8 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
else => unreachable,
}
- return Result.ok;
+ return .ok;
}
/// Helper struct to denote that the value is in memory but requires a linker relocation fixup: