diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-11-04 17:26:17 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-01-15 15:11:35 -0800 |
| commit | 795e7c64d5f67006246d172e5cd58233cb76f05e (patch) | |
| tree | 7d376eeb11a443a0861a360c9447f28a58bb618e /src/codegen.zig | |
| parent | 77273103a8f9895ceab28287dffcf4d4c6fcb91b (diff) | |
| download | zig-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.zig | 71 |
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: |
