authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-11-04 17:26:17-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
log795e7c64d5f67006246d172e5cd58233cb76f05e
tree7d376eeb11a443a0861a360c9447f28a58bb618e
parent77273103a8f9895ceab28287dffcf4d4c6fcb91b

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.

34 files changed, 4309 insertions(+), 7191 deletions(-)

CMakeLists.txt+1-1
...@@ -643,9 +643,9 @@ set(ZIG_STAGE2_SOURCES...@@ -643,9 +643,9 @@ set(ZIG_STAGE2_SOURCES
643 src/link/StringTable.zig643 src/link/StringTable.zig
644 src/link/Wasm.zig644 src/link/Wasm.zig
645 src/link/Wasm/Archive.zig645 src/link/Wasm/Archive.zig
646 src/link/Wasm/Flush.zig
646 src/link/Wasm/Object.zig647 src/link/Wasm/Object.zig
647 src/link/Wasm/Symbol.zig648 src/link/Wasm/Symbol.zig
648 src/link/Wasm/ZigObject.zig
649 src/link/aarch64.zig649 src/link/aarch64.zig
650 src/link/riscv.zig650 src/link/riscv.zig
651 src/link/table_section.zig651 src/link/table_section.zig
lib/std/Build/Step/CheckObject.zig+1-1
...@@ -2682,7 +2682,7 @@ const WasmDumper = struct {...@@ -2682,7 +2682,7 @@ const WasmDumper = struct {
2682 else => unreachable,2682 else => unreachable,
2683 }2683 }
2684 const end_opcode = try std.leb.readUleb128(u8, reader);2684 const end_opcode = try std.leb.readUleb128(u8, reader);
2685 if (end_opcode != std.wasm.opcode(.end)) {2685 if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) {
2686 return step.fail("expected 'end' opcode in init expression", .{});2686 return step.fail("expected 'end' opcode in init expression", .{});
2687 }2687 }
2688 }2688 }
lib/std/wasm.zig+5-179
...@@ -4,8 +4,6 @@...@@ -4,8 +4,6 @@
4const std = @import("std.zig");4const std = @import("std.zig");
5const testing = std.testing;5const testing = std.testing;
66
7// TODO: Add support for multi-byte ops (e.g. table operations)
8
9/// Wasm instruction opcodes7/// Wasm instruction opcodes
10///8///
11/// All instructions are defined as per spec:9/// All instructions are defined as per spec:
...@@ -195,27 +193,6 @@ pub const Opcode = enum(u8) {...@@ -195,27 +193,6 @@ pub const Opcode = enum(u8) {
195 _,193 _,
196};194};
197195
198/// Returns the integer value of an `Opcode`. Used by the Zig compiler
199/// to write instructions to the wasm binary file
200pub fn opcode(op: Opcode) u8 {
201 return @intFromEnum(op);
202}
203
204test "opcodes" {
205 // Ensure our opcodes values remain intact as certain values are skipped due to them being reserved
206 const i32_const = opcode(.i32_const);
207 const end = opcode(.end);
208 const drop = opcode(.drop);
209 const local_get = opcode(.local_get);
210 const i64_extend32_s = opcode(.i64_extend32_s);
211
212 try testing.expectEqual(@as(u16, 0x41), i32_const);
213 try testing.expectEqual(@as(u16, 0x0B), end);
214 try testing.expectEqual(@as(u16, 0x1A), drop);
215 try testing.expectEqual(@as(u16, 0x20), local_get);
216 try testing.expectEqual(@as(u16, 0xC4), i64_extend32_s);
217}
218
219/// Opcodes that require a prefix `0xFC`.196/// Opcodes that require a prefix `0xFC`.
220/// Each opcode represents a varuint32, meaning197/// Each opcode represents a varuint32, meaning
221/// they are encoded as leb128 in binary.198/// they are encoded as leb128 in binary.
...@@ -241,12 +218,6 @@ pub const MiscOpcode = enum(u32) {...@@ -241,12 +218,6 @@ pub const MiscOpcode = enum(u32) {
241 _,218 _,
242};219};
243220
244/// Returns the integer value of an `MiscOpcode`. Used by the Zig compiler
245/// to write instructions to the wasm binary file
246pub fn miscOpcode(op: MiscOpcode) u32 {
247 return @intFromEnum(op);
248}
249
250/// Simd opcodes that require a prefix `0xFD`.221/// Simd opcodes that require a prefix `0xFD`.
251/// Each opcode represents a varuint32, meaning222/// Each opcode represents a varuint32, meaning
252/// they are encoded as leb128 in binary.223/// they are encoded as leb128 in binary.
...@@ -512,12 +483,6 @@ pub const SimdOpcode = enum(u32) {...@@ -512,12 +483,6 @@ pub const SimdOpcode = enum(u32) {
512 f32x4_relaxed_dot_bf16x8_add_f32x4 = 0x114,483 f32x4_relaxed_dot_bf16x8_add_f32x4 = 0x114,
513};484};
514485
515/// Returns the integer value of an `SimdOpcode`. Used by the Zig compiler
516/// to write instructions to the wasm binary file
517pub fn simdOpcode(op: SimdOpcode) u32 {
518 return @intFromEnum(op);
519}
520
521/// Atomic opcodes that require a prefix `0xFE`.486/// Atomic opcodes that require a prefix `0xFE`.
522/// Each opcode represents a varuint32, meaning487/// Each opcode represents a varuint32, meaning
523/// they are encoded as leb128 in binary.488/// they are encoded as leb128 in binary.
...@@ -592,12 +557,6 @@ pub const AtomicsOpcode = enum(u32) {...@@ -592,12 +557,6 @@ pub const AtomicsOpcode = enum(u32) {
592 i64_atomic_rmw32_cmpxchg_u = 0x4E,557 i64_atomic_rmw32_cmpxchg_u = 0x4E,
593};558};
594559
595/// Returns the integer value of an `AtomicsOpcode`. Used by the Zig compiler
596/// to write instructions to the wasm binary file
597pub fn atomicsOpcode(op: AtomicsOpcode) u32 {
598 return @intFromEnum(op);
599}
600
601/// Enum representing all Wasm value types as per spec:560/// Enum representing all Wasm value types as per spec:
602/// https://webassembly.github.io/spec/core/binary/types.html561/// https://webassembly.github.io/spec/core/binary/types.html
603pub const Valtype = enum(u8) {562pub const Valtype = enum(u8) {
...@@ -608,11 +567,6 @@ pub const Valtype = enum(u8) {...@@ -608,11 +567,6 @@ pub const Valtype = enum(u8) {
608 v128 = 0x7B,567 v128 = 0x7B,
609};568};
610569
611/// Returns the integer value of a `Valtype`
612pub fn valtype(value: Valtype) u8 {
613 return @intFromEnum(value);
614}
615
616/// Reference types, where the funcref references to a function regardless of its type570/// Reference types, where the funcref references to a function regardless of its type
617/// and ref references an object from the embedder.571/// and ref references an object from the embedder.
618pub const RefType = enum(u8) {572pub const RefType = enum(u8) {
...@@ -620,41 +574,17 @@ pub const RefType = enum(u8) {...@@ -620,41 +574,17 @@ pub const RefType = enum(u8) {
620 externref = 0x6F,574 externref = 0x6F,
621};575};
622576
623/// Returns the integer value of a `Reftype`
624pub fn reftype(value: RefType) u8 {
625 return @intFromEnum(value);
626}
627
628test "valtypes" {
629 const _i32 = valtype(.i32);
630 const _i64 = valtype(.i64);
631 const _f32 = valtype(.f32);
632 const _f64 = valtype(.f64);
633
634 try testing.expectEqual(@as(u8, 0x7F), _i32);
635 try testing.expectEqual(@as(u8, 0x7E), _i64);
636 try testing.expectEqual(@as(u8, 0x7D), _f32);
637 try testing.expectEqual(@as(u8, 0x7C), _f64);
638}
639
640/// Limits classify the size range of resizeable storage associated with memory types and table types.577/// Limits classify the size range of resizeable storage associated with memory types and table types.
641pub const Limits = struct {578pub const Limits = struct {
642 flags: u8,579 flags: Flags,
643 min: u32,580 min: u32,
644 max: u32,581 max: u32,
645582
646 pub const Flags = enum(u8) {583 pub const Flags = packed struct(u8) {
647 WASM_LIMITS_FLAG_HAS_MAX = 0x1,584 has_max: bool,
648 WASM_LIMITS_FLAG_IS_SHARED = 0x2,585 is_shared: bool,
586 reserved: u6 = 0,
649 };587 };
650
651 pub fn hasFlag(limits: Limits, flag: Flags) bool {
652 return limits.flags & @intFromEnum(flag) != 0;
653 }
654
655 pub fn setFlag(limits: *Limits, flag: Flags) void {
656 limits.flags |= @intFromEnum(flag);
657 }
658};588};
659589
660/// Initialization expressions are used to set the initial value on an object590/// Initialization expressions are used to set the initial value on an object
...@@ -667,18 +597,6 @@ pub const InitExpression = union(enum) {...@@ -667,18 +597,6 @@ pub const InitExpression = union(enum) {
667 global_get: u32,597 global_get: u32,
668};598};
669599
670/// Represents a function entry, holding the index to its type
671pub const Func = struct {
672 type_index: u32,
673};
674
675/// Tables are used to hold pointers to opaque objects.
676/// This can either by any function, or an object from the host.
677pub const Table = struct {
678 limits: Limits,
679 reftype: RefType,
680};
681
682/// Describes the layout of the memory where `min` represents600/// Describes the layout of the memory where `min` represents
683/// the minimal amount of pages, and the optional `max` represents601/// the minimal amount of pages, and the optional `max` represents
684/// the max pages. When `null` will allow the host to determine the602/// the max pages. When `null` will allow the host to determine the
...@@ -687,88 +605,6 @@ pub const Memory = struct {...@@ -687,88 +605,6 @@ pub const Memory = struct {
687 limits: Limits,605 limits: Limits,
688};606};
689607
690/// Represents the type of a `Global` or an imported global.
691pub const GlobalType = struct {
692 valtype: Valtype,
693 mutable: bool,
694};
695
696pub const Global = struct {
697 global_type: GlobalType,
698 init: InitExpression,
699};
700
701/// Notates an object to be exported from wasm
702/// to the host.
703pub const Export = struct {
704 name: []const u8,
705 kind: ExternalKind,
706 index: u32,
707};
708
709/// Element describes the layout of the table that can
710/// be found at `table_index`
711pub const Element = struct {
712 table_index: u32,
713 offset: InitExpression,
714 func_indexes: []const u32,
715};
716
717/// Imports are used to import objects from the host
718pub const Import = struct {
719 module_name: []const u8,
720 name: []const u8,
721 kind: Kind,
722
723 pub const Kind = union(ExternalKind) {
724 function: u32,
725 table: Table,
726 memory: Limits,
727 global: GlobalType,
728 };
729};
730
731/// `Type` represents a function signature type containing both
732/// a slice of parameters as well as a slice of return values.
733pub const Type = struct {
734 params: []const Valtype,
735 returns: []const Valtype,
736
737 pub fn format(self: Type, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
738 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
739 _ = opt;
740 try writer.writeByte('(');
741 for (self.params, 0..) |param, i| {
742 try writer.print("{s}", .{@tagName(param)});
743 if (i + 1 != self.params.len) {
744 try writer.writeAll(", ");
745 }
746 }
747 try writer.writeAll(") -> ");
748 if (self.returns.len == 0) {
749 try writer.writeAll("nil");
750 } else {
751 for (self.returns, 0..) |return_ty, i| {
752 try writer.print("{s}", .{@tagName(return_ty)});
753 if (i + 1 != self.returns.len) {
754 try writer.writeAll(", ");
755 }
756 }
757 }
758 }
759
760 pub fn eql(self: Type, other: Type) bool {
761 return std.mem.eql(Valtype, self.params, other.params) and
762 std.mem.eql(Valtype, self.returns, other.returns);
763 }
764
765 pub fn deinit(self: *Type, gpa: std.mem.Allocator) void {
766 gpa.free(self.params);
767 gpa.free(self.returns);
768 self.* = undefined;
769 }
770};
771
772/// Wasm module sections as per spec:608/// Wasm module sections as per spec:
773/// https://webassembly.github.io/spec/core/binary/modules.html609/// https://webassembly.github.io/spec/core/binary/modules.html
774pub const Section = enum(u8) {610pub const Section = enum(u8) {
...@@ -788,11 +624,6 @@ pub const Section = enum(u8) {...@@ -788,11 +624,6 @@ pub const Section = enum(u8) {
788 _,624 _,
789};625};
790626
791/// Returns the integer value of a given `Section`
792pub fn section(val: Section) u8 {
793 return @intFromEnum(val);
794}
795
796/// The kind of the type when importing or exporting to/from the host environment.627/// The kind of the type when importing or exporting to/from the host environment.
797/// https://webassembly.github.io/spec/core/syntax/modules.html628/// https://webassembly.github.io/spec/core/syntax/modules.html
798pub const ExternalKind = enum(u8) {629pub const ExternalKind = enum(u8) {
...@@ -802,11 +633,6 @@ pub const ExternalKind = enum(u8) {...@@ -802,11 +633,6 @@ pub const ExternalKind = enum(u8) {
802 global,633 global,
803};634};
804635
805/// Returns the integer value of a given `ExternalKind`
806pub fn externalKind(val: ExternalKind) u8 {
807 return @intFromEnum(val);
808}
809
810/// Defines the enum values for each subsection id for the "Names" custom section636/// Defines the enum values for each subsection id for the "Names" custom section
811/// as described by:637/// as described by:
812/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section638/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section
src/Compilation.zig+2-3
...@@ -2438,9 +2438,8 @@ fn flush(...@@ -2438,9 +2438,8 @@ fn flush(
2438 if (comp.bin_file) |lf| {2438 if (comp.bin_file) |lf| {
2439 // This is needed before reading the error flags.2439 // This is needed before reading the error flags.
2440 lf.flush(arena, tid, prog_node) catch |err| switch (err) {2440 lf.flush(arena, tid, prog_node) catch |err| switch (err) {
2441 error.FlushFailure, error.LinkFailure => {}, // error reported through link_diags.flags2441 error.LinkFailure => {}, // Already reported.
2442 error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr2442 error.OutOfMemory => return error.OutOfMemory,
2443 else => |e| return e,
2444 };2443 };
2445 }2444 }
24462445
src/Zcu.zig+9
...@@ -524,6 +524,15 @@ pub const Export = struct {...@@ -524,6 +524,15 @@ pub const Export = struct {
524 section: InternPool.OptionalNullTerminatedString = .none,524 section: InternPool.OptionalNullTerminatedString = .none,
525 visibility: std.builtin.SymbolVisibility = .default,525 visibility: std.builtin.SymbolVisibility = .default,
526 };526 };
527
528 /// Index into `all_exports`.
529 pub const Index = enum(u32) {
530 _,
531
532 pub fn ptr(i: Index, zcu: *const Zcu) *Export {
533 return &zcu.all_exports.items[@intFromEnum(i)];
534 }
535 };
527};536};
528537
529pub const Reference = struct {538pub const Reference = struct {
src/Zcu/PerThread.zig+11-23
...@@ -1722,22 +1722,19 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai...@@ -1722,22 +1722,19 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
1722 // Correcting this failure will involve changing a type this function1722 // Correcting this failure will involve changing a type this function
1723 // depends on, hence triggering re-analysis of this function, so this1723 // depends on, hence triggering re-analysis of this function, so this
1724 // interacts correctly with incremental compilation.1724 // interacts correctly with incremental compilation.
1725 // TODO: do we need to mark this failure anywhere? I don't think so, since compilation
1726 // will fail due to the type error anyway.
1727 } else if (comp.bin_file) |lf| {1725 } else if (comp.bin_file) |lf| {
1728 lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {1726 lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
1729 error.OutOfMemory => return error.OutOfMemory,1727 error.OutOfMemory => return error.OutOfMemory,
1730 error.AnalysisFail => {1728 error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
1731 assert(zcu.failed_codegen.contains(nav_index));1729 error.LinkFailure => assert(comp.link_diags.hasErrors()),
1732 },1730 error.Overflow => {
1733 else => {1731 try zcu.failed_codegen.putNoClobber(nav_index, try Zcu.ErrorMsg.create(
1734 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
1735 gpa,1732 gpa,
1736 zcu.navSrcLoc(nav_index),1733 zcu.navSrcLoc(nav_index),
1737 "unable to codegen: {s}",1734 "unable to codegen: {s}",
1738 .{@errorName(err)},1735 .{@errorName(err)},
1739 ));1736 ));
1740 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));1737 // Not a retryable failure.
1741 },1738 },
1742 };1739 };
1743 } else if (zcu.llvm_object) |llvm_object| {1740 } else if (zcu.llvm_object) |llvm_object| {
...@@ -3100,6 +3097,7 @@ pub fn populateTestFunctions(...@@ -3100,6 +3097,7 @@ pub fn populateTestFunctions(
3100pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {3097pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {
3101 const zcu = pt.zcu;3098 const zcu = pt.zcu;
3102 const comp = zcu.comp;3099 const comp = zcu.comp;
3100 const gpa = zcu.gpa;
3103 const ip = &zcu.intern_pool;3101 const ip = &zcu.intern_pool;
31043102
3105 const nav = zcu.intern_pool.getNav(nav_index);3103 const nav = zcu.intern_pool.getNav(nav_index);
...@@ -3113,26 +3111,16 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error...@@ -3113,26 +3111,16 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
3113 } else if (comp.bin_file) |lf| {3111 } else if (comp.bin_file) |lf| {
3114 lf.updateNav(pt, nav_index) catch |err| switch (err) {3112 lf.updateNav(pt, nav_index) catch |err| switch (err) {
3115 error.OutOfMemory => return error.OutOfMemory,3113 error.OutOfMemory => return error.OutOfMemory,
3116 error.AnalysisFail => {3114 error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
3117 assert(zcu.failed_codegen.contains(nav_index));3115 error.LinkFailure => assert(comp.link_diags.hasErrors()),
3118 },3116 error.Overflow => {
3119 else => {3117 try zcu.failed_codegen.putNoClobber(nav_index, try Zcu.ErrorMsg.create(
3120 const gpa = zcu.gpa;
3121 try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1);
3122 zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, try Zcu.ErrorMsg.create(
3123 gpa,3118 gpa,
3124 zcu.navSrcLoc(nav_index),3119 zcu.navSrcLoc(nav_index),
3125 "unable to codegen: {s}",3120 "unable to codegen: {s}",
3126 .{@errorName(err)},3121 .{@errorName(err)},
3127 ));3122 ));
3128 if (nav.analysis != null) {3123 // Not a retryable failure.
3129 try zcu.retryable_failures.append(zcu.gpa, .wrap(.{ .nav_val = nav_index }));
3130 } else {
3131 // TODO: we don't have a way to indicate that this failure is retryable!
3132 // Since these are really rare, we could as a cop-out retry the whole build next update.
3133 // But perhaps we can do better...
3134 @panic("TODO: retryable failure codegenning non-declaration Nav");
3135 }
3136 },3124 },
3137 };3125 };
3138 } else if (zcu.llvm_object) |llvm_object| {3126 } else if (zcu.llvm_object) |llvm_object| {
src/arch/aarch64/CodeGen.zig+128-128
...@@ -167,7 +167,7 @@ const DbgInfoReloc = struct {...@@ -167,7 +167,7 @@ const DbgInfoReloc = struct {
167 name: [:0]const u8,167 name: [:0]const u8,
168 mcv: MCValue,168 mcv: MCValue,
169169
170 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {170 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) CodeGenError!void {
171 switch (reloc.tag) {171 switch (reloc.tag) {
172 .arg,172 .arg,
173 .dbg_arg_inline,173 .dbg_arg_inline,
...@@ -181,7 +181,7 @@ const DbgInfoReloc = struct {...@@ -181,7 +181,7 @@ const DbgInfoReloc = struct {
181 }181 }
182 }182 }
183183
184 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void {184 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) CodeGenError!void {
185 switch (function.debug_output) {185 switch (function.debug_output) {
186 .dwarf => |dw| {186 .dwarf => |dw| {
187 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {187 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
...@@ -209,7 +209,7 @@ const DbgInfoReloc = struct {...@@ -209,7 +209,7 @@ const DbgInfoReloc = struct {
209 }209 }
210 }210 }
211211
212 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {212 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) CodeGenError!void {
213 switch (function.debug_output) {213 switch (function.debug_output) {
214 .dwarf => |dwarf| {214 .dwarf => |dwarf| {
215 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {215 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
...@@ -395,13 +395,13 @@ pub fn generate(...@@ -395,13 +395,13 @@ pub fn generate(
395 try reloc.genDbgInfo(function);395 try reloc.genDbgInfo(function);
396 }396 }
397397
398 var mir = Mir{398 var mir: Mir = .{
399 .instructions = function.mir_instructions.toOwnedSlice(),399 .instructions = function.mir_instructions.toOwnedSlice(),
400 .extra = try function.mir_extra.toOwnedSlice(gpa),400 .extra = try function.mir_extra.toOwnedSlice(gpa),
401 };401 };
402 defer mir.deinit(gpa);402 defer mir.deinit(gpa);
403403
404 var emit = Emit{404 var emit: Emit = .{
405 .mir = mir,405 .mir = mir,
406 .bin_file = lf,406 .bin_file = lf,
407 .debug_output = debug_output,407 .debug_output = debug_output,
...@@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
723 .cmp_gt => try self.airCmp(inst, .gt),723 .cmp_gt => try self.airCmp(inst, .gt),
724 .cmp_neq => try self.airCmp(inst, .neq),724 .cmp_neq => try self.airCmp(inst, .neq),
725725
726 .cmp_vector => try self.airCmpVector(inst),726 .cmp_vector => try self.airCmpVector(inst),
727 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),727 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
728728
729 .alloc => try self.airAlloc(inst),729 .alloc => try self.airAlloc(inst),
...@@ -744,7 +744,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -744,7 +744,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
744 .fpext => try self.airFpext(inst),744 .fpext => try self.airFpext(inst),
745 .intcast => try self.airIntCast(inst),745 .intcast => try self.airIntCast(inst),
746 .trunc => try self.airTrunc(inst),746 .trunc => try self.airTrunc(inst),
747 .int_from_bool => try self.airIntFromBool(inst),747 .int_from_bool => try self.airIntFromBool(inst),
748 .is_non_null => try self.airIsNonNull(inst),748 .is_non_null => try self.airIsNonNull(inst),
749 .is_non_null_ptr => try self.airIsNonNullPtr(inst),749 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
750 .is_null => try self.airIsNull(inst),750 .is_null => try self.airIsNull(inst),
...@@ -756,7 +756,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -756,7 +756,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
756 .load => try self.airLoad(inst),756 .load => try self.airLoad(inst),
757 .loop => try self.airLoop(inst),757 .loop => try self.airLoop(inst),
758 .not => try self.airNot(inst),758 .not => try self.airNot(inst),
759 .int_from_ptr => try self.airIntFromPtr(inst),759 .int_from_ptr => try self.airIntFromPtr(inst),
760 .ret => try self.airRet(inst),760 .ret => try self.airRet(inst),
761 .ret_safe => try self.airRet(inst), // TODO761 .ret_safe => try self.airRet(inst), // TODO
762 .ret_load => try self.airRetLoad(inst),762 .ret_load => try self.airRetLoad(inst),
...@@ -765,8 +765,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -765,8 +765,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
765 .struct_field_ptr=> try self.airStructFieldPtr(inst),765 .struct_field_ptr=> try self.airStructFieldPtr(inst),
766 .struct_field_val=> try self.airStructFieldVal(inst),766 .struct_field_val=> try self.airStructFieldVal(inst),
767 .array_to_slice => try self.airArrayToSlice(inst),767 .array_to_slice => try self.airArrayToSlice(inst),
768 .float_from_int => try self.airFloatFromInt(inst),768 .float_from_int => try self.airFloatFromInt(inst),
769 .int_from_float => try self.airIntFromFloat(inst),769 .int_from_float => try self.airIntFromFloat(inst),
770 .cmpxchg_strong => try self.airCmpxchg(inst),770 .cmpxchg_strong => try self.airCmpxchg(inst),
771 .cmpxchg_weak => try self.airCmpxchg(inst),771 .cmpxchg_weak => try self.airCmpxchg(inst),
772 .atomic_rmw => try self.airAtomicRmw(inst),772 .atomic_rmw => try self.airAtomicRmw(inst),
...@@ -1107,7 +1107,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {...@@ -1107,7 +1107,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
1107/// Copies a value to a register without tracking the register. The register is not considered1107/// Copies a value to a register without tracking the register. The register is not considered
1108/// allocated. A second call to `copyToTmpRegister` may return the same register.1108/// allocated. A second call to `copyToTmpRegister` may return the same register.
1109/// This can have a side effect of spilling instructions to the stack to free up a register.1109/// This can have a side effect of spilling instructions to the stack to free up a register.
1110fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {1110fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) InnerError!Register {
1111 const raw_reg = try self.register_manager.allocReg(null, gp);1111 const raw_reg = try self.register_manager.allocReg(null, gp);
1112 const reg = self.registerAlias(raw_reg, ty);1112 const reg = self.registerAlias(raw_reg, ty);
1113 try self.genSetReg(ty, reg, mcv);1113 try self.genSetReg(ty, reg, mcv);
...@@ -1125,12 +1125,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa...@@ -1125,12 +1125,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa
1125 return MCValue{ .register = reg };1125 return MCValue{ .register = reg };
1126}1126}
11271127
1128fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {1128fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1129 const stack_offset = try self.allocMemPtr(inst);1129 const stack_offset = try self.allocMemPtr(inst);
1130 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });1130 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1131}1131}
11321132
1133fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {1133fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
1134 const pt = self.pt;1134 const pt = self.pt;
1135 const zcu = pt.zcu;1135 const zcu = pt.zcu;
1136 const result: MCValue = switch (self.ret_mcv) {1136 const result: MCValue = switch (self.ret_mcv) {
...@@ -1152,19 +1152,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1152,19 +1152,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1152 return self.finishAir(inst, result, .{ .none, .none, .none });1152 return self.finishAir(inst, result, .{ .none, .none, .none });
1153}1153}
11541154
1155fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {1155fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1156 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1156 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1157 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});1157 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
1158 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1158 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1159}1159}
11601160
1161fn airFpext(self: *Self, inst: Air.Inst.Index) !void {1161fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void {
1162 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1162 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1163 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});1163 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
1164 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1164 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1165}1165}
11661166
1167fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {1167fn airIntCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
1168 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1168 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1169 if (self.liveness.isUnused(inst))1169 if (self.liveness.isUnused(inst))
1170 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });1170 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
...@@ -1293,7 +1293,7 @@ fn trunc(...@@ -1293,7 +1293,7 @@ fn trunc(
1293 }1293 }
1294}1294}
12951295
1296fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {1296fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1297 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1297 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1298 const operand = try self.resolveInst(ty_op.operand);1298 const operand = try self.resolveInst(ty_op.operand);
1299 const operand_ty = self.typeOf(ty_op.operand);1299 const operand_ty = self.typeOf(ty_op.operand);
...@@ -1306,14 +1306,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -1306,14 +1306,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1306 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1306 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1307}1307}
13081308
1309fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void {1309fn airIntFromBool(self: *Self, inst: Air.Inst.Index) InnerError!void {
1310 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;1310 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
1311 const operand = try self.resolveInst(un_op);1311 const operand = try self.resolveInst(un_op);
1312 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;1312 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
1313 return self.finishAir(inst, result, .{ un_op, .none, .none });1313 return self.finishAir(inst, result, .{ un_op, .none, .none });
1314}1314}
13151315
1316fn airNot(self: *Self, inst: Air.Inst.Index) !void {1316fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
1317 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1317 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1318 const pt = self.pt;1318 const pt = self.pt;
1319 const zcu = pt.zcu;1319 const zcu = pt.zcu;
...@@ -1484,7 +1484,7 @@ fn minMax(...@@ -1484,7 +1484,7 @@ fn minMax(
1484 }1484 }
1485}1485}
14861486
1487fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {1487fn airMinMax(self: *Self, inst: Air.Inst.Index) InnerError!void {
1488 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];1488 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
1489 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;1489 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1490 const lhs_ty = self.typeOf(bin_op.lhs);1490 const lhs_ty = self.typeOf(bin_op.lhs);
...@@ -1502,7 +1502,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {...@@ -1502,7 +1502,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
1502 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1502 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1503}1503}
15041504
1505fn airSlice(self: *Self, inst: Air.Inst.Index) !void {1505fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
1506 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;1506 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1507 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1507 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1508 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1508 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
...@@ -2440,7 +2440,7 @@ fn ptrArithmetic(...@@ -2440,7 +2440,7 @@ fn ptrArithmetic(
2440 }2440 }
2441}2441}
24422442
2443fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {2443fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void {
2444 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2444 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2445 const lhs_ty = self.typeOf(bin_op.lhs);2445 const lhs_ty = self.typeOf(bin_op.lhs);
2446 const rhs_ty = self.typeOf(bin_op.rhs);2446 const rhs_ty = self.typeOf(bin_op.rhs);
...@@ -2490,7 +2490,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {...@@ -2490,7 +2490,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
2490 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2490 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2491}2491}
24922492
2493fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {2493fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void {
2494 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2494 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2495 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2495 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2496 const lhs_ty = self.typeOf(bin_op.lhs);2496 const lhs_ty = self.typeOf(bin_op.lhs);
...@@ -2505,25 +2505,25 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void...@@ -2505,25 +2505,25 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void
2505 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2505 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2506}2506}
25072507
2508fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {2508fn airAddSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2509 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2509 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2510 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});2510 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
2511 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2511 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2512}2512}
25132513
2514fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {2514fn airSubSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2515 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2515 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2516 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});2516 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
2517 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2517 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2518}2518}
25192519
2520fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {2520fn airMulSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2521 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2521 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2522 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});2522 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
2523 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2523 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2524}2524}
25252525
2526fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {2526fn airOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2527 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];2527 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
2528 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2528 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2529 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2529 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
...@@ -2536,9 +2536,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2536,9 +2536,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2536 const rhs_ty = self.typeOf(extra.rhs);2536 const rhs_ty = self.typeOf(extra.rhs);
25372537
2538 const tuple_ty = self.typeOfIndex(inst);2538 const tuple_ty = self.typeOfIndex(inst);
2539 const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu)));2539 const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
2540 const tuple_align = tuple_ty.abiAlignment(zcu);2540 const tuple_align = tuple_ty.abiAlignment(zcu);
2541 const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu)));2541 const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
25422542
2543 switch (lhs_ty.zigTypeTag(zcu)) {2543 switch (lhs_ty.zigTypeTag(zcu)) {
2544 .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),2544 .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
...@@ -2652,7 +2652,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2652,7 +2652,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2652 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2652 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2653}2653}
26542654
2655fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {2655fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2656 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2656 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2657 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2657 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2658 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });2658 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
...@@ -2876,7 +2876,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2876,7 +2876,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2876 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2876 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2877}2877}
28782878
2879fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {2879fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2880 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2880 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2881 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2881 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2882 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });2882 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
...@@ -3012,13 +3012,13 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -3012,13 +3012,13 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
3012 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3012 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3013}3013}
30143014
3015fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {3015fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
3016 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3016 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3017 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});3017 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
3018 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3018 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3019}3019}
30203020
3021fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {3021fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3022 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3022 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3024 const optional_ty = self.typeOf(ty_op.operand);3024 const optional_ty = self.typeOf(ty_op.operand);
...@@ -3055,13 +3055,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty:...@@ -3055,13 +3055,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty:
3055 }3055 }
3056}3056}
30573057
3058fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {3058fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3059 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3059 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3060 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});3060 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
3061 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3061 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3062}3062}
30633063
3064fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {3064fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3065 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3065 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3066 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});3066 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
3067 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3067 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -3137,7 +3137,7 @@ fn errUnionErr(...@@ -3137,7 +3137,7 @@ fn errUnionErr(
3137 }3137 }
3138}3138}
31393139
3140fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {3140fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3141 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3141 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3142 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3142 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3143 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };3143 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
...@@ -3218,7 +3218,7 @@ fn errUnionPayload(...@@ -3218,7 +3218,7 @@ fn errUnionPayload(
3218 }3218 }
3219}3219}
32203220
3221fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {3221fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3222 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3222 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3223 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3223 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3224 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };3224 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
...@@ -3230,26 +3230,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -3230,26 +3230,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
3230}3230}
32313231
3232// *(E!T) -> E3232// *(E!T) -> E
3233fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {3233fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3234 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3234 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3235 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});3235 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
3236 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3236 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3237}3237}
32383238
3239// *(E!T) -> *T3239// *(E!T) -> *T
3240fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {3240fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3241 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3241 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3242 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});3242 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
3243 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3243 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3244}3244}
32453245
3246fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {3246fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3247 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3247 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3248 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});3248 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
3249 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3249 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3250}3250}
32513251
3252fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {3252fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
3253 const result: MCValue = if (self.liveness.isUnused(inst))3253 const result: MCValue = if (self.liveness.isUnused(inst))
3254 .dead3254 .dead
3255 else3255 else
...@@ -3257,17 +3257,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {...@@ -3257,17 +3257,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
3257 return self.finishAir(inst, result, .{ .none, .none, .none });3257 return self.finishAir(inst, result, .{ .none, .none, .none });
3258}3258}
32593259
3260fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {3260fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
3261 _ = inst;3261 _ = inst;
3262 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});3262 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
3263}3263}
32643264
3265fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {3265fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) InnerError!void {
3266 _ = inst;3266 _ = inst;
3267 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});3267 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
3268}3268}
32693269
3270fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {3270fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void {
3271 const pt = self.pt;3271 const pt = self.pt;
3272 const zcu = pt.zcu;3272 const zcu = pt.zcu;
3273 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3273 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -3313,7 +3313,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {...@@ -3313,7 +3313,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
3313}3313}
33143314
3315/// T to E!T3315/// T to E!T
3316fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {3316fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3317 const pt = self.pt;3317 const pt = self.pt;
3318 const zcu = pt.zcu;3318 const zcu = pt.zcu;
3319 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3319 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -3338,7 +3338,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -3338,7 +3338,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
3338}3338}
33393339
3340/// E to E!T3340/// E to E!T
3341fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {3341fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3342 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3342 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3343 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3343 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3344 const pt = self.pt;3344 const pt = self.pt;
...@@ -3379,7 +3379,7 @@ fn slicePtr(mcv: MCValue) MCValue {...@@ -3379,7 +3379,7 @@ fn slicePtr(mcv: MCValue) MCValue {
3379 }3379 }
3380}3380}
33813381
3382fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {3382fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3383 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3383 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3384 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3384 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3385 const mcv = try self.resolveInst(ty_op.operand);3385 const mcv = try self.resolveInst(ty_op.operand);
...@@ -3388,7 +3388,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3388,7 +3388,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
3388 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3388 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3389}3389}
33903390
3391fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {3391fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
3392 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3392 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3393 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3393 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3394 const ptr_bits = 64;3394 const ptr_bits = 64;
...@@ -3412,7 +3412,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -3412,7 +3412,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
3412 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3412 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3413}3413}
34143414
3415fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {3415fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3416 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3416 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3418 const ptr_bits = 64;3418 const ptr_bits = 64;
...@@ -3429,7 +3429,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3429,7 +3429,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
3429 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3429 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3430}3430}
34313431
3432fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {3432fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3433 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3433 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3434 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3434 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3435 const mcv = try self.resolveInst(ty_op.operand);3435 const mcv = try self.resolveInst(ty_op.operand);
...@@ -3444,7 +3444,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3444,7 +3444,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
3444 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3444 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3445}3445}
34463446
3447fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {3447fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3448 const pt = self.pt;3448 const pt = self.pt;
3449 const zcu = pt.zcu;3449 const zcu = pt.zcu;
3450 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3450 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -3487,7 +3487,7 @@ fn ptrElemVal(...@@ -3487,7 +3487,7 @@ fn ptrElemVal(
3487 }3487 }
3488}3488}
34893489
3490fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {3490fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3491 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3491 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3492 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3492 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3493 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3493 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
...@@ -3506,13 +3506,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3506,13 +3506,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
3506 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3506 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3507}3507}
35083508
3509fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {3509fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3510 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3510 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3511 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});3511 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
3512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3513}3513}
35143514
3515fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {3515fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3516 const pt = self.pt;3516 const pt = self.pt;
3517 const zcu = pt.zcu;3517 const zcu = pt.zcu;
3518 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3518 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -3526,7 +3526,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3526,7 +3526,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
3526 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3526 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3527}3527}
35283528
3529fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {3529fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3530 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3530 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3531 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3531 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3532 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3532 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
...@@ -3542,55 +3542,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3542,55 +3542,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
3542 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3542 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3543}3543}
35443544
3545fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {3545fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
3546 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3546 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3547 _ = bin_op;3547 _ = bin_op;
3548 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});3548 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
3549}3549}
35503550
3551fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {3551fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
3552 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3552 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3553 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});3553 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
3554 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3554 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3555}3555}
35563556
3557fn airClz(self: *Self, inst: Air.Inst.Index) !void {3557fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
3558 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3558 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3559 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});3559 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
3560 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3560 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3561}3561}
35623562
3563fn airCtz(self: *Self, inst: Air.Inst.Index) !void {3563fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
3564 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3564 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3565 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});3565 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
3566 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3566 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3567}3567}
35683568
3569fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {3569fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
3570 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3570 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3571 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});3571 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
3572 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3572 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3573}3573}
35743574
3575fn airAbs(self: *Self, inst: Air.Inst.Index) !void {3575fn airAbs(self: *Self, inst: Air.Inst.Index) InnerError!void {
3576 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3576 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3577 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});3577 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
3578 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3578 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3579}3579}
35803580
3581fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {3581fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void {
3582 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3582 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3583 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});3583 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
3584 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3584 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3585}3585}
35863586
3587fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {3587fn airBitReverse(self: *Self, inst: Air.Inst.Index) InnerError!void {
3588 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3588 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3589 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});3589 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
3590 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3590 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3591}3591}
35923592
3593fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {3593fn airUnaryMath(self: *Self, inst: Air.Inst.Index) InnerError!void {
3594 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3594 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3595 const result: MCValue = if (self.liveness.isUnused(inst))3595 const result: MCValue = if (self.liveness.isUnused(inst))
3596 .dead3596 .dead
...@@ -3885,7 +3885,7 @@ fn genInlineMemsetCode(...@@ -3885,7 +3885,7 @@ fn genInlineMemsetCode(
3885 // end:3885 // end:
3886}3886}
38873887
3888fn airLoad(self: *Self, inst: Air.Inst.Index) !void {3888fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
3889 const pt = self.pt;3889 const pt = self.pt;
3890 const zcu = pt.zcu;3890 const zcu = pt.zcu;
3891 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3891 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -4086,7 +4086,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4086,7 +4086,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4086 }4086 }
4087}4087}
40884088
4089fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {4089fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
4090 if (safety) {4090 if (safety) {
4091 // TODO if the value is undef, write 0xaa bytes to dest4091 // TODO if the value is undef, write 0xaa bytes to dest
4092 } else {4092 } else {
...@@ -4103,14 +4103,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {...@@ -4103,14 +4103,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
4103 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });4103 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
4104}4104}
41054105
4106fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {4106fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4107 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4107 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4108 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;4108 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4109 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);4109 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
4110 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });4110 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
4111}4111}
41124112
4113fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {4113fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) InnerError!void {
4114 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4114 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4115 const result = try self.structFieldPtr(inst, ty_op.operand, index);4115 const result = try self.structFieldPtr(inst, ty_op.operand, index);
4116 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });4116 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -4138,7 +4138,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde...@@ -4138,7 +4138,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
4138 };4138 };
4139}4139}
41404140
4141fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {4141fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
4142 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4142 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4143 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;4143 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4144 const operand = extra.struct_operand;4144 const operand = extra.struct_operand;
...@@ -4194,7 +4194,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -4194,7 +4194,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
4194 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });4194 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
4195}4195}
41964196
4197fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {4197fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4198 const pt = self.pt;4198 const pt = self.pt;
4199 const zcu = pt.zcu;4199 const zcu = pt.zcu;
4200 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4200 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
...@@ -4218,7 +4218,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4218,7 +4218,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
4218 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });4218 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
4219}4219}
42204220
4221fn airArg(self: *Self, inst: Air.Inst.Index) !void {4221fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
4222 // skip zero-bit arguments as they don't have a corresponding arg instruction4222 // skip zero-bit arguments as they don't have a corresponding arg instruction
4223 var arg_index = self.arg_index;4223 var arg_index = self.arg_index;
4224 while (self.args[arg_index] == .none) arg_index += 1;4224 while (self.args[arg_index] == .none) arg_index += 1;
...@@ -4238,7 +4238,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -4238,7 +4238,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4238 return self.finishAir(inst, result, .{ .none, .none, .none });4238 return self.finishAir(inst, result, .{ .none, .none, .none });
4239}4239}
42404240
4241fn airTrap(self: *Self) !void {4241fn airTrap(self: *Self) InnerError!void {
4242 _ = try self.addInst(.{4242 _ = try self.addInst(.{
4243 .tag = .brk,4243 .tag = .brk,
4244 .data = .{ .imm16 = 0x0001 },4244 .data = .{ .imm16 = 0x0001 },
...@@ -4246,7 +4246,7 @@ fn airTrap(self: *Self) !void {...@@ -4246,7 +4246,7 @@ fn airTrap(self: *Self) !void {
4246 return self.finishAirBookkeeping();4246 return self.finishAirBookkeeping();
4247}4247}
42484248
4249fn airBreakpoint(self: *Self) !void {4249fn airBreakpoint(self: *Self) InnerError!void {
4250 _ = try self.addInst(.{4250 _ = try self.addInst(.{
4251 .tag = .brk,4251 .tag = .brk,
4252 .data = .{ .imm16 = 0xf000 },4252 .data = .{ .imm16 = 0xf000 },
...@@ -4254,17 +4254,17 @@ fn airBreakpoint(self: *Self) !void {...@@ -4254,17 +4254,17 @@ fn airBreakpoint(self: *Self) !void {
4254 return self.finishAirBookkeeping();4254 return self.finishAirBookkeeping();
4255}4255}
42564256
4257fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {4257fn airRetAddr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4258 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});4258 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});
4259 return self.finishAir(inst, result, .{ .none, .none, .none });4259 return self.finishAir(inst, result, .{ .none, .none, .none });
4260}4260}
42614261
4262fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {4262fn airFrameAddress(self: *Self, inst: Air.Inst.Index) InnerError!void {
4263 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});4263 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});
4264 return self.finishAir(inst, result, .{ .none, .none, .none });4264 return self.finishAir(inst, result, .{ .none, .none, .none });
4265}4265}
42664266
4267fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {4267fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
4268 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});4268 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});
4269 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4269 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4270 const callee = pl_op.operand;4270 const callee = pl_op.operand;
...@@ -4422,7 +4422,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4422,7 +4422,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4422 return bt.finishAir(result);4422 return bt.finishAir(result);
4423}4423}
44244424
4425fn airRet(self: *Self, inst: Air.Inst.Index) !void {4425fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void {
4426 const pt = self.pt;4426 const pt = self.pt;
4427 const zcu = pt.zcu;4427 const zcu = pt.zcu;
4428 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4428 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4455,7 +4455,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -4455,7 +4455,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
4455 return self.finishAir(inst, .dead, .{ un_op, .none, .none });4455 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4456}4456}
44574457
4458fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {4458fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
4459 const pt = self.pt;4459 const pt = self.pt;
4460 const zcu = pt.zcu;4460 const zcu = pt.zcu;
4461 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4461 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4499,7 +4499,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -4499,7 +4499,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
4499 return self.finishAir(inst, .dead, .{ un_op, .none, .none });4499 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4500}4500}
45014501
4502fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {4502fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) InnerError!void {
4503 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;4503 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4504 const lhs_ty = self.typeOf(bin_op.lhs);4504 const lhs_ty = self.typeOf(bin_op.lhs);
45054505
...@@ -4597,12 +4597,12 @@ fn cmp(...@@ -4597,12 +4597,12 @@ fn cmp(
4597 }4597 }
4598}4598}
45994599
4600fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {4600fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void {
4601 _ = inst;4601 _ = inst;
4602 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});4602 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
4603}4603}
46044604
4605fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {4605fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
4606 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4606 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4607 const operand = try self.resolveInst(un_op);4607 const operand = try self.resolveInst(un_op);
4608 _ = operand;4608 _ = operand;
...@@ -4610,7 +4610,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -4610,7 +4610,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
4610 return self.finishAir(inst, result, .{ un_op, .none, .none });4610 return self.finishAir(inst, result, .{ un_op, .none, .none });
4611}4611}
46124612
4613fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {4613fn airDbgStmt(self: *Self, inst: Air.Inst.Index) InnerError!void {
4614 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;4614 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
46154615
4616 _ = try self.addInst(.{4616 _ = try self.addInst(.{
...@@ -4624,7 +4624,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {...@@ -4624,7 +4624,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
4624 return self.finishAirBookkeeping();4624 return self.finishAirBookkeeping();
4625}4625}
46264626
4627fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {4627fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
4628 const pt = self.pt;4628 const pt = self.pt;
4629 const zcu = pt.zcu;4629 const zcu = pt.zcu;
4630 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4630 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
...@@ -4635,7 +4635,7 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4635,7 +4635,7 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
4635 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));4635 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
4636}4636}
46374637
4638fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {4638fn airDbgVar(self: *Self, inst: Air.Inst.Index) InnerError!void {
4639 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4639 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4640 const operand = pl_op.operand;4640 const operand = pl_op.operand;
4641 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];4641 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
...@@ -4686,7 +4686,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {...@@ -4686,7 +4686,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
4686 }4686 }
4687}4687}
46884688
4689fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {4689fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4690 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4690 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4691 const cond = try self.resolveInst(pl_op.operand);4691 const cond = try self.resolveInst(pl_op.operand);
4692 const extra = self.air.extraData(Air.CondBr, pl_op.payload);4692 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
...@@ -4919,7 +4919,7 @@ fn isNonErr(...@@ -4919,7 +4919,7 @@ fn isNonErr(
4919 }4919 }
4920}4920}
49214921
4922fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {4922fn airIsNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
4923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4925 const operand = try self.resolveInst(un_op);4925 const operand = try self.resolveInst(un_op);
...@@ -4930,7 +4930,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -4930,7 +4930,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
4930 return self.finishAir(inst, result, .{ un_op, .none, .none });4930 return self.finishAir(inst, result, .{ un_op, .none, .none });
4931}4931}
49324932
4933fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {4933fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4934 const pt = self.pt;4934 const pt = self.pt;
4935 const zcu = pt.zcu;4935 const zcu = pt.zcu;
4936 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4936 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4947,7 +4947,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4947,7 +4947,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4947 return self.finishAir(inst, result, .{ un_op, .none, .none });4947 return self.finishAir(inst, result, .{ un_op, .none, .none });
4948}4948}
49494949
4950fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {4950fn airIsNonNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
4951 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4951 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4952 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4952 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4953 const operand = try self.resolveInst(un_op);4953 const operand = try self.resolveInst(un_op);
...@@ -4958,7 +4958,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -4958,7 +4958,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
4958 return self.finishAir(inst, result, .{ un_op, .none, .none });4958 return self.finishAir(inst, result, .{ un_op, .none, .none });
4959}4959}
49604960
4961fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {4961fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4962 const pt = self.pt;4962 const pt = self.pt;
4963 const zcu = pt.zcu;4963 const zcu = pt.zcu;
4964 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4964 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4975,7 +4975,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4975,7 +4975,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4975 return self.finishAir(inst, result, .{ un_op, .none, .none });4975 return self.finishAir(inst, result, .{ un_op, .none, .none });
4976}4976}
49774977
4978fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {4978fn airIsErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4979 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4979 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4980 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4980 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4981 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };4981 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
...@@ -4986,7 +4986,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4986,7 +4986,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
4986 return self.finishAir(inst, result, .{ un_op, .none, .none });4986 return self.finishAir(inst, result, .{ un_op, .none, .none });
4987}4987}
49884988
4989fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {4989fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4990 const pt = self.pt;4990 const pt = self.pt;
4991 const zcu = pt.zcu;4991 const zcu = pt.zcu;
4992 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4992 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -5003,7 +5003,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5003,7 +5003,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
5003 return self.finishAir(inst, result, .{ un_op, .none, .none });5003 return self.finishAir(inst, result, .{ un_op, .none, .none });
5004}5004}
50055005
5006fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {5006fn airIsNonErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5007 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5007 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {5008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5009 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };5009 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
...@@ -5014,7 +5014,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5014,7 +5014,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
5014 return self.finishAir(inst, result, .{ un_op, .none, .none });5014 return self.finishAir(inst, result, .{ un_op, .none, .none });
5015}5015}
50165016
5017fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {5017fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5018 const pt = self.pt;5018 const pt = self.pt;
5019 const zcu = pt.zcu;5019 const zcu = pt.zcu;
5020 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5020 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -5031,7 +5031,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5031,7 +5031,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
5031 return self.finishAir(inst, result, .{ un_op, .none, .none });5031 return self.finishAir(inst, result, .{ un_op, .none, .none });
5032}5032}
50335033
5034fn airLoop(self: *Self, inst: Air.Inst.Index) !void {5034fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void {
5035 // A loop is a setup to be able to jump back to the beginning.5035 // A loop is a setup to be able to jump back to the beginning.
5036 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5036 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5037 const loop = self.air.extraData(Air.Block, ty_pl.payload);5037 const loop = self.air.extraData(Air.Block, ty_pl.payload);
...@@ -5052,7 +5052,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {...@@ -5052,7 +5052,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {
5052 });5052 });
5053}5053}
50545054
5055fn airBlock(self: *Self, inst: Air.Inst.Index) !void {5055fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
5056 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5056 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5057 const extra = self.air.extraData(Air.Block, ty_pl.payload);5057 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5058 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));5058 try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
...@@ -5090,7 +5090,7 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !...@@ -5090,7 +5090,7 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
5090 return self.finishAir(inst, result, .{ .none, .none, .none });5090 return self.finishAir(inst, result, .{ .none, .none, .none });
5091}5091}
50925092
5093fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {5093fn airSwitch(self: *Self, inst: Air.Inst.Index) InnerError!void {
5094 const switch_br = self.air.unwrapSwitch(inst);5094 const switch_br = self.air.unwrapSwitch(inst);
5095 const condition_ty = self.typeOf(switch_br.operand);5095 const condition_ty = self.typeOf(switch_br.operand);
5096 const liveness = try self.liveness.getSwitchBr(5096 const liveness = try self.liveness.getSwitchBr(
...@@ -5224,7 +5224,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {...@@ -5224,7 +5224,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
5224 }5224 }
5225}5225}
52265226
5227fn airBr(self: *Self, inst: Air.Inst.Index) !void {5227fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5228 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;5228 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
5229 try self.br(branch.block_inst, branch.operand);5229 try self.br(branch.block_inst, branch.operand);
5230 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });5230 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
...@@ -5268,7 +5268,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -5268,7 +5268,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
5268 }));5268 }));
5269}5269}
52705270
5271fn airAsm(self: *Self, inst: Air.Inst.Index) !void {5271fn airAsm(self: *Self, inst: Air.Inst.Index) InnerError!void {
5272 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5272 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5273 const extra = self.air.extraData(Air.Asm, ty_pl.payload);5273 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
5274 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;5274 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
...@@ -5601,7 +5601,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5601,7 +5601,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5601 .tag = .ldr_ptr_stack,5601 .tag = .ldr_ptr_stack,
5602 .data = .{ .load_store_stack = .{5602 .data = .{ .load_store_stack = .{
5603 .rt = reg,5603 .rt = reg,
5604 .offset = @as(u32, @intCast(off)),5604 .offset = @intCast(off),
5605 } },5605 } },
5606 });5606 });
5607 },5607 },
...@@ -5617,13 +5617,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5617,13 +5617,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5617 .immediate => |x| {5617 .immediate => |x| {
5618 _ = try self.addInst(.{5618 _ = try self.addInst(.{
5619 .tag = .movz,5619 .tag = .movz,
5620 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x)) } },5620 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x) } },
5621 });5621 });
56225622
5623 if (x & 0x0000_0000_ffff_0000 != 0) {5623 if (x & 0x0000_0000_ffff_0000 != 0) {
5624 _ = try self.addInst(.{5624 _ = try self.addInst(.{
5625 .tag = .movk,5625 .tag = .movk,
5626 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 16)), .hw = 1 } },5626 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 16), .hw = 1 } },
5627 });5627 });
5628 }5628 }
56295629
...@@ -5631,13 +5631,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5631,13 +5631,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5631 if (x & 0x0000_ffff_0000_0000 != 0) {5631 if (x & 0x0000_ffff_0000_0000 != 0) {
5632 _ = try self.addInst(.{5632 _ = try self.addInst(.{
5633 .tag = .movk,5633 .tag = .movk,
5634 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 32)), .hw = 2 } },5634 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 32), .hw = 2 } },
5635 });5635 });
5636 }5636 }
5637 if (x & 0xffff_0000_0000_0000 != 0) {5637 if (x & 0xffff_0000_0000_0000 != 0) {
5638 _ = try self.addInst(.{5638 _ = try self.addInst(.{
5639 .tag = .movk,5639 .tag = .movk,
5640 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 48)), .hw = 3 } },5640 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 48), .hw = 3 } },
5641 });5641 });
5642 }5642 }
5643 }5643 }
...@@ -5709,7 +5709,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5709,7 +5709,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5709 .tag = tag,5709 .tag = tag,
5710 .data = .{ .load_store_stack = .{5710 .data = .{ .load_store_stack = .{
5711 .rt = reg,5711 .rt = reg,
5712 .offset = @as(u32, @intCast(off)),5712 .offset = @intCast(off),
5713 } },5713 } },
5714 });5714 });
5715 },5715 },
...@@ -5733,7 +5733,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5733,7 +5733,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5733 .tag = tag,5733 .tag = tag,
5734 .data = .{ .load_store_stack = .{5734 .data = .{ .load_store_stack = .{
5735 .rt = reg,5735 .rt = reg,
5736 .offset = @as(u32, @intCast(off)),5736 .offset = @intCast(off),
5737 } },5737 } },
5738 });5738 });
5739 },5739 },
...@@ -5918,13 +5918,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -5918,13 +5918,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5918 }5918 }
5919}5919}
59205920
5921fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {5921fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5922 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5922 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5923 const result = try self.resolveInst(un_op);5923 const result = try self.resolveInst(un_op);
5924 return self.finishAir(inst, result, .{ un_op, .none, .none });5924 return self.finishAir(inst, result, .{ un_op, .none, .none });
5925}5925}
59265926
5927fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {5927fn airBitCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
5928 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5928 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5929 const result = if (self.liveness.isUnused(inst)) .dead else result: {5929 const result = if (self.liveness.isUnused(inst)) .dead else result: {
5930 const operand = try self.resolveInst(ty_op.operand);5930 const operand = try self.resolveInst(ty_op.operand);
...@@ -5945,7 +5945,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -5945,7 +5945,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
5945 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5945 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5946}5946}
59475947
5948fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {5948fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
5949 const pt = self.pt;5949 const pt = self.pt;
5950 const zcu = pt.zcu;5950 const zcu = pt.zcu;
5951 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5951 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
...@@ -5963,7 +5963,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -5963,7 +5963,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
5963 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5963 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5964}5964}
59655965
5966fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {5966fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
5967 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5967 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5968 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{5968 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
5969 self.target.cpu.arch,5969 self.target.cpu.arch,
...@@ -5971,7 +5971,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {...@@ -5971,7 +5971,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
5971 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5971 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5972}5972}
59735973
5974fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {5974fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
5975 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5975 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5976 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{5976 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
5977 self.target.cpu.arch,5977 self.target.cpu.arch,
...@@ -5979,7 +5979,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {...@@ -5979,7 +5979,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
5979 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });5979 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5980}5980}
59815981
5982fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {5982fn airCmpxchg(self: *Self, inst: Air.Inst.Index) InnerError!void {
5983 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5983 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5984 const extra = self.air.extraData(Air.Block, ty_pl.payload);5984 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5985 _ = extra;5985 _ = extra;
...@@ -5989,23 +5989,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {...@@ -5989,23 +5989,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
5989 });5989 });
5990}5990}
59915991
5992fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {5992fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) InnerError!void {
5993 _ = inst;5993 _ = inst;
5994 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});5994 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
5995}5995}
59965996
5997fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {5997fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
5998 _ = inst;5998 _ = inst;
5999 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});5999 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
6000}6000}
60016001
6002fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {6002fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) InnerError!void {
6003 _ = inst;6003 _ = inst;
6004 _ = order;6004 _ = order;
6005 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});6005 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
6006}6006}
60076007
6008fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {6008fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
6009 _ = inst;6009 _ = inst;
6010 if (safety) {6010 if (safety) {
6011 // TODO if the value is undef, write 0xaa bytes to dest6011 // TODO if the value is undef, write 0xaa bytes to dest
...@@ -6015,12 +6015,12 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {...@@ -6015,12 +6015,12 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
6015 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});6015 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
6016}6016}
60176017
6018fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {6018fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
6019 _ = inst;6019 _ = inst;
6020 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});6020 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
6021}6021}
60226022
6023fn airTagName(self: *Self, inst: Air.Inst.Index) !void {6023fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {
6024 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6024 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
6025 const operand = try self.resolveInst(un_op);6025 const operand = try self.resolveInst(un_op);
6026 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {6026 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
...@@ -6030,7 +6030,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {...@@ -6030,7 +6030,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
6030 return self.finishAir(inst, result, .{ un_op, .none, .none });6030 return self.finishAir(inst, result, .{ un_op, .none, .none });
6031}6031}
60326032
6033fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {6033fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
6034 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6034 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
6035 const operand = try self.resolveInst(un_op);6035 const operand = try self.resolveInst(un_op);
6036 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {6036 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
...@@ -6040,33 +6040,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -6040,33 +6040,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
6040 return self.finishAir(inst, result, .{ un_op, .none, .none });6040 return self.finishAir(inst, result, .{ un_op, .none, .none });
6041}6041}
60426042
6043fn airSplat(self: *Self, inst: Air.Inst.Index) !void {6043fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void {
6044 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;6044 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6045 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});6045 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});
6046 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });6046 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
6047}6047}
60486048
6049fn airSelect(self: *Self, inst: Air.Inst.Index) !void {6049fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void {
6050 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6050 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6051 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;6051 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});6052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});
6053 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });6053 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
6054}6054}
60556055
6056fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {6056fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!void {
6057 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6057 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6058 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;6058 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
6059 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});6059 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});
6060 return self.finishAir(inst, result, .{ extra.a, extra.b, .none });6060 return self.finishAir(inst, result, .{ extra.a, extra.b, .none });
6061}6061}
60626062
6063fn airReduce(self: *Self, inst: Air.Inst.Index) !void {6063fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void {
6064 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;6064 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
6065 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{});6065 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{});
6066 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });6066 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
6067}6067}
60686068
6069fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {6069fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
6070 const pt = self.pt;6070 const pt = self.pt;
6071 const zcu = pt.zcu;6071 const zcu = pt.zcu;
6072 const vector_ty = self.typeOfIndex(inst);6072 const vector_ty = self.typeOfIndex(inst);
...@@ -6090,19 +6090,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {...@@ -6090,19 +6090,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
6090 return bt.finishAir(result);6090 return bt.finishAir(result);
6091}6091}
60926092
6093fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {6093fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
6094 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6094 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6095 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;6095 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
6096 _ = extra;6096 _ = extra;
6097 return self.fail("TODO implement airUnionInit for aarch64", .{});6097 return self.fail("TODO implement airUnionInit for aarch64", .{});
6098}6098}
60996099
6100fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {6100fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void {
6101 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;6101 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
6102 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });6102 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
6103}6103}
61046104
6105fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {6105fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void {
6106 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6106 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6107 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;6107 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6108 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {6108 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
...@@ -6111,7 +6111,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {...@@ -6111,7 +6111,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
6111 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });6111 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
6112}6112}
61136113
6114fn airTry(self: *Self, inst: Air.Inst.Index) !void {6114fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void {
6115 const pt = self.pt;6115 const pt = self.pt;
6116 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6116 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6117 const extra = self.air.extraData(Air.Try, pl_op.payload);6117 const extra = self.air.extraData(Air.Try, pl_op.payload);
...@@ -6139,7 +6139,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {...@@ -6139,7 +6139,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
6139 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });6139 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
6140}6140}
61416141
6142fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {6142fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
6143 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6143 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6144 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);6144 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
6145 const body = self.air.extra[extra.end..][0..extra.data.body_len];6145 const body = self.air.extra[extra.end..][0..extra.data.body_len];
src/arch/arm/CodeGen.zig+3-3
...@@ -245,7 +245,7 @@ const DbgInfoReloc = struct {...@@ -245,7 +245,7 @@ const DbgInfoReloc = struct {
245 name: [:0]const u8,245 name: [:0]const u8,
246 mcv: MCValue,246 mcv: MCValue,
247247
248 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {248 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) CodeGenError!void {
249 switch (reloc.tag) {249 switch (reloc.tag) {
250 .arg,250 .arg,
251 .dbg_arg_inline,251 .dbg_arg_inline,
...@@ -259,7 +259,7 @@ const DbgInfoReloc = struct {...@@ -259,7 +259,7 @@ const DbgInfoReloc = struct {
259 }259 }
260 }260 }
261261
262 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void {262 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) CodeGenError!void {
263 switch (function.debug_output) {263 switch (function.debug_output) {
264 .dwarf => |dw| {264 .dwarf => |dw| {
265 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {265 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
...@@ -287,7 +287,7 @@ const DbgInfoReloc = struct {...@@ -287,7 +287,7 @@ const DbgInfoReloc = struct {
287 }287 }
288 }288 }
289289
290 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {290 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) CodeGenError!void {
291 switch (function.debug_output) {291 switch (function.debug_output) {
292 .dwarf => |dw| {292 .dwarf => |dw| {
293 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {293 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
src/arch/wasm/CodeGen.zig+142-390
...@@ -6,7 +6,6 @@ const assert = std.debug.assert;...@@ -6,7 +6,6 @@ const assert = std.debug.assert;
6const testing = std.testing;6const testing = std.testing;
7const leb = std.leb;7const leb = std.leb;
8const mem = std.mem;8const mem = std.mem;
9const wasm = std.wasm;
10const log = std.log.scoped(.codegen);9const log = std.log.scoped(.codegen);
1110
12const codegen = @import("../../codegen.zig");11const codegen = @import("../../codegen.zig");
...@@ -55,22 +54,19 @@ const WValue = union(enum) {...@@ -55,22 +54,19 @@ const WValue = union(enum) {
55 float32: f32,54 float32: f32,
56 /// A constant 64bit float value55 /// A constant 64bit float value
57 float64: f64,56 float64: f64,
58 /// A value that represents a pointer to the data section57 /// A value that represents a pointer to the data section.
59 /// Note: The value contains the symbol index, rather than the actual address58 memory: InternPool.Index,
60 /// as we use this to perform the relocation.
61 memory: u32,
62 /// A value that represents a parent pointer and an offset59 /// A value that represents a parent pointer and an offset
63 /// from that pointer. i.e. when slicing with constant values.60 /// from that pointer. i.e. when slicing with constant values.
64 memory_offset: struct {61 memory_offset: struct {
65 /// The symbol of the parent pointer62 pointer: InternPool.Index,
66 pointer: u32,
67 /// Offset will be set as addend when relocating63 /// Offset will be set as addend when relocating
68 offset: u32,64 offset: u32,
69 },65 },
70 /// Represents a function pointer66 /// Represents a function pointer
71 /// In wasm function pointers are indexes into a function table,67 /// In wasm function pointers are indexes into a function table,
72 /// rather than an address in the data section.68 /// rather than an address in the data section.
73 function_index: u32,69 function_index: InternPool.Index,
74 /// Offset from the bottom of the virtual stack, with the offset70 /// Offset from the bottom of the virtual stack, with the offset
75 /// pointing to where the value lives.71 /// pointing to where the value lives.
76 stack_offset: struct {72 stack_offset: struct {
...@@ -119,7 +115,7 @@ const WValue = union(enum) {...@@ -119,7 +115,7 @@ const WValue = union(enum) {
119 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.115 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
120116
121 const index = local_value - reserved;117 const index = local_value - reserved;
122 const valtype = @as(wasm.Valtype, @enumFromInt(gen.locals.items[index]));118 const valtype: std.wasm.Valtype = @enumFromInt(gen.locals.items[index]);
123 switch (valtype) {119 switch (valtype) {
124 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead120 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead
125 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,121 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
...@@ -132,8 +128,6 @@ const WValue = union(enum) {...@@ -132,8 +128,6 @@ const WValue = union(enum) {
132 }128 }
133};129};
134130
135/// Wasm ops, but without input/output/signedness information
136/// Used for `buildOpcode`
137const Op = enum {131const Op = enum {
138 @"unreachable",132 @"unreachable",
139 nop,133 nop,
...@@ -200,70 +194,42 @@ const Op = enum {...@@ -200,70 +194,42 @@ const Op = enum {
200 extend,194 extend,
201};195};
202196
203/// Contains the settings needed to create an `Opcode` using `buildOpcode`.
204///
205/// The fields correspond to the opcode name. Here is an example
206/// i32_trunc_f32_s
207/// ^ ^ ^ ^
208/// | | | |
209/// valtype1 | | |
210/// = .i32 | | |
211/// | | |
212/// op | |
213/// = .trunc | |
214/// | |
215/// valtype2 |
216/// = .f32 |
217/// |
218/// width |
219/// = null |
220/// |
221/// signed
222/// = true
223///
224/// There can be missing fields, here are some more examples:
225/// i64_load8_u
226/// --> .{ .valtype1 = .i64, .op = .load, .width = 8, signed = false }
227/// i32_mul
228/// --> .{ .valtype1 = .i32, .op = .trunc }
229/// nop
230/// --> .{ .op = .nop }
231const OpcodeBuildArguments = struct {197const OpcodeBuildArguments = struct {
232 /// First valtype in the opcode (usually represents the type of the output)198 /// First valtype in the opcode (usually represents the type of the output)
233 valtype1: ?wasm.Valtype = null,199 valtype1: ?std.wasm.Valtype = null,
234 /// The operation (e.g. call, unreachable, div, min, sqrt, etc.)200 /// The operation (e.g. call, unreachable, div, min, sqrt, etc.)
235 op: Op,201 op: Op,
236 /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s)202 /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s)
237 width: ?u8 = null,203 width: ?u8 = null,
238 /// Second valtype in the opcode name (usually represents the type of the input)204 /// Second valtype in the opcode name (usually represents the type of the input)
239 valtype2: ?wasm.Valtype = null,205 valtype2: ?std.wasm.Valtype = null,
240 /// Signedness of the op206 /// Signedness of the op
241 signedness: ?std.builtin.Signedness = null,207 signedness: ?std.builtin.Signedness = null,
242};208};
243209
244/// Helper function that builds an Opcode given the arguments needed210/// TODO: deprecated, should be split up per tag.
245fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {211fn buildOpcode(args: OpcodeBuildArguments) std.wasm.Opcode {
246 switch (args.op) {212 switch (args.op) {
247 .@"unreachable" => return .@"unreachable",213 .@"unreachable" => unreachable,
248 .nop => return .nop,214 .nop => unreachable,
249 .block => return .block,215 .block => unreachable,
250 .loop => return .loop,216 .loop => unreachable,
251 .@"if" => return .@"if",217 .@"if" => unreachable,
252 .@"else" => return .@"else",218 .@"else" => unreachable,
253 .end => return .end,219 .end => unreachable,
254 .br => return .br,220 .br => unreachable,
255 .br_if => return .br_if,221 .br_if => unreachable,
256 .br_table => return .br_table,222 .br_table => unreachable,
257 .@"return" => return .@"return",223 .@"return" => unreachable,
258 .call => return .call,224 .call => unreachable,
259 .call_indirect => return .call_indirect,225 .call_indirect => unreachable,
260 .drop => return .drop,226 .drop => unreachable,
261 .select => return .select,227 .select => unreachable,
262 .local_get => return .local_get,228 .local_get => unreachable,
263 .local_set => return .local_set,229 .local_set => unreachable,
264 .local_tee => return .local_tee,230 .local_tee => unreachable,
265 .global_get => return .global_get,231 .global_get => unreachable,
266 .global_set => return .global_set,232 .global_set => unreachable,
267233
268 .load => if (args.width) |width| switch (width) {234 .load => if (args.width) |width| switch (width) {
269 8 => switch (args.valtype1.?) {235 8 => switch (args.valtype1.?) {
...@@ -626,11 +592,11 @@ test "Wasm - buildOpcode" {...@@ -626,11 +592,11 @@ test "Wasm - buildOpcode" {
626 const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });592 const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });
627 const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });593 const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });
628594
629 try testing.expectEqual(@as(wasm.Opcode, .i32_const), i32_const);595 try testing.expectEqual(@as(std.wasm.Opcode, .i32_const), i32_const);
630 try testing.expectEqual(@as(wasm.Opcode, .end), end);596 try testing.expectEqual(@as(std.wasm.Opcode, .end), end);
631 try testing.expectEqual(@as(wasm.Opcode, .local_get), local_get);597 try testing.expectEqual(@as(std.wasm.Opcode, .local_get), local_get);
632 try testing.expectEqual(@as(wasm.Opcode, .i64_extend32_s), i64_extend32_s);598 try testing.expectEqual(@as(std.wasm.Opcode, .i64_extend32_s), i64_extend32_s);
633 try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);599 try testing.expectEqual(@as(std.wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
634}600}
635601
636/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`602/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
...@@ -806,13 +772,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {...@@ -806,13 +772,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
806 // In the other cases, we will simply lower the constant to a value that fits772 // In the other cases, we will simply lower the constant to a value that fits
807 // into a single local (such as a pointer, integer, bool, etc).773 // into a single local (such as a pointer, integer, bool, etc).
808 const result: WValue = if (isByRef(ty, pt, func.target.*))774 const result: WValue = if (isByRef(ty, pt, func.target.*))
809 switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) {775 .{ .memory = val.toIntern() }
810 .mcv => |mcv| .{ .memory = mcv.load_symbol },
811 .fail => |err_msg| {
812 func.err_msg = err_msg;
813 return error.CodegenFail;
814 },
815 }
816 else776 else
817 try func.lowerConstant(val, ty);777 try func.lowerConstant(val, ty);
818778
...@@ -919,7 +879,7 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {...@@ -919,7 +879,7 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
919 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });879 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
920}880}
921881
922fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {882fn addExtended(func: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {
923 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));883 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
924 try func.mir_extra.append(func.gpa, @intFromEnum(opcode));884 try func.mir_extra.append(func.gpa, @intFromEnum(opcode));
925 try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });885 try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
...@@ -929,6 +889,10 @@ fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!vo...@@ -929,6 +889,10 @@ fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!vo
929 try func.addInst(.{ .tag = tag, .data = .{ .label = label } });889 try func.addInst(.{ .tag = tag, .data = .{ .label = label } });
930}890}
931891
892fn addCallTagName(func: *CodeGen, ip_index: InternPool.Index) error{OutOfMemory}!void {
893 try func.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = ip_index } });
894}
895
932/// Accepts an unsigned 32bit integer rather than a signed integer to896/// Accepts an unsigned 32bit integer rather than a signed integer to
933/// prevent us from having to bitcast multiple times as most values897/// prevent us from having to bitcast multiple times as most values
934/// within codegen are represented as unsigned rather than signed.898/// within codegen are represented as unsigned rather than signed.
...@@ -950,7 +914,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {...@@ -950,7 +914,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
950 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));914 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
951 // tag + 128bit value915 // tag + 128bit value
952 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);916 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
953 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));917 func.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const));
954 func.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values)));918 func.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values)));
955 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });919 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
956}920}
...@@ -968,15 +932,15 @@ fn addMemArg(func: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOf...@@ -968,15 +932,15 @@ fn addMemArg(func: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOf
968932
969/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the933/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the
970/// given `tag`.934/// given `tag`.
971fn addAtomicMemArg(func: *CodeGen, tag: wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {935fn addAtomicMemArg(func: *CodeGen, tag: std.wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
972 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));936 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
973 _ = try func.addExtra(mem_arg);937 _ = try func.addExtra(mem_arg);
974 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });938 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
975}939}
976940
977/// Helper function to emit atomic mir opcodes.941/// Helper function to emit atomic mir opcodes.
978fn addAtomicTag(func: *CodeGen, tag: wasm.AtomicsOpcode) error{OutOfMemory}!void {942fn addAtomicTag(func: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void {
979 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));943 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
980 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });944 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
981}945}
982946
...@@ -1003,7 +967,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32...@@ -1003,7 +967,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
1003}967}
1004968
1005/// Using a given `Type`, returns the corresponding valtype for .auto callconv969/// Using a given `Type`, returns the corresponding valtype for .auto callconv
1006fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {970fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valtype {
1007 const zcu = pt.zcu;971 const zcu = pt.zcu;
1008 const ip = &zcu.intern_pool;972 const ip = &zcu.intern_pool;
1009 return switch (ty.zigTypeTag(zcu)) {973 return switch (ty.zigTypeTag(zcu)) {
...@@ -1044,7 +1008,7 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {...@@ -1044,7 +1008,7 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype {
10441008
1045/// Using a given `Type`, returns the byte representation of its wasm value type1009/// Using a given `Type`, returns the byte representation of its wasm value type
1046fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {1010fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {
1047 return wasm.valtype(typeToValtype(ty, pt, target));1011 return @intFromEnum(typeToValtype(ty, pt, target));
1048}1012}
10491013
1050/// Using a given `Type`, returns the corresponding wasm value type1014/// Using a given `Type`, returns the corresponding wasm value type
...@@ -1052,7 +1016,7 @@ fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {...@@ -1052,7 +1016,7 @@ fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {
1052/// with no return type1016/// with no return type
1053fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {1017fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {
1054 return switch (ty.ip_index) {1018 return switch (ty.ip_index) {
1055 .void_type, .noreturn_type => wasm.block_empty,1019 .void_type, .noreturn_type => std.wasm.block_empty,
1056 else => genValtype(ty, pt, target),1020 else => genValtype(ty, pt, target),
1057 };1021 };
1058}1022}
...@@ -1141,35 +1105,34 @@ fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -1141,35 +1105,34 @@ fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1141 return .{ .local = .{ .value = initial_index, .references = 1 } };1105 return .{ .local = .{ .value = initial_index, .references = 1 } };
1142}1106}
11431107
1144/// Generates a `wasm.Type` from a given function type.
1145/// Memory is owned by the caller.
1146fn genFunctype(1108fn genFunctype(
1147 gpa: Allocator,1109 wasm: *link.File.Wasm,
1148 cc: std.builtin.CallingConvention,1110 cc: std.builtin.CallingConvention,
1149 params: []const InternPool.Index,1111 params: []const InternPool.Index,
1150 return_type: Type,1112 return_type: Type,
1151 pt: Zcu.PerThread,1113 pt: Zcu.PerThread,
1152 target: std.Target,1114 target: std.Target,
1153) !wasm.Type {1115) !link.File.Wasm.FunctionType.Index {
1154 const zcu = pt.zcu;1116 const zcu = pt.zcu;
1155 var temp_params = std.ArrayList(wasm.Valtype).init(gpa);1117 const gpa = zcu.gpa;
1156 defer temp_params.deinit();1118 var temp_params: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty;
1157 var returns = std.ArrayList(wasm.Valtype).init(gpa);1119 defer temp_params.deinit(gpa);
1158 defer returns.deinit();1120 var returns: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty;
1121 defer returns.deinit(gpa);
11591122
1160 if (firstParamSRet(cc, return_type, pt, target)) {1123 if (firstParamSRet(cc, return_type, pt, target)) {
1161 try temp_params.append(.i32); // memory address is always a 32-bit handle1124 try temp_params.append(gpa, .i32); // memory address is always a 32-bit handle
1162 } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {1125 } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
1163 if (cc == .wasm_watc) {1126 if (cc == .wasm_watc) {
1164 const res_classes = abi.classifyType(return_type, zcu);1127 const res_classes = abi.classifyType(return_type, zcu);
1165 assert(res_classes[0] == .direct and res_classes[1] == .none);1128 assert(res_classes[0] == .direct and res_classes[1] == .none);
1166 const scalar_type = abi.scalarType(return_type, zcu);1129 const scalar_type = abi.scalarType(return_type, zcu);
1167 try returns.append(typeToValtype(scalar_type, pt, target));1130 try returns.append(gpa, typeToValtype(scalar_type, pt, target));
1168 } else {1131 } else {
1169 try returns.append(typeToValtype(return_type, pt, target));1132 try returns.append(gpa, typeToValtype(return_type, pt, target));
1170 }1133 }
1171 } else if (return_type.isError(zcu)) {1134 } else if (return_type.isError(zcu)) {
1172 try returns.append(.i32);1135 try returns.append(gpa, .i32);
1173 }1136 }
11741137
1175 // param types1138 // param types
...@@ -1183,24 +1146,24 @@ fn genFunctype(...@@ -1183,24 +1146,24 @@ fn genFunctype(
1183 if (param_classes[1] == .none) {1146 if (param_classes[1] == .none) {
1184 if (param_classes[0] == .direct) {1147 if (param_classes[0] == .direct) {
1185 const scalar_type = abi.scalarType(param_type, zcu);1148 const scalar_type = abi.scalarType(param_type, zcu);
1186 try temp_params.append(typeToValtype(scalar_type, pt, target));1149 try temp_params.append(gpa, typeToValtype(scalar_type, pt, target));
1187 } else {1150 } else {
1188 try temp_params.append(typeToValtype(param_type, pt, target));1151 try temp_params.append(gpa, typeToValtype(param_type, pt, target));
1189 }1152 }
1190 } else {1153 } else {
1191 // i128/f1281154 // i128/f128
1192 try temp_params.append(.i64);1155 try temp_params.append(gpa, .i64);
1193 try temp_params.append(.i64);1156 try temp_params.append(gpa, .i64);
1194 }1157 }
1195 },1158 },
1196 else => try temp_params.append(typeToValtype(param_type, pt, target)),1159 else => try temp_params.append(gpa, typeToValtype(param_type, pt, target)),
1197 }1160 }
1198 }1161 }
11991162
1200 return wasm.Type{1163 return wasm.addFuncType(.{
1201 .params = try temp_params.toOwnedSlice(),1164 .params = try wasm.internValtypeList(temp_params.items),
1202 .returns = try returns.toOwnedSlice(),1165 .returns = try wasm.internValtypeList(returns.items),
1203 };1166 });
1204}1167}
12051168
1206pub fn generate(1169pub fn generate(
...@@ -1244,14 +1207,13 @@ pub fn generate(...@@ -1244,14 +1207,13 @@ pub fn generate(
1244}1207}
12451208
1246fn genFunc(func: *CodeGen) InnerError!void {1209fn genFunc(func: *CodeGen) InnerError!void {
1210 const wasm = func.bin_file;
1247 const pt = func.pt;1211 const pt = func.pt;
1248 const zcu = pt.zcu;1212 const zcu = pt.zcu;
1249 const ip = &zcu.intern_pool;1213 const ip = &zcu.intern_pool;
1250 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);1214 const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu);
1251 const fn_info = zcu.typeToFunc(fn_ty).?;1215 const fn_info = zcu.typeToFunc(fn_ty).?;
1252 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);1216 const fn_ty_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
1253 defer func_type.deinit(func.gpa);
1254 _ = try func.bin_file.storeNavType(func.owner_nav, func_type);
12551217
1256 var cc_result = try func.resolveCallingConventionValues(fn_ty);1218 var cc_result = try func.resolveCallingConventionValues(fn_ty);
1257 defer cc_result.deinit(func.gpa);1219 defer cc_result.deinit(func.gpa);
...@@ -1273,7 +1235,8 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1273,7 +1235,8 @@ fn genFunc(func: *CodeGen) InnerError!void {
12731235
1274 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)1236 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
1275 // we emit an unreachable instruction to tell the stack validator that part will never be reached.1237 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
1276 if (func_type.returns.len != 0 and func.air.instructions.len > 0) {1238 const returns = fn_ty_index.ptr(wasm).returns.slice(wasm);
1239 if (returns.len != 0 and func.air.instructions.len > 0) {
1277 const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1);1240 const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1);
1278 const last_inst_ty = func.typeOfIndex(inst);1241 const last_inst_ty = func.typeOfIndex(inst);
1279 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) {1242 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) {
...@@ -1291,7 +1254,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1291,7 +1254,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
1291 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);1254 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
1292 defer prologue.deinit();1255 defer prologue.deinit();
12931256
1294 const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym);1257 const sp = @intFromEnum(wasm.zig_object.?.stack_pointer_sym);
1295 // load stack pointer1258 // load stack pointer
1296 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });1259 try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
1297 // store stack pointer so we can restore it when we return from the function1260 // store stack pointer so we can restore it when we return from the function
...@@ -1328,7 +1291,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1328,7 +1291,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
13281291
1329 var emit: Emit = .{1292 var emit: Emit = .{
1330 .mir = mir,1293 .mir = mir,
1331 .bin_file = func.bin_file,1294 .bin_file = wasm,
1332 .code = func.code,1295 .code = func.code,
1333 .locals = func.locals.items,1296 .locals = func.locals.items,
1334 .owner_nav = func.owner_nav,1297 .owner_nav = func.owner_nav,
...@@ -1643,8 +1606,8 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1643,8 +1606,8 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1643 try func.addLabel(.local_set, offset.local.value);1606 try func.addLabel(.local_set, offset.local.value);
16441607
1645 // outer block to jump to when loop is done1608 // outer block to jump to when loop is done
1646 try func.startBlock(.block, wasm.block_empty);1609 try func.startBlock(.block, std.wasm.block_empty);
1647 try func.startBlock(.loop, wasm.block_empty);1610 try func.startBlock(.loop, std.wasm.block_empty);
16481611
1649 // loop condition (offset == length -> break)1612 // loop condition (offset == length -> break)
1650 {1613 {
...@@ -1792,7 +1755,7 @@ const SimdStoreStrategy = enum {...@@ -1792,7 +1755,7 @@ const SimdStoreStrategy = enum {
1792/// features are enabled, the function will return `.direct`. This would allow to store1755/// features are enabled, the function will return `.direct`. This would allow to store
1793/// it using a instruction, rather than an unrolled version.1756/// it using a instruction, rather than an unrolled version.
1794fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy {1757fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy {
1795 std.debug.assert(ty.zigTypeTag(zcu) == .vector);1758 assert(ty.zigTypeTag(zcu) == .vector);
1796 if (ty.bitSize(zcu) != 128) return .unrolled;1759 if (ty.bitSize(zcu) != 128) return .unrolled;
1797 const hasFeature = std.Target.wasm.featureSetHas;1760 const hasFeature = std.Target.wasm.featureSetHas;
1798 const features = target.cpu.features;1761 const features = target.cpu.features;
...@@ -2186,10 +2149,11 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2186,10 +2149,11 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2186}2149}
21872150
2188fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {2151fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2152 const wasm = func.bin_file;
2189 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});2153 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});
2190 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;2154 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2191 const extra = func.air.extraData(Air.Call, pl_op.payload);2155 const extra = func.air.extraData(Air.Call, pl_op.payload);
2192 const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len]));2156 const args: []const Air.Inst.Ref = @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len]);
2193 const ty = func.typeOf(pl_op.operand);2157 const ty = func.typeOf(pl_op.operand);
21942158
2195 const pt = func.pt;2159 const pt = func.pt;
...@@ -2208,43 +2172,14 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2208,43 +2172,14 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2208 const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null;2172 const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null;
22092173
2210 switch (ip.indexToKey(func_val.toIntern())) {2174 switch (ip.indexToKey(func_val.toIntern())) {
2211 .func => |function| {2175 inline .func, .@"extern" => |x| break :blk x.owner_nav,
2212 _ = try func.bin_file.getOrCreateAtomForNav(pt, function.owner_nav);
2213 break :blk function.owner_nav;
2214 },
2215 .@"extern" => |@"extern"| {
2216 const ext_nav = ip.getNav(@"extern".owner_nav);
2217 const ext_info = zcu.typeToFunc(Type.fromInterned(@"extern".ty)).?;
2218 var func_type = try genFunctype(
2219 func.gpa,
2220 ext_info.cc,
2221 ext_info.param_types.get(ip),
2222 Type.fromInterned(ext_info.return_type),
2223 pt,
2224 func.target.*,
2225 );
2226 defer func_type.deinit(func.gpa);
2227 const atom_index = try func.bin_file.getOrCreateAtomForNav(pt, @"extern".owner_nav);
2228 const atom = func.bin_file.getAtomPtr(atom_index);
2229 const type_index = try func.bin_file.storeNavType(@"extern".owner_nav, func_type);
2230 try func.bin_file.addOrUpdateImport(
2231 ext_nav.name.toSlice(ip),
2232 atom.sym_index,
2233 @"extern".lib_name.toSlice(ip),
2234 type_index,
2235 );
2236 break :blk @"extern".owner_nav;
2237 },
2238 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {2176 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
2239 .nav => |nav| {2177 .nav => |nav| break :blk nav,
2240 _ = try func.bin_file.getOrCreateAtomForNav(pt, nav);
2241 break :blk nav;
2242 },
2243 else => {},2178 else => {},
2244 },2179 },
2245 else => {},2180 else => {},
2246 }2181 }
2247 return func.fail("Expected a function, but instead found '{s}'", .{@tagName(ip.indexToKey(func_val.toIntern()))});2182 return func.fail("unable to lower callee to a function index", .{});
2248 };2183 };
22492184
2250 const sret: WValue = if (first_param_sret) blk: {2185 const sret: WValue = if (first_param_sret) blk: {
...@@ -2262,21 +2197,17 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2262,21 +2197,17 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2262 try func.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val);2197 try func.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val);
2263 }2198 }
22642199
2265 if (callee) |direct| {2200 if (callee) |nav_index| {
2266 const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom;2201 try func.addNav(.call_nav, nav_index);
2267 try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
2268 } else {2202 } else {
2269 // in this case we call a function pointer2203 // in this case we call a function pointer
2270 // so load its value onto the stack2204 // so load its value onto the stack
2271 std.debug.assert(ty.zigTypeTag(zcu) == .pointer);2205 assert(ty.zigTypeTag(zcu) == .pointer);
2272 const operand = try func.resolveInst(pl_op.operand);2206 const operand = try func.resolveInst(pl_op.operand);
2273 try func.emitWValue(operand);2207 try func.emitWValue(operand);
22742208
2275 var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);2209 const fn_type_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*);
2276 defer fn_type.deinit(func.gpa);2210 try func.addLabel(.call_indirect, @intFromEnum(fn_type_index));
2277
2278 const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type);
2279 try func.addLabel(.call_indirect, fn_type_index);
2280 }2211 }
22812212
2282 const result_value = result_value: {2213 const result_value = result_value: {
...@@ -2418,7 +2349,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2418,7 +2349,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2418 const extra_index: u32 = @intCast(func.mir_extra.items.len);2349 const extra_index: u32 = @intCast(func.mir_extra.items.len);
2419 // stores as := opcode, offset, alignment (opcode::memarg)2350 // stores as := opcode, offset, alignment (opcode::memarg)
2420 try func.mir_extra.appendSlice(func.gpa, &[_]u32{2351 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2421 std.wasm.simdOpcode(.v128_store),2352 @intFromEnum(std.wasm.SimdOpcode.v128_store),
2422 offset + lhs.offset(),2353 offset + lhs.offset(),
2423 @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0),2354 @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0),
2424 });2355 });
...@@ -2533,7 +2464,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2533,7 +2464,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2533 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));2464 const extra_index = @as(u32, @intCast(func.mir_extra.items.len));
2534 // stores as := opcode, offset, alignment (opcode::memarg)2465 // stores as := opcode, offset, alignment (opcode::memarg)
2535 try func.mir_extra.appendSlice(func.gpa, &[_]u32{2466 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2536 std.wasm.simdOpcode(.v128_load),2467 @intFromEnum(std.wasm.SimdOpcode.v128_load),
2537 offset + operand.offset(),2468 offset + operand.offset(),
2538 @intCast(ty.abiAlignment(zcu).toByteUnits().?),2469 @intCast(ty.abiAlignment(zcu).toByteUnits().?),
2539 });2470 });
...@@ -2664,7 +2595,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!...@@ -2664,7 +2595,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!
2664 }2595 }
2665 }2596 }
26662597
2667 const opcode: wasm.Opcode = buildOpcode(.{2598 const opcode: std.wasm.Opcode = buildOpcode(.{
2668 .op = op,2599 .op = op,
2669 .valtype1 = typeToValtype(ty, pt, func.target.*),2600 .valtype1 = typeToValtype(ty, pt, func.target.*),
2670 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,2601 .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned,
...@@ -2988,7 +2919,7 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {...@@ -2988,7 +2919,7 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue {
2988 },2919 },
2989 32, 64 => {2920 32, 64 => {
2990 try func.emitWValue(arg);2921 try func.emitWValue(arg);
2991 const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64;2922 const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64;
2992 const opcode = buildOpcode(.{ .op = .neg, .valtype1 = val_type });2923 const opcode = buildOpcode(.{ .op = .neg, .valtype1 = val_type });
2993 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));2924 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2994 return .stack;2925 return .stack;
...@@ -3197,20 +3128,14 @@ fn lowerUavRef(...@@ -3197,20 +3128,14 @@ fn lowerUavRef(
3197 return .{ .imm32 = 0xaaaaaaaa };3128 return .{ .imm32 = 0xaaaaaaaa };
3198 }3129 }
31993130
3200 const decl_align = zcu.intern_pool.indexToKey(uav.orig_ty).ptr_type.flags.alignment;3131 return if (is_fn_body) .{
3201 const res = try func.bin_file.lowerUav(pt, uav.val, decl_align, func.src_loc);3132 .function_index = uav.val,
3202 const target_sym_index = switch (res) {3133 } else if (offset == 0) .{
3203 .mcv => |mcv| mcv.load_symbol,3134 .memory = uav.val,
3204 .fail => |err_msg| {3135 } else .{ .memory_offset = .{
3205 func.err_msg = err_msg;3136 .pointer = uav.val,
3206 return error.CodegenFail;3137 .offset = offset,
3207 },3138 } };
3208 };
3209 if (is_fn_body) {
3210 return .{ .function_index = target_sym_index };
3211 } else if (offset == 0) {
3212 return .{ .memory = target_sym_index };
3213 } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
3214}3139}
32153140
3216fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue {3141fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue {
...@@ -3334,13 +3259,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3334,13 +3259,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3334 .f64 => |f64_val| return .{ .float64 = f64_val },3259 .f64 => |f64_val| return .{ .float64 = f64_val },
3335 else => unreachable,3260 else => unreachable,
3336 },3261 },
3337 .slice => switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) {3262 .slice => return .{ .memory = val.toIntern() },
3338 .mcv => |mcv| return .{ .memory = mcv.load_symbol },
3339 .fail => |err_msg| {
3340 func.err_msg = err_msg;
3341 return error.CodegenFail;
3342 },
3343 },
3344 .ptr => return func.lowerPtr(val.toIntern(), 0),3263 .ptr => return func.lowerPtr(val.toIntern(), 0),
3345 .opt => if (ty.optionalReprIsPayload(zcu)) {3264 .opt => if (ty.optionalReprIsPayload(zcu)) {
3346 const pl_ty = ty.optionalChild(zcu);3265 const pl_ty = ty.optionalChild(zcu);
...@@ -3489,12 +3408,12 @@ fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []cons...@@ -3489,12 +3408,12 @@ fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []cons
3489 const wasm_block_ty = genBlockType(block_ty, pt, func.target.*);3408 const wasm_block_ty = genBlockType(block_ty, pt, func.target.*);
34903409
3491 // if wasm_block_ty is non-empty, we create a register to store the temporary value3410 // if wasm_block_ty is non-empty, we create a register to store the temporary value
3492 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {3411 const block_result: WValue = if (wasm_block_ty != std.wasm.block_empty) blk: {
3493 const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty;3412 const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty;
3494 break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten3413 break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
3495 } else .none;3414 } else .none;
34963415
3497 try func.startBlock(.block, wasm.block_empty);3416 try func.startBlock(.block, std.wasm.block_empty);
3498 // Here we set the current block idx, so breaks know the depth to jump3417 // Here we set the current block idx, so breaks know the depth to jump
3499 // to when breaking out.3418 // to when breaking out.
3500 try func.blocks.putNoClobber(func.gpa, inst, .{3419 try func.blocks.putNoClobber(func.gpa, inst, .{
...@@ -3512,7 +3431,7 @@ fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []cons...@@ -3512,7 +3431,7 @@ fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []cons
3512}3431}
35133432
3514/// appends a new wasm block to the code section and increases the `block_depth` by 13433/// appends a new wasm block to the code section and increases the `block_depth` by 1
3515fn startBlock(func: *CodeGen, block_tag: wasm.Opcode, valtype: u8) !void {3434fn startBlock(func: *CodeGen, block_tag: std.wasm.Opcode, valtype: u8) !void {
3516 func.block_depth += 1;3435 func.block_depth += 1;
3517 try func.addInst(.{3436 try func.addInst(.{
3518 .tag = Mir.Inst.Tag.fromOpcode(block_tag),3437 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
...@@ -3533,7 +3452,7 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3533,7 +3452,7 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
35333452
3534 // result type of loop is always 'noreturn', meaning we can always3453 // result type of loop is always 'noreturn', meaning we can always
3535 // emit the wasm type 'block_empty'.3454 // emit the wasm type 'block_empty'.
3536 try func.startBlock(.loop, wasm.block_empty);3455 try func.startBlock(.loop, std.wasm.block_empty);
35373456
3538 try func.loops.putNoClobber(func.gpa, inst, func.block_depth);3457 try func.loops.putNoClobber(func.gpa, inst, func.block_depth);
3539 defer assert(func.loops.remove(inst));3458 defer assert(func.loops.remove(inst));
...@@ -3553,7 +3472,7 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3553,7 +3472,7 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3553 const liveness_condbr = func.liveness.getCondBr(inst);3472 const liveness_condbr = func.liveness.getCondBr(inst);
35543473
3555 // result type is always noreturn, so use `block_empty` as type.3474 // result type is always noreturn, so use `block_empty` as type.
3556 try func.startBlock(.block, wasm.block_empty);3475 try func.startBlock(.block, std.wasm.block_empty);
3557 // emit the conditional value3476 // emit the conditional value
3558 try func.emitWValue(condition);3477 try func.emitWValue(condition);
35593478
...@@ -3632,7 +3551,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO...@@ -3632,7 +3551,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
3632 try func.lowerToStack(lhs);3551 try func.lowerToStack(lhs);
3633 try func.lowerToStack(rhs);3552 try func.lowerToStack(rhs);
36343553
3635 const opcode: wasm.Opcode = buildOpcode(.{3554 const opcode: std.wasm.Opcode = buildOpcode(.{
3636 .valtype1 = typeToValtype(ty, pt, func.target.*),3555 .valtype1 = typeToValtype(ty, pt, func.target.*),
3637 .op = switch (op) {3556 .op = switch (op) {
3638 .lt => .lt,3557 .lt => .lt,
...@@ -3674,7 +3593,7 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math...@@ -3674,7 +3593,7 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math
3674 32, 64 => {3593 32, 64 => {
3675 try func.emitWValue(lhs);3594 try func.emitWValue(lhs);
3676 try func.emitWValue(rhs);3595 try func.emitWValue(rhs);
3677 const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64;3596 const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64;
3678 const opcode = buildOpcode(.{ .op = op, .valtype1 = val_type });3597 const opcode = buildOpcode(.{ .op = op, .valtype1 = val_type });
3679 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));3598 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3680 return .stack;3599 return .stack;
...@@ -4053,7 +3972,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4053,7 +3972,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4053 const pt = func.pt;3972 const pt = func.pt;
4054 const zcu = pt.zcu;3973 const zcu = pt.zcu;
4055 // result type is always 'noreturn'3974 // result type is always 'noreturn'
4056 const blocktype = wasm.block_empty;3975 const blocktype = std.wasm.block_empty;
4057 const switch_br = func.air.unwrapSwitch(inst);3976 const switch_br = func.air.unwrapSwitch(inst);
4058 const target = try func.resolveInst(switch_br.operand);3977 const target = try func.resolveInst(switch_br.operand);
4059 const target_ty = func.typeOf(switch_br.operand);3978 const target_ty = func.typeOf(switch_br.operand);
...@@ -4245,7 +4164,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4245,7 +4164,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4245 return func.finishAir(inst, .none, &.{});4164 return func.finishAir(inst, .none, &.{});
4246}4165}
42474166
4248fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {4167fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {
4249 const pt = func.pt;4168 const pt = func.pt;
4250 const zcu = pt.zcu;4169 const zcu = pt.zcu;
4251 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4170 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4467,7 +4386,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4467,7 +4386,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
4467 } else return func.load(operand, wanted, 0);4386 } else return func.load(operand, wanted, 0);
4468}4387}
44694388
4470fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {4389fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
4471 const pt = func.pt;4390 const pt = func.pt;
4472 const zcu = pt.zcu;4391 const zcu = pt.zcu;
4473 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4392 const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
...@@ -4481,7 +4400,7 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind:...@@ -4481,7 +4400,7 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind:
44814400
4482/// For a given type and operand, checks if it's considered `null`.4401/// For a given type and operand, checks if it's considered `null`.
4483/// NOTE: Leaves the result on the stack4402/// NOTE: Leaves the result on the stack
4484fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {4403fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: std.wasm.Opcode) InnerError!WValue {
4485 const pt = func.pt;4404 const pt = func.pt;
4486 const zcu = pt.zcu;4405 const zcu = pt.zcu;
4487 try func.emitWValue(operand);4406 try func.emitWValue(operand);
...@@ -4967,8 +4886,8 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue...@@ -4967,8 +4886,8 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue
4967 try func.addLabel(.local_set, end_ptr.local.value);4886 try func.addLabel(.local_set, end_ptr.local.value);
49684887
4969 // outer block to jump to when loop is done4888 // outer block to jump to when loop is done
4970 try func.startBlock(.block, wasm.block_empty);4889 try func.startBlock(.block, std.wasm.block_empty);
4971 try func.startBlock(.loop, wasm.block_empty);4890 try func.startBlock(.loop, std.wasm.block_empty);
49724891
4973 // check for condition for loop end4892 // check for condition for loop end
4974 try func.emitWValue(new_ptr);4893 try func.emitWValue(new_ptr);
...@@ -5022,11 +4941,11 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5022,11 +4941,11 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5022 try func.addTag(.i32_mul);4941 try func.addTag(.i32_mul);
5023 try func.addTag(.i32_add);4942 try func.addTag(.i32_add);
5024 } else {4943 } else {
5025 std.debug.assert(array_ty.zigTypeTag(zcu) == .vector);4944 assert(array_ty.zigTypeTag(zcu) == .vector);
50264945
5027 switch (index) {4946 switch (index) {
5028 inline .imm32, .imm64 => |lane| {4947 inline .imm32, .imm64 => |lane| {
5029 const opcode: wasm.SimdOpcode = switch (elem_ty.bitSize(zcu)) {4948 const opcode: std.wasm.SimdOpcode = switch (elem_ty.bitSize(zcu)) {
5030 8 => if (elem_ty.isSignedInt(zcu)) .i8x16_extract_lane_s else .i8x16_extract_lane_u,4949 8 => if (elem_ty.isSignedInt(zcu)) .i8x16_extract_lane_s else .i8x16_extract_lane_u,
5031 16 => if (elem_ty.isSignedInt(zcu)) .i16x8_extract_lane_s else .i16x8_extract_lane_u,4950 16 => if (elem_ty.isSignedInt(zcu)) .i16x8_extract_lane_s else .i16x8_extract_lane_u,
5032 32 => if (elem_ty.isInt(zcu)) .i32x4_extract_lane else .f32x4_extract_lane,4951 32 => if (elem_ty.isInt(zcu)) .i32x4_extract_lane else .f32x4_extract_lane,
...@@ -5034,7 +4953,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5034,7 +4953,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5034 else => unreachable,4953 else => unreachable,
5035 };4954 };
50364955
5037 var operands = [_]u32{ std.wasm.simdOpcode(opcode), @as(u8, @intCast(lane)) };4956 var operands = [_]u32{ @intFromEnum(opcode), @as(u8, @intCast(lane)) };
50384957
5039 try func.emitWValue(array);4958 try func.emitWValue(array);
50404959
...@@ -5171,10 +5090,10 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5171,10 +5090,10 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5171 // the scalar value onto the stack.5090 // the scalar value onto the stack.
5172 .stack_offset, .memory, .memory_offset => {5091 .stack_offset, .memory, .memory_offset => {
5173 const opcode = switch (elem_ty.bitSize(zcu)) {5092 const opcode = switch (elem_ty.bitSize(zcu)) {
5174 8 => std.wasm.simdOpcode(.v128_load8_splat),5093 8 => @intFromEnum(std.wasm.SimdOpcode.v128_load8_splat),
5175 16 => std.wasm.simdOpcode(.v128_load16_splat),5094 16 => @intFromEnum(std.wasm.SimdOpcode.v128_load16_splat),
5176 32 => std.wasm.simdOpcode(.v128_load32_splat),5095 32 => @intFromEnum(std.wasm.SimdOpcode.v128_load32_splat),
5177 64 => std.wasm.simdOpcode(.v128_load64_splat),5096 64 => @intFromEnum(std.wasm.SimdOpcode.v128_load64_splat),
5178 else => break :blk, // Cannot make use of simd-instructions5097 else => break :blk, // Cannot make use of simd-instructions
5179 };5098 };
5180 try func.emitWValue(operand);5099 try func.emitWValue(operand);
...@@ -5191,10 +5110,10 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5191,10 +5110,10 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5191 },5110 },
5192 .local => {5111 .local => {
5193 const opcode = switch (elem_ty.bitSize(zcu)) {5112 const opcode = switch (elem_ty.bitSize(zcu)) {
5194 8 => std.wasm.simdOpcode(.i8x16_splat),5113 8 => @intFromEnum(std.wasm.SimdOpcode.i8x16_splat),
5195 16 => std.wasm.simdOpcode(.i16x8_splat),5114 16 => @intFromEnum(std.wasm.SimdOpcode.i16x8_splat),
5196 32 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i32x4_splat) else std.wasm.simdOpcode(.f32x4_splat),5115 32 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i32x4_splat) else @intFromEnum(std.wasm.SimdOpcode.f32x4_splat),
5197 64 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i64x2_splat) else std.wasm.simdOpcode(.f64x2_splat),5116 64 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i64x2_splat) else @intFromEnum(std.wasm.SimdOpcode.f64x2_splat),
5198 else => break :blk, // Cannot make use of simd-instructions5117 else => break :blk, // Cannot make use of simd-instructions
5199 };5118 };
5200 try func.emitWValue(operand);5119 try func.emitWValue(operand);
...@@ -5267,7 +5186,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5267,7 +5186,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5267 return func.finishAir(inst, result, &.{ extra.a, extra.b });5186 return func.finishAir(inst, result, &.{ extra.a, extra.b });
5268 } else {5187 } else {
5269 var operands = [_]u32{5188 var operands = [_]u32{
5270 std.wasm.simdOpcode(.i8x16_shuffle),5189 @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle),
5271 } ++ [1]u32{undefined} ** 4;5190 } ++ [1]u32{undefined} ** 4;
52725191
5273 var lanes = mem.asBytes(operands[1..]);5192 var lanes = mem.asBytes(operands[1..]);
...@@ -5538,7 +5457,7 @@ fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op:...@@ -5538,7 +5457,7 @@ fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op:
5538 var result = try func.ensureAllocLocal(Type.i32);5457 var result = try func.ensureAllocLocal(Type.i32);
5539 defer result.free(func);5458 defer result.free(func);
55405459
5541 try func.startBlock(.block, wasm.block_empty);5460 try func.startBlock(.block, std.wasm.block_empty);
5542 _ = try func.isNull(lhs, operand_ty, .i32_eq);5461 _ = try func.isNull(lhs, operand_ty, .i32_eq);
5543 _ = try func.isNull(rhs, operand_ty, .i32_eq);5462 _ = try func.isNull(rhs, operand_ty, .i32_eq);
5544 try func.addTag(.i32_ne); // inverse so we can exit early5463 try func.addTag(.i32_ne); // inverse so we can exit early
...@@ -5678,7 +5597,7 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!...@@ -5678,7 +5597,7 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!
5678 Type.f32,5597 Type.f32,
5679 &.{operand},5598 &.{operand},
5680 );5599 );
5681 std.debug.assert(f32_result == .stack);5600 assert(f32_result == .stack);
56825601
5683 if (wanted_bits == 64) {5602 if (wanted_bits == 64) {
5684 try func.addTag(.f64_promote_f32);5603 try func.addTag(.f64_promote_f32);
...@@ -6557,7 +6476,7 @@ fn lowerTry(...@@ -6557,7 +6476,7 @@ fn lowerTry(
65576476
6558 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {6477 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
6559 // Block we can jump out of when error is not set6478 // Block we can jump out of when error is not set
6560 try func.startBlock(.block, wasm.block_empty);6479 try func.startBlock(.block, std.wasm.block_empty);
65616480
6562 // check if the error tag is set for the error union.6481 // check if the error tag is set for the error union.
6563 try func.emitWValue(err_union);6482 try func.emitWValue(err_union);
...@@ -7162,17 +7081,13 @@ fn callIntrinsic(...@@ -7162,17 +7081,13 @@ fn callIntrinsic(
7162 args: []const WValue,7081 args: []const WValue,
7163) InnerError!WValue {7082) InnerError!WValue {
7164 assert(param_types.len == args.len);7083 assert(param_types.len == args.len);
7165 const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| {7084 const wasm = func.bin_file;
7166 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
7167 };
7168
7169 // Always pass over C-ABI
7170 const pt = func.pt;7085 const pt = func.pt;
7171 const zcu = pt.zcu;7086 const zcu = pt.zcu;
7172 var func_type = try genFunctype(func.gpa, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*);7087 const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*);
7173 defer func_type.deinit(func.gpa);7088 const func_index = wasm.getOutputFunction(try wasm.internString(name), func_type_index);
7174 const func_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, func_type);7089
7175 try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);7090 // Always pass over C-ABI
71767091
7177 const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*);7092 const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*);
7178 // if we want return as first param, we allocate a pointer to stack,7093 // if we want return as first param, we allocate a pointer to stack,
...@@ -7191,7 +7106,7 @@ fn callIntrinsic(...@@ -7191,7 +7106,7 @@ fn callIntrinsic(
7191 }7106 }
71927107
7193 // Actually call our intrinsic7108 // Actually call our intrinsic
7194 try func.addLabel(.call, @intFromEnum(symbol_index));7109 try func.addLabel(.call_func, func_index);
71957110
7196 if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) {7111 if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
7197 return .none;7112 return .none;
...@@ -7210,177 +7125,14 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7210,177 +7125,14 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7210 const operand = try func.resolveInst(un_op);7125 const operand = try func.resolveInst(un_op);
7211 const enum_ty = func.typeOf(un_op);7126 const enum_ty = func.typeOf(un_op);
72127127
7213 const func_sym_index = try func.getTagNameFunction(enum_ty);
7214
7215 const result_ptr = try func.allocStack(func.typeOfIndex(inst));7128 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
7216 try func.lowerToStack(result_ptr);7129 try func.lowerToStack(result_ptr);
7217 try func.emitWValue(operand);7130 try func.emitWValue(operand);
7218 try func.addLabel(.call, func_sym_index);7131 try func.addCallTagName(enum_ty.toIntern());
72197132
7220 return func.finishAir(inst, result_ptr, &.{un_op});7133 return func.finishAir(inst, result_ptr, &.{un_op});
7221}7134}
72227135
7223fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
7224 const pt = func.pt;
7225 const zcu = pt.zcu;
7226 const ip = &zcu.intern_pool;
7227
7228 var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);
7229 defer arena_allocator.deinit();
7230 const arena = arena_allocator.allocator();
7231
7232 const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{ip.loadEnumType(enum_ty.toIntern()).name.fmt(ip)});
7233
7234 // check if we already generated code for this.
7235 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
7236 return @intFromEnum(loc.index);
7237 }
7238
7239 const int_tag_ty = enum_ty.intTagType(zcu);
7240
7241 if (int_tag_ty.bitSize(zcu) > 64) {
7242 return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{});
7243 }
7244
7245 var relocs = std.ArrayList(link.File.Wasm.Relocation).init(func.gpa);
7246 defer relocs.deinit();
7247
7248 var body_list = std.ArrayList(u8).init(func.gpa);
7249 defer body_list.deinit();
7250 var writer = body_list.writer();
7251
7252 // The locals of the function body (always 0)
7253 try leb.writeUleb128(writer, @as(u32, 0));
7254
7255 // outer block
7256 try writer.writeByte(std.wasm.opcode(.block));
7257 try writer.writeByte(std.wasm.block_empty);
7258
7259 // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse.
7260 // generate an if-else chain for each tag value as well as constant.
7261 const tag_names = enum_ty.enumFields(zcu);
7262 for (0..tag_names.len) |tag_index| {
7263 const tag_name = tag_names.get(ip)[tag_index];
7264 const tag_name_len = tag_name.length(ip);
7265 // for each tag name, create an unnamed const,
7266 // and then get a pointer to its value.
7267 const name_ty = try pt.arrayType(.{
7268 .len = tag_name_len,
7269 .child = .u8_type,
7270 .sentinel = .zero_u8,
7271 });
7272 const name_val = try pt.intern(.{ .aggregate = .{
7273 .ty = name_ty.toIntern(),
7274 .storage = .{ .bytes = tag_name.toString() },
7275 } });
7276 const tag_sym_index = switch (try func.bin_file.lowerUav(pt, name_val, .none, func.src_loc)) {
7277 .mcv => |mcv| mcv.load_symbol,
7278 .fail => |err_msg| {
7279 func.err_msg = err_msg;
7280 return error.CodegenFail;
7281 },
7282 };
7283
7284 // block for this if case
7285 try writer.writeByte(std.wasm.opcode(.block));
7286 try writer.writeByte(std.wasm.block_empty);
7287
7288 // get actual tag value (stored in 2nd parameter);
7289 try writer.writeByte(std.wasm.opcode(.local_get));
7290 try leb.writeUleb128(writer, @as(u32, 1));
7291
7292 const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index));
7293 const tag_value = try func.lowerConstant(tag_val, enum_ty);
7294
7295 switch (tag_value) {
7296 .imm32 => |value| {
7297 try writer.writeByte(std.wasm.opcode(.i32_const));
7298 try leb.writeIleb128(writer, @as(i32, @bitCast(value)));
7299 try writer.writeByte(std.wasm.opcode(.i32_ne));
7300 },
7301 .imm64 => |value| {
7302 try writer.writeByte(std.wasm.opcode(.i64_const));
7303 try leb.writeIleb128(writer, @as(i64, @bitCast(value)));
7304 try writer.writeByte(std.wasm.opcode(.i64_ne));
7305 },
7306 else => unreachable,
7307 }
7308 // if they're not equal, break out of current branch
7309 try writer.writeByte(std.wasm.opcode(.br_if));
7310 try leb.writeUleb128(writer, @as(u32, 0));
7311
7312 // store the address of the tagname in the pointer field of the slice
7313 // get the address twice so we can also store the length.
7314 try writer.writeByte(std.wasm.opcode(.local_get));
7315 try leb.writeUleb128(writer, @as(u32, 0));
7316 try writer.writeByte(std.wasm.opcode(.local_get));
7317 try leb.writeUleb128(writer, @as(u32, 0));
7318
7319 // get address of tagname and emit a relocation to it
7320 if (func.arch() == .wasm32) {
7321 const encoded_alignment = @ctz(@as(u32, 4));
7322 try writer.writeByte(std.wasm.opcode(.i32_const));
7323 try relocs.append(.{
7324 .relocation_type = .R_WASM_MEMORY_ADDR_LEB,
7325 .offset = @as(u32, @intCast(body_list.items.len)),
7326 .index = tag_sym_index,
7327 });
7328 try writer.writeAll(&[_]u8{0} ** 5); // will be relocated
7329
7330 // store pointer
7331 try writer.writeByte(std.wasm.opcode(.i32_store));
7332 try leb.writeUleb128(writer, encoded_alignment);
7333 try leb.writeUleb128(writer, @as(u32, 0));
7334
7335 // store length
7336 try writer.writeByte(std.wasm.opcode(.i32_const));
7337 try leb.writeUleb128(writer, @as(u32, @intCast(tag_name_len)));
7338 try writer.writeByte(std.wasm.opcode(.i32_store));
7339 try leb.writeUleb128(writer, encoded_alignment);
7340 try leb.writeUleb128(writer, @as(u32, 4));
7341 } else {
7342 const encoded_alignment = @ctz(@as(u32, 8));
7343 try writer.writeByte(std.wasm.opcode(.i64_const));
7344 try relocs.append(.{
7345 .relocation_type = .R_WASM_MEMORY_ADDR_LEB64,
7346 .offset = @as(u32, @intCast(body_list.items.len)),
7347 .index = tag_sym_index,
7348 });
7349 try writer.writeAll(&[_]u8{0} ** 10); // will be relocated
7350
7351 // store pointer
7352 try writer.writeByte(std.wasm.opcode(.i64_store));
7353 try leb.writeUleb128(writer, encoded_alignment);
7354 try leb.writeUleb128(writer, @as(u32, 0));
7355
7356 // store length
7357 try writer.writeByte(std.wasm.opcode(.i64_const));
7358 try leb.writeUleb128(writer, @as(u64, @intCast(tag_name_len)));
7359 try writer.writeByte(std.wasm.opcode(.i64_store));
7360 try leb.writeUleb128(writer, encoded_alignment);
7361 try leb.writeUleb128(writer, @as(u32, 8));
7362 }
7363
7364 // break outside blocks
7365 try writer.writeByte(std.wasm.opcode(.br));
7366 try leb.writeUleb128(writer, @as(u32, 1));
7367
7368 // end the block for this case
7369 try writer.writeByte(std.wasm.opcode(.end));
7370 }
7371
7372 try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name
7373 // finish outer block
7374 try writer.writeByte(std.wasm.opcode(.end));
7375 // finish function body
7376 try writer.writeByte(std.wasm.opcode(.end));
7377
7378 const slice_ty = Type.slice_const_u8_sentinel_0;
7379 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, pt, func.target.*);
7380 const sym_index = try func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
7381 return @intFromEnum(sym_index);
7382}
7383
7384fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {7136fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7385 const pt = func.pt;7137 const pt = func.pt;
7386 const zcu = pt.zcu;7138 const zcu = pt.zcu;
...@@ -7418,11 +7170,11 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7418,11 +7170,11 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7418 }7170 }
74197171
7420 // start block for 'true' branch7172 // start block for 'true' branch
7421 try func.startBlock(.block, wasm.block_empty);7173 try func.startBlock(.block, std.wasm.block_empty);
7422 // start block for 'false' branch7174 // start block for 'false' branch
7423 try func.startBlock(.block, wasm.block_empty);7175 try func.startBlock(.block, std.wasm.block_empty);
7424 // block for the jump table itself7176 // block for the jump table itself
7425 try func.startBlock(.block, wasm.block_empty);7177 try func.startBlock(.block, std.wasm.block_empty);
74267178
7427 // lower operand to determine jump table target7179 // lower operand to determine jump table target
7428 try func.emitWValue(operand);7180 try func.emitWValue(operand);
...@@ -7549,7 +7301,7 @@ fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7549,7 +7301,7 @@ fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7549 const ty = func.typeOfIndex(inst);7301 const ty = func.typeOfIndex(inst);
75507302
7551 if (func.useAtomicFeature()) {7303 if (func.useAtomicFeature()) {
7552 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(pt.zcu)) {7304 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(pt.zcu)) {
7553 1 => .i32_atomic_load8_u,7305 1 => .i32_atomic_load8_u,
7554 2 => .i32_atomic_load16_u,7306 2 => .i32_atomic_load16_u,
7555 4 => .i32_atomic_load,7307 4 => .i32_atomic_load,
...@@ -7589,7 +7341,7 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7589,7 +7341,7 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7589 const value = try tmp.toLocal(func, ty);7341 const value = try tmp.toLocal(func, ty);
75907342
7591 // create a loop to cmpxchg the new value7343 // create a loop to cmpxchg the new value
7592 try func.startBlock(.loop, wasm.block_empty);7344 try func.startBlock(.loop, std.wasm.block_empty);
75937345
7594 try func.emitWValue(ptr);7346 try func.emitWValue(ptr);
7595 try func.emitWValue(value);7347 try func.emitWValue(value);
...@@ -7639,7 +7391,7 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7639,7 +7391,7 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7639 else => {7391 else => {
7640 try func.emitWValue(ptr);7392 try func.emitWValue(ptr);
7641 try func.emitWValue(operand);7393 try func.emitWValue(operand);
7642 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {7394 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7643 1 => switch (op) {7395 1 => switch (op) {
7644 .Xchg => .i32_atomic_rmw8_xchg_u,7396 .Xchg => .i32_atomic_rmw8_xchg_u,
7645 .Add => .i32_atomic_rmw8_add_u,7397 .Add => .i32_atomic_rmw8_add_u,
...@@ -7754,7 +7506,7 @@ fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7754,7 +7506,7 @@ fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7754 const ty = ptr_ty.childType(zcu);7506 const ty = ptr_ty.childType(zcu);
77557507
7756 if (func.useAtomicFeature()) {7508 if (func.useAtomicFeature()) {
7757 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {7509 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
7758 1 => .i32_atomic_store8,7510 1 => .i32_atomic_store8,
7759 2 => .i32_atomic_store16,7511 2 => .i32_atomic_store16,
7760 4 => .i32_atomic_store,7512 4 => .i32_atomic_store,
src/arch/wasm/Emit.zig+57-60
...@@ -3,12 +3,13 @@...@@ -3,12 +3,13 @@
33
4const Emit = @This();4const Emit = @This();
5const std = @import("std");5const std = @import("std");
6const leb128 = std.leb;
7
6const Mir = @import("Mir.zig");8const Mir = @import("Mir.zig");
7const link = @import("../../link.zig");9const link = @import("../../link.zig");
8const Zcu = @import("../../Zcu.zig");10const Zcu = @import("../../Zcu.zig");
9const InternPool = @import("../../InternPool.zig");11const InternPool = @import("../../InternPool.zig");
10const codegen = @import("../../codegen.zig");12const codegen = @import("../../codegen.zig");
11const leb128 = std.leb;
1213
13/// Contains our list of instructions14/// Contains our list of instructions
14mir: Mir,15mir: Mir,
...@@ -254,7 +255,8 @@ fn offset(self: Emit) u32 {...@@ -254,7 +255,8 @@ fn offset(self: Emit) u32 {
254fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {255fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
255 @branchHint(.cold);256 @branchHint(.cold);
256 std.debug.assert(emit.error_msg == null);257 std.debug.assert(emit.error_msg == null);
257 const comp = emit.bin_file.base.comp;258 const wasm = emit.bin_file;
259 const comp = wasm.base.comp;
258 const zcu = comp.zcu.?;260 const zcu = comp.zcu.?;
259 const gpa = comp.gpa;261 const gpa = comp.gpa;
260 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args);262 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args);
...@@ -287,7 +289,7 @@ fn emitBrTable(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -287,7 +289,7 @@ fn emitBrTable(emit: *Emit, inst: Mir.Inst.Index) !void {
287 const labels = emit.mir.extra[extra.end..][0..extra.data.length];289 const labels = emit.mir.extra[extra.end..][0..extra.data.length];
288 const writer = emit.code.writer();290 const writer = emit.code.writer();
289291
290 try emit.code.append(std.wasm.opcode(.br_table));292 try emit.code.append(@intFromEnum(std.wasm.Opcode.br_table));
291 try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth293 try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth
292 for (labels) |label| {294 for (labels) |label| {
293 try leb128.writeUleb128(writer, label);295 try leb128.writeUleb128(writer, label);
...@@ -301,7 +303,8 @@ fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -301,7 +303,8 @@ fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
301}303}
302304
303fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {305fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
304 const comp = emit.bin_file.base.comp;306 const wasm = emit.bin_file;
307 const comp = wasm.base.comp;
305 const gpa = comp.gpa;308 const gpa = comp.gpa;
306 const label = emit.mir.instructions.items(.data)[inst].label;309 const label = emit.mir.instructions.items(.data)[inst].label;
307 try emit.code.append(@intFromEnum(tag));310 try emit.code.append(@intFromEnum(tag));
...@@ -310,38 +313,38 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -310,38 +313,38 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
310 const global_offset = emit.offset();313 const global_offset = emit.offset();
311 try emit.code.appendSlice(&buf);314 try emit.code.appendSlice(&buf);
312315
313 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;316 const zo = wasm.zig_object.?;
314 const atom = emit.bin_file.getAtomPtr(atom_index);317 try zo.relocs.append(gpa, .{
315 try atom.relocs.append(gpa, .{318 .nav_index = emit.nav_index,
316 .index = label,319 .index = label,
317 .offset = global_offset,320 .offset = global_offset,
318 .relocation_type = .R_WASM_GLOBAL_INDEX_LEB,321 .tag = .GLOBAL_INDEX_LEB,
319 });322 });
320}323}
321324
322fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void {325fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void {
323 const value: i32 = emit.mir.instructions.items(.data)[inst].imm32;326 const value: i32 = emit.mir.instructions.items(.data)[inst].imm32;
324 try emit.code.append(std.wasm.opcode(.i32_const));327 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));
325 try leb128.writeIleb128(emit.code.writer(), value);328 try leb128.writeIleb128(emit.code.writer(), value);
326}329}
327330
328fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void {331fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void {
329 const extra_index = emit.mir.instructions.items(.data)[inst].payload;332 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
330 const value = emit.mir.extraData(Mir.Imm64, extra_index);333 const value = emit.mir.extraData(Mir.Imm64, extra_index);
331 try emit.code.append(std.wasm.opcode(.i64_const));334 try emit.code.append(@intFromEnum(std.wasm.Opcode.i64_const));
332 try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64())));335 try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64())));
333}336}
334337
335fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void {338fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void {
336 const value: f32 = emit.mir.instructions.items(.data)[inst].float32;339 const value: f32 = emit.mir.instructions.items(.data)[inst].float32;
337 try emit.code.append(std.wasm.opcode(.f32_const));340 try emit.code.append(@intFromEnum(std.wasm.Opcode.f32_const));
338 try emit.code.writer().writeInt(u32, @bitCast(value), .little);341 try emit.code.writer().writeInt(u32, @bitCast(value), .little);
339}342}
340343
341fn emitFloat64(emit: *Emit, inst: Mir.Inst.Index) !void {344fn emitFloat64(emit: *Emit, inst: Mir.Inst.Index) !void {
342 const extra_index = emit.mir.instructions.items(.data)[inst].payload;345 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
343 const value = emit.mir.extraData(Mir.Float64, extra_index);346 const value = emit.mir.extraData(Mir.Float64, extra_index);
344 try emit.code.append(std.wasm.opcode(.f64_const));347 try emit.code.append(@intFromEnum(std.wasm.Opcode.f64_const));
345 try emit.code.writer().writeInt(u64, value.data.toU64(), .little);348 try emit.code.writer().writeInt(u64, value.data.toU64(), .little);
346}349}
347350
...@@ -360,105 +363,99 @@ fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {...@@ -360,105 +363,99 @@ fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {
360}363}
361364
362fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {365fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
363 const comp = emit.bin_file.base.comp;366 const wasm = emit.bin_file;
367 const comp = wasm.base.comp;
364 const gpa = comp.gpa;368 const gpa = comp.gpa;
365 const label = emit.mir.instructions.items(.data)[inst].label;369 const label = emit.mir.instructions.items(.data)[inst].label;
366 try emit.code.append(std.wasm.opcode(.call));370 try emit.code.append(@intFromEnum(std.wasm.Opcode.call));
367 const call_offset = emit.offset();371 const call_offset = emit.offset();
368 var buf: [5]u8 = undefined;372 var buf: [5]u8 = undefined;
369 leb128.writeUnsignedFixed(5, &buf, label);373 leb128.writeUnsignedFixed(5, &buf, label);
370 try emit.code.appendSlice(&buf);374 try emit.code.appendSlice(&buf);
371375
372 if (label != 0) {376 const zo = wasm.zig_object.?;
373 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;377 try zo.relocs.append(gpa, .{
374 const atom = emit.bin_file.getAtomPtr(atom_index);378 .offset = call_offset,
375 try atom.relocs.append(gpa, .{379 .index = label,
376 .offset = call_offset,380 .tag = .FUNCTION_INDEX_LEB,
377 .index = label,381 });
378 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
379 });
380 }
381}382}
382383
383fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {384fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
385 const wasm = emit.bin_file;
384 const type_index = emit.mir.instructions.items(.data)[inst].label;386 const type_index = emit.mir.instructions.items(.data)[inst].label;
385 try emit.code.append(std.wasm.opcode(.call_indirect));387 try emit.code.append(@intFromEnum(std.wasm.Opcode.call_indirect));
386 // NOTE: If we remove unused function types in the future for incremental388 // NOTE: If we remove unused function types in the future for incremental
387 // linking, we must also emit a relocation for this `type_index`389 // linking, we must also emit a relocation for this `type_index`
388 const call_offset = emit.offset();390 const call_offset = emit.offset();
389 var buf: [5]u8 = undefined;391 var buf: [5]u8 = undefined;
390 leb128.writeUnsignedFixed(5, &buf, type_index);392 leb128.writeUnsignedFixed(5, &buf, type_index);
391 try emit.code.appendSlice(&buf);393 try emit.code.appendSlice(&buf);
392 if (type_index != 0) {394
393 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;395 const zo = wasm.zig_object.?;
394 const atom = emit.bin_file.getAtomPtr(atom_index);396 try zo.relocs.append(wasm.base.comp.gpa, .{
395 try atom.relocs.append(emit.bin_file.base.comp.gpa, .{397 .offset = call_offset,
396 .offset = call_offset,398 .index = type_index,
397 .index = type_index,399 .tag = .TYPE_INDEX_LEB,
398 .relocation_type = .R_WASM_TYPE_INDEX_LEB,400 });
399 });401
400 }
401 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index402 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
402}403}
403404
404fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {405fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
405 const comp = emit.bin_file.base.comp;406 const wasm = emit.bin_file;
407 const comp = wasm.base.comp;
406 const gpa = comp.gpa;408 const gpa = comp.gpa;
407 const symbol_index = emit.mir.instructions.items(.data)[inst].label;409 const symbol_index = emit.mir.instructions.items(.data)[inst].label;
408 try emit.code.append(std.wasm.opcode(.i32_const));410 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));
409 const index_offset = emit.offset();411 const index_offset = emit.offset();
410 var buf: [5]u8 = undefined;412 var buf: [5]u8 = undefined;
411 leb128.writeUnsignedFixed(5, &buf, symbol_index);413 leb128.writeUnsignedFixed(5, &buf, symbol_index);
412 try emit.code.appendSlice(&buf);414 try emit.code.appendSlice(&buf);
413415
414 if (symbol_index != 0) {416 const zo = wasm.zig_object.?;
415 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;417 try zo.relocs.append(gpa, .{
416 const atom = emit.bin_file.getAtomPtr(atom_index);418 .offset = index_offset,
417 try atom.relocs.append(gpa, .{419 .index = symbol_index,
418 .offset = index_offset,420 .tag = .TABLE_INDEX_SLEB,
419 .index = symbol_index,421 });
420 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
421 });
422 }
423}422}
424423
425fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {424fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
425 const wasm = emit.bin_file;
426 const extra_index = emit.mir.instructions.items(.data)[inst].payload;426 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
427 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;427 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
428 const mem_offset = emit.offset() + 1;428 const mem_offset = emit.offset() + 1;
429 const comp = emit.bin_file.base.comp;429 const comp = wasm.base.comp;
430 const gpa = comp.gpa;430 const gpa = comp.gpa;
431 const target = comp.root_mod.resolved_target.result;431 const target = comp.root_mod.resolved_target.result;
432 const is_wasm32 = target.cpu.arch == .wasm32;432 const is_wasm32 = target.cpu.arch == .wasm32;
433 if (is_wasm32) {433 if (is_wasm32) {
434 try emit.code.append(std.wasm.opcode(.i32_const));434 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));
435 var buf: [5]u8 = undefined;435 var buf: [5]u8 = undefined;
436 leb128.writeUnsignedFixed(5, &buf, mem.pointer);436 leb128.writeUnsignedFixed(5, &buf, mem.pointer);
437 try emit.code.appendSlice(&buf);437 try emit.code.appendSlice(&buf);
438 } else {438 } else {
439 try emit.code.append(std.wasm.opcode(.i64_const));439 try emit.code.append(@intFromEnum(std.wasm.Opcode.i64_const));
440 var buf: [10]u8 = undefined;440 var buf: [10]u8 = undefined;
441 leb128.writeUnsignedFixed(10, &buf, mem.pointer);441 leb128.writeUnsignedFixed(10, &buf, mem.pointer);
442 try emit.code.appendSlice(&buf);442 try emit.code.appendSlice(&buf);
443 }443 }
444444
445 if (mem.pointer != 0) {445 const zo = wasm.zig_object.?;
446 const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom;446 try zo.relocs.append(gpa, .{
447 const atom = emit.bin_file.getAtomPtr(atom_index);447 .offset = mem_offset,
448 try atom.relocs.append(gpa, .{448 .index = mem.pointer,
449 .offset = mem_offset,449 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
450 .index = mem.pointer,450 .addend = @as(i32, @intCast(mem.offset)),
451 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,451 });
452 .addend = @as(i32, @intCast(mem.offset)),
453 });
454 }
455}452}
456453
457fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {454fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
458 const extra_index = emit.mir.instructions.items(.data)[inst].payload;455 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
459 const opcode = emit.mir.extra[extra_index];456 const opcode = emit.mir.extra[extra_index];
460 const writer = emit.code.writer();457 const writer = emit.code.writer();
461 try emit.code.append(std.wasm.opcode(.misc_prefix));458 try emit.code.append(@intFromEnum(std.wasm.Opcode.misc_prefix));
462 try leb128.writeUleb128(writer, opcode);459 try leb128.writeUleb128(writer, opcode);
463 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {460 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
464 // bulk-memory opcodes461 // bulk-memory opcodes
...@@ -497,7 +494,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -497,7 +494,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
497 const extra_index = emit.mir.instructions.items(.data)[inst].payload;494 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
498 const opcode = emit.mir.extra[extra_index];495 const opcode = emit.mir.extra[extra_index];
499 const writer = emit.code.writer();496 const writer = emit.code.writer();
500 try emit.code.append(std.wasm.opcode(.simd_prefix));497 try emit.code.append(@intFromEnum(std.wasm.Opcode.simd_prefix));
501 try leb128.writeUleb128(writer, opcode);498 try leb128.writeUleb128(writer, opcode);
502 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {499 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {
503 .v128_store,500 .v128_store,
...@@ -548,7 +545,7 @@ fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -548,7 +545,7 @@ fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
548 const extra_index = emit.mir.instructions.items(.data)[inst].payload;545 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
549 const opcode = emit.mir.extra[extra_index];546 const opcode = emit.mir.extra[extra_index];
550 const writer = emit.code.writer();547 const writer = emit.code.writer();
551 try emit.code.append(std.wasm.opcode(.atomics_prefix));548 try emit.code.append(@intFromEnum(std.wasm.Opcode.atomics_prefix));
552 try leb128.writeUleb128(writer, opcode);549 try leb128.writeUleb128(writer, opcode);
553 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {550 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {
554 .i32_atomic_load,551 .i32_atomic_load,
src/arch/wasm/Mir.zig+16-9
...@@ -7,6 +7,8 @@...@@ -7,6 +7,8 @@
7//! and known jump labels for blocks.7//! and known jump labels for blocks.
88
9const Mir = @This();9const Mir = @This();
10const InternPool = @import("../../InternPool.zig");
11const Wasm = @import("../../link/Wasm.zig");
1012
11const std = @import("std");13const std = @import("std");
1214
...@@ -78,22 +80,23 @@ pub const Inst = struct {...@@ -78,22 +80,23 @@ pub const Inst = struct {
78 ///80 ///
79 /// Uses `nop`81 /// Uses `nop`
80 @"return" = 0x0F,82 @"return" = 0x0F,
81 /// Calls a function by its index83 /// Calls a function using `nav_index`.
82 ///84 call_nav,
83 /// Uses `label`85 /// Calls a function using `func_index`.
84 call = 0x10,86 call_func,
85 /// Calls a function pointer by its function signature87 /// Calls a function pointer by its function signature
86 /// and index into the function table.88 /// and index into the function table.
87 ///89 ///
88 /// Uses `label`90 /// Uses `label`
89 call_indirect = 0x11,91 call_indirect = 0x11,
92 /// Calls a function by its index.
93 ///
94 /// The function is the auto-generated tag name function for the type
95 /// provided in `ip_index`.
96 call_tag_name,
90 /// Contains a symbol to a function pointer97 /// Contains a symbol to a function pointer
91 /// uses `label`98 /// uses `label`
92 ///99 function_index,
93 /// Note: This uses `0x16` as value which is reserved by the WebAssembly
94 /// specification but unused, meaning we must update this if the specification were to
95 /// use this value.
96 function_index = 0x16,
97 /// Pops three values from the stack and pushes100 /// Pops three values from the stack and pushes
98 /// the first or second value dependent on the third value.101 /// the first or second value dependent on the third value.
99 /// Uses `tag`102 /// Uses `tag`
...@@ -580,6 +583,10 @@ pub const Inst = struct {...@@ -580,6 +583,10 @@ pub const Inst = struct {
580 ///583 ///
581 /// Used by e.g. `br_table`584 /// Used by e.g. `br_table`
582 payload: u32,585 payload: u32,
586
587 ip_index: InternPool.Index,
588 nav_index: InternPool.Nav.Index,
589 func_index: Wasm.FunctionIndex,
583 };590 };
584};591};
585592
src/codegen.zig+55-16
...@@ -25,18 +25,19 @@ const Alignment = InternPool.Alignment;...@@ -25,18 +25,19 @@ const Alignment = InternPool.Alignment;
25const dev = @import("dev.zig");25const dev = @import("dev.zig");
2626
27pub const Result = union(enum) {27pub const Result = union(enum) {
28 /// The `code` parameter passed to `generateSymbol` has the value ok.28 /// The `code` parameter passed to `generateSymbol` has the value.
29 ok,29 ok,
30
31 /// There was a codegen error.30 /// There was a codegen error.
32 fail: *ErrorMsg,31 fail: *ErrorMsg,
33};32};
3433
35pub const CodeGenError = error{34pub const CodeGenError = error{
36 OutOfMemory,35 OutOfMemory,
36 /// Compiler was asked to operate on a number larger than supported.
37 Overflow,37 Overflow,
38 /// Indicates the error is already stored in Zcu `failed_codegen`.
38 CodegenFail,39 CodegenFail,
39} || link.File.UpdateDebugInfoError;40};
4041
41fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature {42fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature {
42 comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_"));43 comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_"));
...@@ -49,7 +50,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type {...@@ -49,7 +50,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type {
49 .stage2_arm => @import("arch/arm/CodeGen.zig"),50 .stage2_arm => @import("arch/arm/CodeGen.zig"),
50 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),51 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),
51 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),52 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),
52 .stage2_wasm => @import("arch/wasm/CodeGen.zig"),
53 .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"),53 .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"),
54 else => unreachable,54 else => unreachable,
55 };55 };
...@@ -74,7 +74,6 @@ pub fn generateFunction(...@@ -74,7 +74,6 @@ pub fn generateFunction(
74 .stage2_arm,74 .stage2_arm,
75 .stage2_riscv64,75 .stage2_riscv64,
76 .stage2_sparc64,76 .stage2_sparc64,
77 .stage2_wasm,
78 .stage2_x86_64,77 .stage2_x86_64,
79 => |backend| {78 => |backend| {
80 dev.check(devFeatureForBackend(backend));79 dev.check(devFeatureForBackend(backend));
...@@ -96,9 +95,7 @@ pub fn generateLazyFunction(...@@ -96,9 +95,7 @@ pub fn generateLazyFunction(
96 const target = zcu.fileByIndex(file).mod.resolved_target.result;95 const target = zcu.fileByIndex(file).mod.resolved_target.result;
97 switch (target_util.zigBackend(target, false)) {96 switch (target_util.zigBackend(target, false)) {
98 else => unreachable,97 else => unreachable,
99 inline .stage2_x86_64,98 inline .stage2_x86_64, .stage2_riscv64 => |backend| {
100 .stage2_riscv64,
101 => |backend| {
102 dev.check(devFeatureForBackend(backend));99 dev.check(devFeatureForBackend(backend));
103 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);100 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
104 },101 },
...@@ -694,6 +691,7 @@ fn lowerUavRef(...@@ -694,6 +691,7 @@ fn lowerUavRef(
694 offset: u64,691 offset: u64,
695) CodeGenError!Result {692) CodeGenError!Result {
696 const zcu = pt.zcu;693 const zcu = pt.zcu;
694 const gpa = zcu.gpa;
697 const ip = &zcu.intern_pool;695 const ip = &zcu.intern_pool;
698 const target = lf.comp.root_mod.resolved_target.result;696 const target = lf.comp.root_mod.resolved_target.result;
699697
...@@ -704,7 +702,7 @@ fn lowerUavRef(...@@ -704,7 +702,7 @@ fn lowerUavRef(
704 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";702 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
705 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {703 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {
706 try code.appendNTimes(0xaa, ptr_width_bytes);704 try code.appendNTimes(0xaa, ptr_width_bytes);
707 return Result.ok;705 return .ok;
708 }706 }
709707
710 const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;708 const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;
...@@ -714,6 +712,26 @@ fn lowerUavRef(...@@ -714,6 +712,26 @@ fn lowerUavRef(
714 .fail => |em| return .{ .fail = em },712 .fail => |em| return .{ .fail = em },
715 }713 }
716714
715 switch (lf.tag) {
716 .c => unreachable,
717 .spirv => unreachable,
718 .nvptx => unreachable,
719 .wasm => {
720 dev.check(link.File.Tag.wasm.devFeature());
721 const wasm = lf.cast(.wasm).?;
722 assert(reloc_parent == .none);
723 try wasm.relocations.append(gpa, .{
724 .tag = .uav_index,
725 .addend = @intCast(offset),
726 .offset = @intCast(code.items.len),
727 .pointee = .{ .uav_index = uav.val },
728 });
729 try code.appendNTimes(0, ptr_width_bytes);
730 return .ok;
731 },
732 else => {},
733 }
734
717 const vaddr = try lf.getUavVAddr(uav_val, .{735 const vaddr = try lf.getUavVAddr(uav_val, .{
718 .parent = reloc_parent,736 .parent = reloc_parent,
719 .offset = code.items.len,737 .offset = code.items.len,
...@@ -741,31 +759,52 @@ fn lowerNavRef(...@@ -741,31 +759,52 @@ fn lowerNavRef(
741) CodeGenError!Result {759) CodeGenError!Result {
742 _ = src_loc;760 _ = src_loc;
743 const zcu = pt.zcu;761 const zcu = pt.zcu;
762 const gpa = zcu.gpa;
744 const ip = &zcu.intern_pool;763 const ip = &zcu.intern_pool;
745 const target = zcu.navFileScope(nav_index).mod.resolved_target.result;764 const target = zcu.navFileScope(nav_index).mod.resolved_target.result;
746765
747 const ptr_width = target.ptrBitWidth();766 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8);
748 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));767 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));
749 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";768 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
750 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {769 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {
751 try code.appendNTimes(0xaa, @divExact(ptr_width, 8));770 try code.appendNTimes(0xaa, ptr_width_bytes);
752 return Result.ok;771 return Result.ok;
753 }772 }
754773
774 switch (lf.tag) {
775 .c => unreachable,
776 .spirv => unreachable,
777 .nvptx => unreachable,
778 .wasm => {
779 dev.check(link.File.Tag.wasm.devFeature());
780 const wasm = lf.cast(.wasm).?;
781 assert(reloc_parent == .none);
782 try wasm.relocations.append(gpa, .{
783 .tag = .nav_index,
784 .addend = @intCast(offset),
785 .offset = @intCast(code.items.len),
786 .pointee = .{ .nav_index = nav_index },
787 });
788 try code.appendNTimes(0, ptr_width_bytes);
789 return .ok;
790 },
791 else => {},
792 }
793
755 const vaddr = try lf.getNavVAddr(pt, nav_index, .{794 const vaddr = try lf.getNavVAddr(pt, nav_index, .{
756 .parent = reloc_parent,795 .parent = reloc_parent,
757 .offset = code.items.len,796 .offset = code.items.len,
758 .addend = @intCast(offset),797 .addend = @intCast(offset),
759 });798 });
760 const endian = target.cpu.arch.endian();799 const endian = target.cpu.arch.endian();
761 switch (ptr_width) {800 switch (ptr_width_bytes) {
762 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),801 2 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian),
763 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),802 4 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian),
764 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),803 8 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
765 else => unreachable,804 else => unreachable,
766 }805 }
767806
768 return Result.ok;807 return .ok;
769}808}
770809
771/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:810/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
src/codegen/llvm.zig+16-26
...@@ -1059,9 +1059,10 @@ pub const Object = struct {...@@ -1059,9 +1059,10 @@ pub const Object = struct {
1059 lto: Compilation.Config.LtoMode,1059 lto: Compilation.Config.LtoMode,
1060 };1060 };
10611061
1062 pub fn emit(o: *Object, options: EmitOptions) !void {1062 pub fn emit(o: *Object, options: EmitOptions) error{ LinkFailure, OutOfMemory }!void {
1063 const zcu = o.pt.zcu;1063 const zcu = o.pt.zcu;
1064 const comp = zcu.comp;1064 const comp = zcu.comp;
1065 const diags = &comp.link_diags;
10651066
1066 {1067 {
1067 try o.genErrorNameTable();1068 try o.genErrorNameTable();
...@@ -1223,27 +1224,30 @@ pub const Object = struct {...@@ -1223,27 +1224,30 @@ pub const Object = struct {
1223 o.builder.clearAndFree();1224 o.builder.clearAndFree();
12241225
1225 if (options.pre_bc_path) |path| {1226 if (options.pre_bc_path) |path| {
1226 var file = try std.fs.cwd().createFile(path, .{});1227 var file = std.fs.cwd().createFile(path, .{}) catch |err|
1228 return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) });
1227 defer file.close();1229 defer file.close();
12281230
1229 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);1231 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);
1230 try file.writeAll(ptr[0..(bitcode.len * 4)]);1232 file.writeAll(ptr[0..(bitcode.len * 4)]) catch |err|
1233 return diags.fail("failed to write to '{s}': {s}", .{ path, @errorName(err) });
1231 }1234 }
12321235
1233 if (options.asm_path == null and options.bin_path == null and1236 if (options.asm_path == null and options.bin_path == null and
1234 options.post_ir_path == null and options.post_bc_path == null) return;1237 options.post_ir_path == null and options.post_bc_path == null) return;
12351238
1236 if (options.post_bc_path) |path| {1239 if (options.post_bc_path) |path| {
1237 var file = try std.fs.cwd().createFileZ(path, .{});1240 var file = std.fs.cwd().createFileZ(path, .{}) catch |err|
1241 return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) });
1238 defer file.close();1242 defer file.close();
12391243
1240 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);1244 const ptr: [*]const u8 = @ptrCast(bitcode.ptr);
1241 try file.writeAll(ptr[0..(bitcode.len * 4)]);1245 file.writeAll(ptr[0..(bitcode.len * 4)]) catch |err|
1246 return diags.fail("failed to write to '{s}': {s}", .{ path, @errorName(err) });
1242 }1247 }
12431248
1244 if (!build_options.have_llvm or !comp.config.use_lib_llvm) {1249 if (!build_options.have_llvm or !comp.config.use_lib_llvm) {
1245 log.err("emitting without libllvm not implemented", .{});1250 return diags.fail("emitting without libllvm not implemented", .{});
1246 return error.FailedToEmit;
1247 }1251 }
12481252
1249 initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch);1253 initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch);
...@@ -1263,8 +1267,7 @@ pub const Object = struct {...@@ -1263,8 +1267,7 @@ pub const Object = struct {
12631267
1264 var module: *llvm.Module = undefined;1268 var module: *llvm.Module = undefined;
1265 if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) {1269 if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) {
1266 log.err("Failed to parse bitcode", .{});1270 return diags.fail("Failed to parse bitcode", .{});
1267 return error.FailedToEmit;
1268 }1271 }
1269 break :emit .{ context, module };1272 break :emit .{ context, module };
1270 };1273 };
...@@ -1274,12 +1277,7 @@ pub const Object = struct {...@@ -1274,12 +1277,7 @@ pub const Object = struct {
1274 var error_message: [*:0]const u8 = undefined;1277 var error_message: [*:0]const u8 = undefined;
1275 if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) {1278 if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) {
1276 defer llvm.disposeMessage(error_message);1279 defer llvm.disposeMessage(error_message);
12771280 return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message });
1278 log.err("LLVM failed to parse '{s}': {s}", .{
1279 target_triple_sentinel,
1280 error_message,
1281 });
1282 @panic("Invalid LLVM triple");
1283 }1281 }
12841282
1285 const optimize_mode = comp.root_mod.optimize_mode;1283 const optimize_mode = comp.root_mod.optimize_mode;
...@@ -1374,10 +1372,9 @@ pub const Object = struct {...@@ -1374,10 +1372,9 @@ pub const Object = struct {
1374 if (options.asm_path != null and options.bin_path != null) {1372 if (options.asm_path != null and options.bin_path != null) {
1375 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {1373 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
1376 defer llvm.disposeMessage(error_message);1374 defer llvm.disposeMessage(error_message);
1377 log.err("LLVM failed to emit bin={s} ir={s}: {s}", .{1375 return diags.fail("LLVM failed to emit bin={s} ir={s}: {s}", .{
1378 emit_bin_msg, post_llvm_ir_msg, error_message,1376 emit_bin_msg, post_llvm_ir_msg, error_message,
1379 });1377 });
1380 return error.FailedToEmit;
1381 }1378 }
1382 lowered_options.bin_filename = null;1379 lowered_options.bin_filename = null;
1383 lowered_options.llvm_ir_filename = null;1380 lowered_options.llvm_ir_filename = null;
...@@ -1386,11 +1383,9 @@ pub const Object = struct {...@@ -1386,11 +1383,9 @@ pub const Object = struct {
1386 lowered_options.asm_filename = options.asm_path;1383 lowered_options.asm_filename = options.asm_path;
1387 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {1384 if (target_machine.emitToFile(module, &error_message, &lowered_options)) {
1388 defer llvm.disposeMessage(error_message);1385 defer llvm.disposeMessage(error_message);
1389 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{1386 return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
1390 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg,1387 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message,
1391 error_message,
1392 });1388 });
1393 return error.FailedToEmit;
1394 }1389 }
1395 }1390 }
13961391
...@@ -1967,11 +1962,6 @@ pub const Object = struct {...@@ -1967,11 +1962,6 @@ pub const Object = struct {
1967 }1962 }
1968 }1963 }
19691964
1970 pub fn freeDecl(self: *Object, decl_index: InternPool.DeclIndex) void {
1971 const global = self.decl_map.get(decl_index) orelse return;
1972 global.delete(&self.builder);
1973 }
1974
1975 fn getDebugFile(o: *Object, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata {1965 fn getDebugFile(o: *Object, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata {
1976 const gpa = o.gpa;1966 const gpa = o.gpa;
1977 const gop = try o.debug_file_map.getOrPut(gpa, file_index);1967 const gop = try o.debug_file_map.getOrPut(gpa, file_index);
src/link.zig+38-118
...@@ -633,42 +633,15 @@ pub const File = struct {...@@ -633,42 +633,15 @@ pub const File = struct {
633 pub const FlushDebugInfoError = Dwarf.FlushError;633 pub const FlushDebugInfoError = Dwarf.FlushError;
634634
635 pub const UpdateNavError = error{635 pub const UpdateNavError = error{
636 OutOfMemory,
637 Overflow,636 Overflow,
638 Underflow,637 OutOfMemory,
639 FileTooBig,638 /// Indicates the error is already reported and stored in
640 InputOutput,639 /// `failed_codegen` on the Zcu.
641 FilesOpenedWithWrongFlags,
642 IsDir,
643 NoSpaceLeft,
644 Unseekable,
645 PermissionDenied,
646 SwapFile,
647 CorruptedData,
648 SystemResources,
649 OperationAborted,
650 BrokenPipe,
651 ConnectionResetByPeer,
652 ConnectionTimedOut,
653 SocketNotConnected,
654 NotOpenForReading,
655 WouldBlock,
656 Canceled,
657 AccessDenied,
658 Unexpected,
659 DiskQuota,
660 NotOpenForWriting,
661 AnalysisFail,
662 CodegenFail,640 CodegenFail,
663 EmitFail,641 /// Indicates the error is already reported and stored in `link_diags`
664 NameTooLong,642 /// on the Compilation.
665 CurrentWorkingDirectoryUnlinked,643 LinkFailure,
666 LockViolation,644 };
667 NetNameDeleted,
668 DeviceBusy,
669 InvalidArgument,
670 HotSwapUnavailableOnHostOperatingSystem,
671 } || UpdateDebugInfoError;
672645
673 /// Called from within CodeGen to retrieve the symbol index of a global symbol.646 /// Called from within CodeGen to retrieve the symbol index of a global symbol.
674 /// If no symbol exists yet with this name, a new undefined global symbol will647 /// If no symbol exists yet with this name, a new undefined global symbol will
...@@ -771,83 +744,11 @@ pub const File = struct {...@@ -771,83 +744,11 @@ pub const File = struct {
771 }744 }
772 }745 }
773746
774 /// TODO audit this error set. most of these should be collapsed into one error,
775 /// and Diags.Flags should be updated to convey the meaning to the user.
776 pub const FlushError = error{747 pub const FlushError = error{
777 CacheCheckFailed,
778 CurrentWorkingDirectoryUnlinked,
779 DivisionByZero,
780 DllImportLibraryNotFound,
781 ExpectedFuncType,
782 FailedToEmit,
783 FileSystem,
784 FilesOpenedWithWrongFlags,
785 /// Deprecated. Use `LinkFailure` instead.
786 /// Formerly used to indicate an error will be present in `Compilation.link_errors`.
787 FlushFailure,
788 /// Indicates an error will be present in `Compilation.link_errors`.748 /// Indicates an error will be present in `Compilation.link_errors`.
789 LinkFailure,749 LinkFailure,
790 FunctionSignatureMismatch,
791 GlobalTypeMismatch,
792 HotSwapUnavailableOnHostOperatingSystem,
793 InvalidCharacter,
794 InvalidEntryKind,
795 InvalidFeatureSet,
796 InvalidFormat,
797 InvalidIndex,
798 InvalidInitFunc,
799 InvalidMagicByte,
800 InvalidWasmVersion,
801 LLDCrashed,
802 LLDReportedFailure,
803 LLD_LinkingIsTODO_ForSpirV,
804 LibCInstallationMissingCrtDir,
805 LibCInstallationNotAvailable,
806 LinkingWithoutZigSourceUnimplemented,
807 MalformedArchive,
808 MalformedDwarf,
809 MalformedSection,
810 MemoryTooBig,
811 MemoryTooSmall,
812 MissAlignment,
813 MissingEndForBody,
814 MissingEndForExpression,
815 MissingSymbol,
816 MissingTableSymbols,
817 ModuleNameMismatch,
818 NoObjectsToLink,
819 NotObjectFile,
820 NotSupported,
821 OutOfMemory,750 OutOfMemory,
822 Overflow,751 };
823 PermissionDenied,
824 StreamTooLong,
825 SwapFile,
826 SymbolCollision,
827 SymbolMismatchingType,
828 TODOImplementPlan9Objs,
829 TODOImplementWritingLibFiles,
830 UnableToSpawnSelf,
831 UnableToSpawnWasm,
832 UnableToWriteArchive,
833 UndefinedLocal,
834 UndefinedSymbol,
835 Underflow,
836 UnexpectedRemainder,
837 UnexpectedTable,
838 UnexpectedValue,
839 UnknownFeature,
840 UnrecognizedVolume,
841 Unseekable,
842 UnsupportedCpuArchitecture,
843 UnsupportedVersion,
844 UnexpectedEndOfFile,
845 } ||
846 fs.File.WriteFileError ||
847 fs.File.OpenError ||
848 std.process.Child.SpawnError ||
849 fs.Dir.CopyFileError ||
850 FlushDebugInfoError;
851752
852 /// Commit pending changes and write headers. Takes into account final output mode753 /// Commit pending changes and write headers. Takes into account final output mode
853 /// and `use_lld`, not only `effectiveOutputMode`.754 /// and `use_lld`, not only `effectiveOutputMode`.
...@@ -864,7 +765,12 @@ pub const File = struct {...@@ -864,7 +765,12 @@ pub const File = struct {
864 assert(comp.c_object_table.count() == 1);765 assert(comp.c_object_table.count() == 1);
865 const the_key = comp.c_object_table.keys()[0];766 const the_key = comp.c_object_table.keys()[0];
866 const cached_pp_file_path = the_key.status.success.object_path;767 const cached_pp_file_path = the_key.status.success.object_path;
867 try cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{});768 cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| {
769 const diags = &base.comp.link_diags;
770 return diags.fail("failed to copy '{'}' to '{'}': {s}", .{
771 @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err),
772 });
773 };
868 return;774 return;
869 }775 }
870776
...@@ -893,16 +799,6 @@ pub const File = struct {...@@ -893,16 +799,6 @@ pub const File = struct {
893 }799 }
894 }800 }
895801
896 /// Called when a Decl is deleted from the Zcu.
897 pub fn freeDecl(base: *File, decl_index: InternPool.DeclIndex) void {
898 switch (base.tag) {
899 inline else => |tag| {
900 dev.check(tag.devFeature());
901 @as(*tag.Type(), @fieldParentPtr("base", base)).freeDecl(decl_index);
902 },
903 }
904 }
905
906 pub const UpdateExportsError = error{802 pub const UpdateExportsError = error{
907 OutOfMemory,803 OutOfMemory,
908 AnalysisFail,804 AnalysisFail,
...@@ -932,6 +828,7 @@ pub const File = struct {...@@ -932,6 +828,7 @@ pub const File = struct {
932 addend: u32,828 addend: u32,
933829
934 pub const Parent = union(enum) {830 pub const Parent = union(enum) {
831 none,
935 atom_index: u32,832 atom_index: u32,
936 debug_output: DebugInfoOutput,833 debug_output: DebugInfoOutput,
937 };834 };
...@@ -948,6 +845,7 @@ pub const File = struct {...@@ -948,6 +845,7 @@ pub const File = struct {
948 .c => unreachable,845 .c => unreachable,
949 .spirv => unreachable,846 .spirv => unreachable,
950 .nvptx => unreachable,847 .nvptx => unreachable,
848 .wasm => unreachable,
951 inline else => |tag| {849 inline else => |tag| {
952 dev.check(tag.devFeature());850 dev.check(tag.devFeature());
953 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);851 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
...@@ -966,6 +864,7 @@ pub const File = struct {...@@ -966,6 +864,7 @@ pub const File = struct {
966 .c => unreachable,864 .c => unreachable,
967 .spirv => unreachable,865 .spirv => unreachable,
968 .nvptx => unreachable,866 .nvptx => unreachable,
867 .wasm => unreachable,
969 inline else => |tag| {868 inline else => |tag| {
970 dev.check(tag.devFeature());869 dev.check(tag.devFeature());
971 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);870 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
...@@ -978,6 +877,7 @@ pub const File = struct {...@@ -978,6 +877,7 @@ pub const File = struct {
978 .c => unreachable,877 .c => unreachable,
979 .spirv => unreachable,878 .spirv => unreachable,
980 .nvptx => unreachable,879 .nvptx => unreachable,
880 .wasm => unreachable,
981 inline else => |tag| {881 inline else => |tag| {
982 dev.check(tag.devFeature());882 dev.check(tag.devFeature());
983 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);883 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
...@@ -1099,6 +999,26 @@ pub const File = struct {...@@ -1099,6 +999,26 @@ pub const File = struct {
1099 }999 }
1100 }1000 }
11011001
1002 /// Called when all linker inputs have been sent via `loadInput`. After
1003 /// this, `loadInput` will not be called anymore.
1004 pub fn prelink(base: *File) FlushError!void {
1005 const use_lld = build_options.have_llvm and base.comp.config.use_lld;
1006 if (use_lld) return;
1007
1008 // In this case, an object file is created by the LLVM backend, so
1009 // there is no prelink phase. The Zig code is linked as a standard
1010 // object along with the others.
1011 if (base.zcu_object_sub_path != null) return;
1012
1013 switch (base.tag) {
1014 inline .wasm => |tag| {
1015 dev.check(tag.devFeature());
1016 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink();
1017 },
1018 else => {},
1019 }
1020 }
1021
1102 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {1022 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
1103 dev.check(.lld_linker);1023 dev.check(.lld_linker);
11041024
src/link/C.zig+9-14
...@@ -175,21 +175,13 @@ pub fn deinit(self: *C) void {...@@ -175,21 +175,13 @@ pub fn deinit(self: *C) void {
175 self.lazy_code_buf.deinit(gpa);175 self.lazy_code_buf.deinit(gpa);
176}176}
177177
178pub fn freeDecl(self: *C, decl_index: InternPool.DeclIndex) void {
179 const gpa = self.base.comp.gpa;
180 if (self.decl_table.fetchSwapRemove(decl_index)) |kv| {
181 var decl_block = kv.value;
182 decl_block.deinit(gpa);
183 }
184}
185
186pub fn updateFunc(178pub fn updateFunc(
187 self: *C,179 self: *C,
188 pt: Zcu.PerThread,180 pt: Zcu.PerThread,
189 func_index: InternPool.Index,181 func_index: InternPool.Index,
190 air: Air,182 air: Air,
191 liveness: Liveness,183 liveness: Liveness,
192) !void {184) link.File.UpdateNavError!void {
193 const zcu = pt.zcu;185 const zcu = pt.zcu;
194 const gpa = zcu.gpa;186 const gpa = zcu.gpa;
195 const func = zcu.funcInfo(func_index);187 const func = zcu.funcInfo(func_index);
...@@ -313,7 +305,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {...@@ -313,7 +305,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {
313 };305 };
314}306}
315307
316pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {308pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void {
317 const tracy = trace(@src());309 const tracy = trace(@src());
318 defer tracy.end();310 defer tracy.end();
319311
...@@ -390,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn...@@ -390,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn
390 _ = ti_id;382 _ = ti_id;
391}383}
392384
393pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {385pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
394 return self.flushModule(arena, tid, prog_node);386 return self.flushModule(arena, tid, prog_node);
395}387}
396388
...@@ -409,7 +401,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {...@@ -409,7 +401,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
409 return defines;401 return defines;
410}402}
411403
412pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {404pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
413 _ = arena; // Has the same lifetime as the call to Compilation.update.405 _ = arena; // Has the same lifetime as the call to Compilation.update.
414406
415 const tracy = trace(@src());407 const tracy = trace(@src());
...@@ -419,6 +411,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -419,6 +411,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
419 defer sub_prog_node.end();411 defer sub_prog_node.end();
420412
421 const comp = self.base.comp;413 const comp = self.base.comp;
414 const diags = &comp.link_diags;
422 const gpa = comp.gpa;415 const gpa = comp.gpa;
423 const zcu = self.base.comp.zcu.?;416 const zcu = self.base.comp.zcu.?;
424 const ip = &zcu.intern_pool;417 const ip = &zcu.intern_pool;
...@@ -554,8 +547,10 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -554,8 +547,10 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
554 }, self.getString(av_block.code));547 }, self.getString(av_block.code));
555548
556 const file = self.base.file.?;549 const file = self.base.file.?;
557 try file.setEndPos(f.file_size);550 file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)});
558 try file.pwritevAll(f.all_buffers.items, 0);551 file.pwritevAll(f.all_buffers.items, 0) catch |err| return diags.fail("failed to write to '{'}': {s}", .{
552 self.base.emit, @errorName(err),
553 });
559}554}
560555
561const Flush = struct {556const Flush = struct {
src/link/Coff.zig+75-44
...@@ -408,7 +408,7 @@ pub fn createEmpty(...@@ -408,7 +408,7 @@ pub fn createEmpty(
408 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;408 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
409 }409 }
410 }410 }
411 try coff.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);411 try coff.pwriteAll(&[_]u8{0}, max_file_offset);
412 }412 }
413413
414 return coff;414 return coff;
...@@ -858,7 +858,7 @@ fn writeAtom(coff: *Coff, atom_index: Atom.Index, code: []u8) !void {...@@ -858,7 +858,7 @@ fn writeAtom(coff: *Coff, atom_index: Atom.Index, code: []u8) !void {
858 }858 }
859859
860 coff.resolveRelocs(atom_index, relocs.items, code, coff.image_base);860 coff.resolveRelocs(atom_index, relocs.items, code, coff.image_base);
861 try coff.base.file.?.pwriteAll(code, file_offset);861 try coff.pwriteAll(code, file_offset);
862862
863 // Now we can mark the relocs as resolved.863 // Now we can mark the relocs as resolved.
864 while (relocs.popOrNull()) |reloc| {864 while (relocs.popOrNull()) |reloc| {
...@@ -891,7 +891,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {...@@ -891,7 +891,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
891 const sect_id = coff.got_section_index.?;891 const sect_id = coff.got_section_index.?;
892892
893 if (coff.got_table_count_dirty) {893 if (coff.got_table_count_dirty) {
894 const needed_size = @as(u32, @intCast(coff.got_table.entries.items.len * coff.ptr_width.size()));894 const needed_size: u32 = @intCast(coff.got_table.entries.items.len * coff.ptr_width.size());
895 try coff.growSection(sect_id, needed_size);895 try coff.growSection(sect_id, needed_size);
896 coff.got_table_count_dirty = false;896 coff.got_table_count_dirty = false;
897 }897 }
...@@ -908,13 +908,13 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {...@@ -908,13 +908,13 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
908 switch (coff.ptr_width) {908 switch (coff.ptr_width) {
909 .p32 => {909 .p32 => {
910 var buf: [4]u8 = undefined;910 var buf: [4]u8 = undefined;
911 mem.writeInt(u32, &buf, @as(u32, @intCast(entry_value + coff.image_base)), .little);911 mem.writeInt(u32, &buf, @intCast(entry_value + coff.image_base), .little);
912 try coff.base.file.?.pwriteAll(&buf, file_offset);912 try coff.pwriteAll(&buf, file_offset);
913 },913 },
914 .p64 => {914 .p64 => {
915 var buf: [8]u8 = undefined;915 var buf: [8]u8 = undefined;
916 mem.writeInt(u64, &buf, entry_value + coff.image_base, .little);916 mem.writeInt(u64, &buf, entry_value + coff.image_base, .little);
917 try coff.base.file.?.pwriteAll(&buf, file_offset);917 try coff.pwriteAll(&buf, file_offset);
918 },918 },
919 }919 }
920920
...@@ -1093,7 +1093,13 @@ fn freeAtom(coff: *Coff, atom_index: Atom.Index) void {...@@ -1093,7 +1093,13 @@ fn freeAtom(coff: *Coff, atom_index: Atom.Index) void {
1093 coff.getAtomPtr(atom_index).sym_index = 0;1093 coff.getAtomPtr(atom_index).sym_index = 0;
1094}1094}
10951095
1096pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {1096pub fn updateFunc(
1097 coff: *Coff,
1098 pt: Zcu.PerThread,
1099 func_index: InternPool.Index,
1100 air: Air,
1101 liveness: Liveness,
1102) link.File.UpdateNavError!void {
1097 if (build_options.skip_non_native and builtin.object_format != .coff) {1103 if (build_options.skip_non_native and builtin.object_format != .coff) {
1098 @panic("Attempted to compile for object format that was disabled by build configuration");1104 @panic("Attempted to compile for object format that was disabled by build configuration");
1099 }1105 }
...@@ -1106,8 +1112,9 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -1106,8 +1112,9 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
1106 const zcu = pt.zcu;1112 const zcu = pt.zcu;
1107 const gpa = zcu.gpa;1113 const gpa = zcu.gpa;
1108 const func = zcu.funcInfo(func_index);1114 const func = zcu.funcInfo(func_index);
1115 const nav_index = func.owner_nav;
11091116
1110 const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav);1117 const atom_index = try coff.getOrCreateAtomForNav(nav_index);
1111 coff.freeRelocations(atom_index);1118 coff.freeRelocations(atom_index);
11121119
1113 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;1120 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
...@@ -1115,25 +1122,38 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -1115,25 +1122,38 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
1115 var code_buffer = std.ArrayList(u8).init(gpa);1122 var code_buffer = std.ArrayList(u8).init(gpa);
1116 defer code_buffer.deinit();1123 defer code_buffer.deinit();
11171124
1118 const res = try codegen.generateFunction(1125 const res = codegen.generateFunction(
1119 &coff.base,1126 &coff.base,
1120 pt,1127 pt,
1121 zcu.navSrcLoc(func.owner_nav),1128 zcu.navSrcLoc(nav_index),
1122 func_index,1129 func_index,
1123 air,1130 air,
1124 liveness,1131 liveness,
1125 &code_buffer,1132 &code_buffer,
1126 .none,1133 .none,
1127 );1134 ) catch |err| switch (err) {
1135 error.CodegenFail => return error.CodegenFail,
1136 error.OutOfMemory => return error.OutOfMemory,
1137 else => |e| {
1138 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
1139 gpa,
1140 zcu.navSrcLoc(nav_index),
1141 "unable to codegen: {s}",
1142 .{@errorName(e)},
1143 ));
1144 try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
1145 return error.CodegenFail;
1146 },
1147 };
1128 const code = switch (res) {1148 const code = switch (res) {
1129 .ok => code_buffer.items,1149 .ok => code_buffer.items,
1130 .fail => |em| {1150 .fail => |em| {
1131 try zcu.failed_codegen.put(zcu.gpa, func.owner_nav, em);1151 try zcu.failed_codegen.put(zcu.gpa, nav_index, em);
1132 return;1152 return;
1133 },1153 },
1134 };1154 };
11351155
1136 try coff.updateNavCode(pt, func.owner_nav, code, .FUNCTION);1156 try coff.updateNavCode(pt, nav_index, code, .FUNCTION);
11371157
1138 // Exports will be updated by `Zcu.processExports` after the update.1158 // Exports will be updated by `Zcu.processExports` after the update.
1139}1159}
...@@ -1258,9 +1278,11 @@ fn updateLazySymbolAtom(...@@ -1258,9 +1278,11 @@ fn updateLazySymbolAtom(
1258 sym: link.File.LazySymbol,1278 sym: link.File.LazySymbol,
1259 atom_index: Atom.Index,1279 atom_index: Atom.Index,
1260 section_index: u16,1280 section_index: u16,
1261) !void {1281) link.File.FlushError!void {
1262 const zcu = pt.zcu;1282 const zcu = pt.zcu;
1263 const gpa = zcu.gpa;1283 const comp = coff.base.comp;
1284 const gpa = comp.gpa;
1285 const diags = &comp.link_diags;
12641286
1265 var required_alignment: InternPool.Alignment = .none;1287 var required_alignment: InternPool.Alignment = .none;
1266 var code_buffer = std.ArrayList(u8).init(gpa);1288 var code_buffer = std.ArrayList(u8).init(gpa);
...@@ -1276,7 +1298,7 @@ fn updateLazySymbolAtom(...@@ -1276,7 +1298,7 @@ fn updateLazySymbolAtom(
1276 const local_sym_index = atom.getSymbolIndex().?;1298 const local_sym_index = atom.getSymbolIndex().?;
12771299
1278 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;1300 const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
1279 const res = try codegen.generateLazySymbol(1301 const res = codegen.generateLazySymbol(
1280 &coff.base,1302 &coff.base,
1281 pt,1303 pt,
1282 src,1304 src,
...@@ -1285,7 +1307,10 @@ fn updateLazySymbolAtom(...@@ -1285,7 +1307,10 @@ fn updateLazySymbolAtom(
1285 &code_buffer,1307 &code_buffer,
1286 .none,1308 .none,
1287 .{ .atom_index = local_sym_index },1309 .{ .atom_index = local_sym_index },
1288 );1310 ) catch |err| switch (err) {
1311 error.CodegenFail => return error.LinkFailure,
1312 else => |e| return diags.fail("failed to generate lazy symbol: {s}", .{@errorName(e)}),
1313 };
1289 const code = switch (res) {1314 const code = switch (res) {
1290 .ok => code_buffer.items,1315 .ok => code_buffer.items,
1291 .fail => |em| {1316 .fail => |em| {
...@@ -1387,7 +1412,7 @@ fn updateNavCode(...@@ -1387,7 +1412,7 @@ fn updateNavCode(
1387 nav_index: InternPool.Nav.Index,1412 nav_index: InternPool.Nav.Index,
1388 code: []u8,1413 code: []u8,
1389 complex_type: coff_util.ComplexType,1414 complex_type: coff_util.ComplexType,
1390) !void {1415) link.File.UpdateNavError!void {
1391 const zcu = pt.zcu;1416 const zcu = pt.zcu;
1392 const ip = &zcu.intern_pool;1417 const ip = &zcu.intern_pool;
1393 const nav = ip.getNav(nav_index);1418 const nav = ip.getNav(nav_index);
...@@ -1405,12 +1430,12 @@ fn updateNavCode(...@@ -1405,12 +1430,12 @@ fn updateNavCode(
1405 const atom = coff.getAtom(atom_index);1430 const atom = coff.getAtom(atom_index);
1406 const sym_index = atom.getSymbolIndex().?;1431 const sym_index = atom.getSymbolIndex().?;
1407 const sect_index = nav_metadata.section;1432 const sect_index = nav_metadata.section;
1408 const code_len = @as(u32, @intCast(code.len));1433 const code_len: u32 = @intCast(code.len);
14091434
1410 if (atom.size != 0) {1435 if (atom.size != 0) {
1411 const sym = atom.getSymbolPtr(coff);1436 const sym = atom.getSymbolPtr(coff);
1412 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));1437 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));
1413 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_index + 1));1438 sym.section_number = @enumFromInt(sect_index + 1);
1414 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };1439 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
14151440
1416 const capacity = atom.capacity(coff);1441 const capacity = atom.capacity(coff);
...@@ -1434,7 +1459,7 @@ fn updateNavCode(...@@ -1434,7 +1459,7 @@ fn updateNavCode(
1434 } else {1459 } else {
1435 const sym = atom.getSymbolPtr(coff);1460 const sym = atom.getSymbolPtr(coff);
1436 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));1461 try coff.setSymbolName(sym, nav.fqn.toSlice(ip));
1437 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_index + 1));1462 sym.section_number = @enumFromInt(sect_index + 1);
1438 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };1463 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
14391464
1440 const vaddr = try coff.allocateAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0));1465 const vaddr = try coff.allocateAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0));
...@@ -1453,7 +1478,6 @@ pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void {...@@ -1453,7 +1478,6 @@ pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void {
1453 if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index);1478 if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index);
14541479
1455 const gpa = coff.base.comp.gpa;1480 const gpa = coff.base.comp.gpa;
1456 log.debug("freeDecl 0x{x}", .{nav_index});
14571481
1458 if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| {1482 if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| {
1459 var kv = const_kv;1483 var kv = const_kv;
...@@ -1674,9 +1698,10 @@ pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: st...@@ -1674,9 +1698,10 @@ pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: st
1674 if (use_lld) {1698 if (use_lld) {
1675 return coff.linkWithLLD(arena, tid, prog_node);1699 return coff.linkWithLLD(arena, tid, prog_node);
1676 }1700 }
1701 const diags = &comp.link_diags;
1677 switch (comp.config.output_mode) {1702 switch (comp.config.output_mode) {
1678 .Exe, .Obj => return coff.flushModule(arena, tid, prog_node),1703 .Exe, .Obj => return coff.flushModule(arena, tid, prog_node),
1679 .Lib => return error.TODOImplementWritingLibFiles,1704 .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}),
1680 }1705 }
1681}1706}
16821707
...@@ -2224,7 +2249,7 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2224,7 +2249,7 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2224 defer sub_prog_node.end();2249 defer sub_prog_node.end();
22252250
2226 const pt: Zcu.PerThread = .activate(2251 const pt: Zcu.PerThread = .activate(
2227 comp.zcu orelse return error.LinkingWithoutZigSourceUnimplemented,2252 comp.zcu orelse return diags.fail("linking without zig source is not yet implemented", .{}),
2228 tid,2253 tid,
2229 );2254 );
2230 defer pt.deactivate();2255 defer pt.deactivate();
...@@ -2232,24 +2257,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2232,24 +2257,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2232 if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| {2257 if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| {
2233 // Most lazy symbols can be updated on first use, but2258 // Most lazy symbols can be updated on first use, but
2234 // anyerror needs to wait for everything to be flushed.2259 // anyerror needs to wait for everything to be flushed.
2235 if (metadata.text_state != .unused) coff.updateLazySymbolAtom(2260 if (metadata.text_state != .unused) try coff.updateLazySymbolAtom(
2236 pt,2261 pt,
2237 .{ .kind = .code, .ty = .anyerror_type },2262 .{ .kind = .code, .ty = .anyerror_type },
2238 metadata.text_atom,2263 metadata.text_atom,
2239 coff.text_section_index.?,2264 coff.text_section_index.?,
2240 ) catch |err| return switch (err) {2265 );
2241 error.CodegenFail => error.FlushFailure,2266 if (metadata.rdata_state != .unused) try coff.updateLazySymbolAtom(
2242 else => |e| e,
2243 };
2244 if (metadata.rdata_state != .unused) coff.updateLazySymbolAtom(
2245 pt,2267 pt,
2246 .{ .kind = .const_data, .ty = .anyerror_type },2268 .{ .kind = .const_data, .ty = .anyerror_type },
2247 metadata.rdata_atom,2269 metadata.rdata_atom,
2248 coff.rdata_section_index.?,2270 coff.rdata_section_index.?,
2249 ) catch |err| return switch (err) {2271 );
2250 error.CodegenFail => error.FlushFailure,
2251 else => |e| e,
2252 };
2253 }2272 }
2254 for (coff.lazy_syms.values()) |*metadata| {2273 for (coff.lazy_syms.values()) |*metadata| {
2255 if (metadata.text_state != .unused) metadata.text_state = .flushed;2274 if (metadata.text_state != .unused) metadata.text_state = .flushed;
...@@ -2594,7 +2613,7 @@ fn writeBaseRelocations(coff: *Coff) !void {...@@ -2594,7 +2613,7 @@ fn writeBaseRelocations(coff: *Coff) !void {
2594 const needed_size = @as(u32, @intCast(buffer.items.len));2613 const needed_size = @as(u32, @intCast(buffer.items.len));
2595 try coff.growSection(coff.reloc_section_index.?, needed_size);2614 try coff.growSection(coff.reloc_section_index.?, needed_size);
25962615
2597 try coff.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);2616 try coff.pwriteAll(buffer.items, header.pointer_to_raw_data);
25982617
2599 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{2618 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{
2600 .virtual_address = header.virtual_address,2619 .virtual_address = header.virtual_address,
...@@ -2727,7 +2746,7 @@ fn writeImportTables(coff: *Coff) !void {...@@ -2727,7 +2746,7 @@ fn writeImportTables(coff: *Coff) !void {
27272746
2728 assert(dll_names_offset == needed_size);2747 assert(dll_names_offset == needed_size);
27292748
2730 try coff.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);2749 try coff.pwriteAll(buffer.items, header.pointer_to_raw_data);
27312750
2732 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{2751 coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{
2733 .virtual_address = header.virtual_address + iat_size,2752 .virtual_address = header.virtual_address + iat_size,
...@@ -2741,20 +2760,22 @@ fn writeImportTables(coff: *Coff) !void {...@@ -2741,20 +2760,22 @@ fn writeImportTables(coff: *Coff) !void {
2741 coff.imports_count_dirty = false;2760 coff.imports_count_dirty = false;
2742}2761}
27432762
2744fn writeStrtab(coff: *Coff) !void {2763fn writeStrtab(coff: *Coff) link.File.FlushError!void {
2745 if (coff.strtab_offset == null) return;2764 if (coff.strtab_offset == null) return;
27462765
2766 const comp = coff.base.comp;
2767 const gpa = comp.gpa;
2768 const diags = &comp.link_diags;
2747 const allocated_size = coff.allocatedSize(coff.strtab_offset.?);2769 const allocated_size = coff.allocatedSize(coff.strtab_offset.?);
2748 const needed_size = @as(u32, @intCast(coff.strtab.buffer.items.len));2770 const needed_size: u32 = @intCast(coff.strtab.buffer.items.len);
27492771
2750 if (needed_size > allocated_size) {2772 if (needed_size > allocated_size) {
2751 coff.strtab_offset = null;2773 coff.strtab_offset = null;
2752 coff.strtab_offset = @as(u32, @intCast(coff.findFreeSpace(needed_size, @alignOf(u32))));2774 coff.strtab_offset = @intCast(coff.findFreeSpace(needed_size, @alignOf(u32)));
2753 }2775 }
27542776
2755 log.debug("writing strtab from 0x{x} to 0x{x}", .{ coff.strtab_offset.?, coff.strtab_offset.? + needed_size });2777 log.debug("writing strtab from 0x{x} to 0x{x}", .{ coff.strtab_offset.?, coff.strtab_offset.? + needed_size });
27562778
2757 const gpa = coff.base.comp.gpa;
2758 var buffer = std.ArrayList(u8).init(gpa);2779 var buffer = std.ArrayList(u8).init(gpa);
2759 defer buffer.deinit();2780 defer buffer.deinit();
2760 try buffer.ensureTotalCapacityPrecise(needed_size);2781 try buffer.ensureTotalCapacityPrecise(needed_size);
...@@ -2763,17 +2784,19 @@ fn writeStrtab(coff: *Coff) !void {...@@ -2763,17 +2784,19 @@ fn writeStrtab(coff: *Coff) !void {
2763 // we write the length of the strtab to a temporary buffer that goes to file.2784 // we write the length of the strtab to a temporary buffer that goes to file.
2764 mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(coff.strtab.buffer.items.len)), .little);2785 mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(coff.strtab.buffer.items.len)), .little);
27652786
2766 try coff.base.file.?.pwriteAll(buffer.items, coff.strtab_offset.?);2787 coff.pwriteAll(buffer.items, coff.strtab_offset.?) catch |err| {
2788 return diags.fail("failed to write: {s}", .{@errorName(err)});
2789 };
2767}2790}
27682791
2769fn writeSectionHeaders(coff: *Coff) !void {2792fn writeSectionHeaders(coff: *Coff) !void {
2770 const offset = coff.getSectionHeadersOffset();2793 const offset = coff.getSectionHeadersOffset();
2771 try coff.base.file.?.pwriteAll(mem.sliceAsBytes(coff.sections.items(.header)), offset);2794 try coff.pwriteAll(mem.sliceAsBytes(coff.sections.items(.header)), offset);
2772}2795}
27732796
2774fn writeDataDirectoriesHeaders(coff: *Coff) !void {2797fn writeDataDirectoriesHeaders(coff: *Coff) !void {
2775 const offset = coff.getDataDirectoryHeadersOffset();2798 const offset = coff.getDataDirectoryHeadersOffset();
2776 try coff.base.file.?.pwriteAll(mem.sliceAsBytes(&coff.data_directories), offset);2799 try coff.pwriteAll(mem.sliceAsBytes(&coff.data_directories), offset);
2777}2800}
27782801
2779fn writeHeader(coff: *Coff) !void {2802fn writeHeader(coff: *Coff) !void {
...@@ -2913,7 +2936,7 @@ fn writeHeader(coff: *Coff) !void {...@@ -2913,7 +2936,7 @@ fn writeHeader(coff: *Coff) !void {
2913 },2936 },
2914 }2937 }
29152938
2916 try coff.base.file.?.pwriteAll(buffer.items, 0);2939 try coff.pwriteAll(buffer.items, 0);
2917}2940}
29182941
2919pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {2942pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
...@@ -3710,6 +3733,14 @@ const ImportTable = struct {...@@ -3710,6 +3733,14 @@ const ImportTable = struct {
3710 const ImportIndex = u32;3733 const ImportIndex = u32;
3711};3734};
37123735
3736fn pwriteAll(coff: *Coff, bytes: []const u8, offset: u64) error{LinkFailure}!void {
3737 const comp = coff.base.comp;
3738 const diags = &comp.link_diags;
3739 coff.base.file.?.pwriteAll(bytes, offset) catch |err| {
3740 return diags.fail("failed to write: {s}", .{@errorName(err)});
3741 };
3742}
3743
3713const Coff = @This();3744const Coff = @This();
37143745
3715const std = @import("std");3746const std = @import("std");
src/link/Dwarf.zig+2-12
...@@ -21,20 +21,10 @@ debug_rnglists: DebugRngLists,...@@ -21,20 +21,10 @@ debug_rnglists: DebugRngLists,
21debug_str: StringSection,21debug_str: StringSection,
2222
23pub const UpdateError = error{23pub const UpdateError = error{
24 /// Indicates the error is already reported on `failed_codegen` in the Zcu.
24 CodegenFail,25 CodegenFail,
25 ReinterpretDeclRef,
26 Unimplemented,
27 OutOfMemory,26 OutOfMemory,
28 EndOfStream,27};
29 Overflow,
30 Underflow,
31 UnexpectedEndOfFile,
32} ||
33 std.fs.File.OpenError ||
34 std.fs.File.SetEndPosError ||
35 std.fs.File.CopyRangeError ||
36 std.fs.File.PReadError ||
37 std.fs.File.PWriteError;
3828
39pub const FlushError =29pub const FlushError =
40 UpdateError ||30 UpdateError ||
src/link/Elf.zig+22-16
...@@ -842,12 +842,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -842,12 +842,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
842 .Exe => {},842 .Exe => {},
843 }843 }
844844
845 if (diags.hasErrors()) return error.FlushFailure;845 if (diags.hasErrors()) return error.LinkFailure;
846846
847 // If we haven't already, create a linker-generated input file comprising of847 // If we haven't already, create a linker-generated input file comprising of
848 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.848 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
849 if (self.linker_defined_index == null) {849 if (self.linker_defined_index == null) {
850 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));850 const index: File.Index = @intCast(try self.files.addOne(gpa));
851 self.files.set(index, .{ .linker_defined = .{ .index = index } });851 self.files.set(index, .{ .linker_defined = .{ .index = index } });
852 self.linker_defined_index = index;852 self.linker_defined_index = index;
853 const object = self.linkerDefinedPtr().?;853 const object = self.linkerDefinedPtr().?;
...@@ -878,7 +878,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -878,7 +878,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
878 }878 }
879879
880 self.checkDuplicates() catch |err| switch (err) {880 self.checkDuplicates() catch |err| switch (err) {
881 error.HasDuplicates => return error.FlushFailure,881 error.HasDuplicates => return error.LinkFailure,
882 else => |e| return e,882 else => |e| return e,
883 };883 };
884884
...@@ -956,14 +956,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -956,14 +956,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
956 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,956 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
957 error.UnsupportedCpuArch => {957 error.UnsupportedCpuArch => {
958 try self.reportUnsupportedCpuArch();958 try self.reportUnsupportedCpuArch();
959 return error.FlushFailure;959 return error.LinkFailure;
960 },960 },
961 else => |e| return e,961 else => |e| return e,
962 };962 };
963 try self.base.file.?.pwriteAll(code, file_offset);963 try self.base.file.?.pwriteAll(code, file_offset);
964 }964 }
965965
966 if (has_reloc_errors) return error.FlushFailure;966 if (has_reloc_errors) return error.LinkFailure;
967 }967 }
968968
969 try self.writePhdrTable();969 try self.writePhdrTable();
...@@ -972,10 +972,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -972,10 +972,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
972 try self.writeMergeSections();972 try self.writeMergeSections();
973973
974 self.writeSyntheticSections() catch |err| switch (err) {974 self.writeSyntheticSections() catch |err| switch (err) {
975 error.RelocFailure => return error.FlushFailure,975 error.RelocFailure => return error.LinkFailure,
976 error.UnsupportedCpuArch => {976 error.UnsupportedCpuArch => {
977 try self.reportUnsupportedCpuArch();977 try self.reportUnsupportedCpuArch();
978 return error.FlushFailure;978 return error.LinkFailure;
979 },979 },
980 else => |e| return e,980 else => |e| return e,
981 };981 };
...@@ -989,7 +989,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -989,7 +989,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
989 try self.writeElfHeader();989 try self.writeElfHeader();
990 }990 }
991991
992 if (diags.hasErrors()) return error.FlushFailure;992 if (diags.hasErrors()) return error.LinkFailure;
993}993}
994994
995fn dumpArgvInit(self: *Elf, arena: Allocator) !void {995fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
...@@ -1389,7 +1389,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -1389,7 +1389,7 @@ fn scanRelocs(self: *Elf) !void {
1389 error.RelaxFailure => unreachable,1389 error.RelaxFailure => unreachable,
1390 error.UnsupportedCpuArch => {1390 error.UnsupportedCpuArch => {
1391 try self.reportUnsupportedCpuArch();1391 try self.reportUnsupportedCpuArch();
1392 return error.FlushFailure;1392 return error.LinkFailure;
1393 },1393 },
1394 error.RelocFailure => has_reloc_errors = true,1394 error.RelocFailure => has_reloc_errors = true,
1395 else => |e| return e,1395 else => |e| return e,
...@@ -1400,7 +1400,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -1400,7 +1400,7 @@ fn scanRelocs(self: *Elf) !void {
1400 error.RelaxFailure => unreachable,1400 error.RelaxFailure => unreachable,
1401 error.UnsupportedCpuArch => {1401 error.UnsupportedCpuArch => {
1402 try self.reportUnsupportedCpuArch();1402 try self.reportUnsupportedCpuArch();
1403 return error.FlushFailure;1403 return error.LinkFailure;
1404 },1404 },
1405 error.RelocFailure => has_reloc_errors = true,1405 error.RelocFailure => has_reloc_errors = true,
1406 else => |e| return e,1406 else => |e| return e,
...@@ -1409,7 +1409,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -1409,7 +1409,7 @@ fn scanRelocs(self: *Elf) !void {
14091409
1410 try self.reportUndefinedSymbols(&undefs);1410 try self.reportUndefinedSymbols(&undefs);
14111411
1412 if (has_reloc_errors) return error.FlushFailure;1412 if (has_reloc_errors) return error.LinkFailure;
14131413
1414 if (self.zigObjectPtr()) |zo| {1414 if (self.zigObjectPtr()) |zo| {
1415 try zo.asFile().createSymbolIndirection(self);1415 try zo.asFile().createSymbolIndirection(self);
...@@ -2327,7 +2327,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {...@@ -2327,7 +2327,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
2327 return self.zigObjectPtr().?.freeNav(self, nav);2327 return self.zigObjectPtr().?.freeNav(self, nav);
2328}2328}
23292329
2330pub fn updateFunc(self: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {2330pub fn updateFunc(
2331 self: *Elf,
2332 pt: Zcu.PerThread,
2333 func_index: InternPool.Index,
2334 air: Air,
2335 liveness: Liveness,
2336) link.File.UpdateNavError!void {
2331 if (build_options.skip_non_native and builtin.object_format != .elf) {2337 if (build_options.skip_non_native and builtin.object_format != .elf) {
2332 @panic("Attempted to compile for object format that was disabled by build configuration");2338 @panic("Attempted to compile for object format that was disabled by build configuration");
2333 }2339 }
...@@ -2426,7 +2432,7 @@ pub fn addCommentString(self: *Elf) !void {...@@ -2426,7 +2432,7 @@ pub fn addCommentString(self: *Elf) !void {
2426 self.comment_merge_section_index = msec_index;2432 self.comment_merge_section_index = msec_index;
2427}2433}
24282434
2429pub fn resolveMergeSections(self: *Elf) !void {2435pub fn resolveMergeSections(self: *Elf) link.File.FlushError!void {
2430 const tracy = trace(@src());2436 const tracy = trace(@src());
2431 defer tracy.end();2437 defer tracy.end();
24322438
...@@ -2441,7 +2447,7 @@ pub fn resolveMergeSections(self: *Elf) !void {...@@ -2441,7 +2447,7 @@ pub fn resolveMergeSections(self: *Elf) !void {
2441 };2447 };
2442 }2448 }
24432449
2444 if (has_errors) return error.FlushFailure;2450 if (has_errors) return error.LinkFailure;
24452451
2446 for (self.objects.items) |index| {2452 for (self.objects.items) |index| {
2447 const object = self.file(index).?.object;2453 const object = self.file(index).?.object;
...@@ -3658,7 +3664,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -3658,7 +3664,7 @@ fn writeAtoms(self: *Elf) !void {
3658 atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {3664 atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {
3659 error.UnsupportedCpuArch => {3665 error.UnsupportedCpuArch => {
3660 try self.reportUnsupportedCpuArch();3666 try self.reportUnsupportedCpuArch();
3661 return error.FlushFailure;3667 return error.LinkFailure;
3662 },3668 },
3663 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,3669 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
3664 else => |e| return e,3670 else => |e| return e,
...@@ -3666,7 +3672,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -3666,7 +3672,7 @@ fn writeAtoms(self: *Elf) !void {
3666 }3672 }
36673673
3668 try self.reportUndefinedSymbols(&undefs);3674 try self.reportUndefinedSymbols(&undefs);
3669 if (has_reloc_errors) return error.FlushFailure;3675 if (has_reloc_errors) return error.LinkFailure;
36703676
3671 if (self.requiresThunks()) {3677 if (self.requiresThunks()) {
3672 for (self.thunks.items) |th| {3678 for (self.thunks.items) |th| {
src/link/Elf/ZigObject.zig+7-5
...@@ -264,7 +264,7 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {...@@ -264,7 +264,7 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
264 }264 }
265}265}
266266
267pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {267pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) link.File.FlushError!void {
268 // Handle any lazy symbols that were emitted by incremental compilation.268 // Handle any lazy symbols that were emitted by incremental compilation.
269 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {269 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
270 const pt: Zcu.PerThread = .activate(elf_file.base.comp.zcu.?, tid);270 const pt: Zcu.PerThread = .activate(elf_file.base.comp.zcu.?, tid);
...@@ -278,7 +278,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {...@@ -278,7 +278,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
278 .{ .kind = .code, .ty = .anyerror_type },278 .{ .kind = .code, .ty = .anyerror_type },
279 metadata.text_symbol_index,279 metadata.text_symbol_index,
280 ) catch |err| return switch (err) {280 ) catch |err| return switch (err) {
281 error.CodegenFail => error.FlushFailure,281 error.CodegenFail => error.LinkFailure,
282 else => |e| e,282 else => |e| e,
283 };283 };
284 if (metadata.rodata_state != .unused) self.updateLazySymbol(284 if (metadata.rodata_state != .unused) self.updateLazySymbol(
...@@ -287,7 +287,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {...@@ -287,7 +287,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
287 .{ .kind = .const_data, .ty = .anyerror_type },287 .{ .kind = .const_data, .ty = .anyerror_type },
288 metadata.rodata_symbol_index,288 metadata.rodata_symbol_index,
289 ) catch |err| return switch (err) {289 ) catch |err| return switch (err) {
290 error.CodegenFail => error.FlushFailure,290 error.CodegenFail => error.LinkFailure,
291 else => |e| e,291 else => |e| e,
292 };292 };
293 }293 }
...@@ -933,6 +933,7 @@ pub fn getNavVAddr(...@@ -933,6 +933,7 @@ pub fn getNavVAddr(
933 const this_sym = self.symbol(this_sym_index);933 const this_sym = self.symbol(this_sym_index);
934 const vaddr = this_sym.address(.{}, elf_file);934 const vaddr = this_sym.address(.{}, elf_file);
935 switch (reloc_info.parent) {935 switch (reloc_info.parent) {
936 .none => unreachable,
936 .atom_index => |atom_index| {937 .atom_index => |atom_index| {
937 const parent_atom = self.symbol(atom_index).atom(elf_file).?;938 const parent_atom = self.symbol(atom_index).atom(elf_file).?;
938 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);939 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
...@@ -965,6 +966,7 @@ pub fn getUavVAddr(...@@ -965,6 +966,7 @@ pub fn getUavVAddr(
965 const sym = self.symbol(sym_index);966 const sym = self.symbol(sym_index);
966 const vaddr = sym.address(.{}, elf_file);967 const vaddr = sym.address(.{}, elf_file);
967 switch (reloc_info.parent) {968 switch (reloc_info.parent) {
969 .none => unreachable,
968 .atom_index => |atom_index| {970 .atom_index => |atom_index| {
969 const parent_atom = self.symbol(atom_index).atom(elf_file).?;971 const parent_atom = self.symbol(atom_index).atom(elf_file).?;
970 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);972 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
...@@ -1408,7 +1410,7 @@ pub fn updateFunc(...@@ -1408,7 +1410,7 @@ pub fn updateFunc(
1408 func_index: InternPool.Index,1410 func_index: InternPool.Index,
1409 air: Air,1411 air: Air,
1410 liveness: Liveness,1412 liveness: Liveness,
1411) !void {1413) link.File.UpdateNavError!void {
1412 const tracy = trace(@src());1414 const tracy = trace(@src());
1413 defer tracy.end();1415 defer tracy.end();
14141416
...@@ -1615,7 +1617,7 @@ fn updateLazySymbol(...@@ -1615,7 +1617,7 @@ fn updateLazySymbol(
1615 pt: Zcu.PerThread,1617 pt: Zcu.PerThread,
1616 sym: link.File.LazySymbol,1618 sym: link.File.LazySymbol,
1617 symbol_index: Symbol.Index,1619 symbol_index: Symbol.Index,
1618) !void {1620) link.File.FlushError!void {
1619 const zcu = pt.zcu;1621 const zcu = pt.zcu;
1620 const gpa = zcu.gpa;1622 const gpa = zcu.gpa;
16211623
src/link/Elf/relocatable.zig+5-5
...@@ -2,7 +2,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v...@@ -2,7 +2,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v
2 const gpa = comp.gpa;2 const gpa = comp.gpa;
3 const diags = &comp.link_diags;3 const diags = &comp.link_diags;
44
5 if (diags.hasErrors()) return error.FlushFailure;5 if (diags.hasErrors()) return error.LinkFailure;
66
7 // First, we flush relocatable object file generated with our backends.7 // First, we flush relocatable object file generated with our backends.
8 if (elf_file.zigObjectPtr()) |zig_object| {8 if (elf_file.zigObjectPtr()) |zig_object| {
...@@ -127,13 +127,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v...@@ -127,13 +127,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v
127 try elf_file.base.file.?.setEndPos(total_size);127 try elf_file.base.file.?.setEndPos(total_size);
128 try elf_file.base.file.?.pwriteAll(buffer.items, 0);128 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
129129
130 if (diags.hasErrors()) return error.FlushFailure;130 if (diags.hasErrors()) return error.LinkFailure;
131}131}
132132
133pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {133pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {
134 const diags = &comp.link_diags;134 const diags = &comp.link_diags;
135135
136 if (diags.hasErrors()) return error.FlushFailure;136 if (diags.hasErrors()) return error.LinkFailure;
137137
138 // Now, we are ready to resolve the symbols across all input files.138 // Now, we are ready to resolve the symbols across all input files.
139 // We will first resolve the files in the ZigObject, next in the parsed139 // We will first resolve the files in the ZigObject, next in the parsed
...@@ -179,7 +179,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void...@@ -179,7 +179,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void
179 try elf_file.writeShdrTable();179 try elf_file.writeShdrTable();
180 try elf_file.writeElfHeader();180 try elf_file.writeElfHeader();
181181
182 if (diags.hasErrors()) return error.FlushFailure;182 if (diags.hasErrors()) return error.LinkFailure;
183}183}
184184
185fn claimUnresolved(elf_file: *Elf) void {185fn claimUnresolved(elf_file: *Elf) void {
...@@ -259,7 +259,7 @@ fn initComdatGroups(elf_file: *Elf) !void {...@@ -259,7 +259,7 @@ fn initComdatGroups(elf_file: *Elf) !void {
259 }259 }
260}260}
261261
262fn updateSectionSizes(elf_file: *Elf) !void {262fn updateSectionSizes(elf_file: *Elf) link.File.FlushError!void {
263 const slice = elf_file.sections.slice();263 const slice = elf_file.sections.slice();
264 for (slice.items(.atom_list_2)) |*atom_list| {264 for (slice.items(.atom_list_2)) |*atom_list| {
265 if (atom_list.atoms.keys().len == 0) continue;265 if (atom_list.atoms.keys().len == 0) continue;
src/link/MachO.zig+12-6
...@@ -481,7 +481,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -481,7 +481,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
481 }481 }
482 };482 };
483483
484 if (diags.hasErrors()) return error.FlushFailure;484 if (diags.hasErrors()) return error.LinkFailure;
485485
486 {486 {
487 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));487 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
...@@ -501,7 +501,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -501,7 +501,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
501 }501 }
502502
503 self.checkDuplicates() catch |err| switch (err) {503 self.checkDuplicates() catch |err| switch (err) {
504 error.HasDuplicates => return error.FlushFailure,504 error.HasDuplicates => return error.LinkFailure,
505 else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}),505 else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}),
506 };506 };
507507
...@@ -516,7 +516,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -516,7 +516,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
516 self.claimUnresolved();516 self.claimUnresolved();
517517
518 self.scanRelocs() catch |err| switch (err) {518 self.scanRelocs() catch |err| switch (err) {
519 error.HasUndefinedSymbols => return error.FlushFailure,519 error.HasUndefinedSymbols => return error.LinkFailure,
520 else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}),520 else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}),
521 };521 };
522522
...@@ -543,7 +543,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -543,7 +543,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
543543
544 if (self.getZigObject()) |zo| {544 if (self.getZigObject()) |zo| {
545 zo.resolveRelocs(self) catch |err| switch (err) {545 zo.resolveRelocs(self) catch |err| switch (err) {
546 error.ResolveFailed => return error.FlushFailure,546 error.ResolveFailed => return error.LinkFailure,
547 else => |e| return e,547 else => |e| return e,
548 };548 };
549 }549 }
...@@ -2998,7 +2998,13 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {...@@ -2998,7 +2998,13 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
2998 try self.base.file.?.pwriteAll(buffer.items, offset);2998 try self.base.file.?.pwriteAll(buffer.items, offset);
2999}2999}
30003000
3001pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {3001pub fn updateFunc(
3002 self: *MachO,
3003 pt: Zcu.PerThread,
3004 func_index: InternPool.Index,
3005 air: Air,
3006 liveness: Liveness,
3007) link.File.UpdateNavError!void {
3002 if (build_options.skip_non_native and builtin.object_format != .macho) {3008 if (build_options.skip_non_native and builtin.object_format != .macho) {
3003 @panic("Attempted to compile for object format that was disabled by build configuration");3009 @panic("Attempted to compile for object format that was disabled by build configuration");
3004 }3010 }
...@@ -3006,7 +3012,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -3006,7 +3012,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index,
3006 return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness);3012 return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness);
3007}3013}
30083014
3009pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {3015pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
3010 if (build_options.skip_non_native and builtin.object_format != .macho) {3016 if (build_options.skip_non_native and builtin.object_format != .macho) {
3011 @panic("Attempted to compile for object format that was disabled by build configuration");3017 @panic("Attempted to compile for object format that was disabled by build configuration");
3012 }3018 }
src/link/MachO/ZigObject.zig+2-2
...@@ -560,7 +560,7 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)...@@ -560,7 +560,7 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)
560 .{ .kind = .code, .ty = .anyerror_type },560 .{ .kind = .code, .ty = .anyerror_type },
561 metadata.text_symbol_index,561 metadata.text_symbol_index,
562 ) catch |err| return switch (err) {562 ) catch |err| return switch (err) {
563 error.CodegenFail => error.FlushFailure,563 error.CodegenFail => error.LinkFailure,
564 else => |e| e,564 else => |e| e,
565 };565 };
566 if (metadata.const_state != .unused) self.updateLazySymbol(566 if (metadata.const_state != .unused) self.updateLazySymbol(
...@@ -569,7 +569,7 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)...@@ -569,7 +569,7 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)
569 .{ .kind = .const_data, .ty = .anyerror_type },569 .{ .kind = .const_data, .ty = .anyerror_type },
570 metadata.const_symbol_index,570 metadata.const_symbol_index,
571 ) catch |err| return switch (err) {571 ) catch |err| return switch (err) {
572 error.CodegenFail => error.FlushFailure,572 error.CodegenFail => error.LinkFailure,
573 else => |e| e,573 else => |e| e,
574 };574 };
575 }575 }
src/link/MachO/relocatable.zig+5-5
...@@ -33,11 +33,11 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat...@@ -33,11 +33,11 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
33 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});33 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
34 }34 }
3535
36 if (diags.hasErrors()) return error.FlushFailure;36 if (diags.hasErrors()) return error.LinkFailure;
3737
38 try macho_file.parseInputFiles();38 try macho_file.parseInputFiles();
3939
40 if (diags.hasErrors()) return error.FlushFailure;40 if (diags.hasErrors()) return error.LinkFailure;
4141
42 try macho_file.resolveSymbols();42 try macho_file.resolveSymbols();
43 try macho_file.dedupLiterals();43 try macho_file.dedupLiterals();
...@@ -93,11 +93,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -93,11 +93,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
93 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});93 diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)});
94 }94 }
9595
96 if (diags.hasErrors()) return error.FlushFailure;96 if (diags.hasErrors()) return error.LinkFailure;
9797
98 try parseInputFilesAr(macho_file);98 try parseInputFilesAr(macho_file);
9999
100 if (diags.hasErrors()) return error.FlushFailure;100 if (diags.hasErrors()) return error.LinkFailure;
101101
102 // First, we flush relocatable object file generated with our backends.102 // First, we flush relocatable object file generated with our backends.
103 if (macho_file.getZigObject()) |zo| {103 if (macho_file.getZigObject()) |zo| {
...@@ -218,7 +218,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -218,7 +218,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
218 try macho_file.base.file.?.setEndPos(total_size);218 try macho_file.base.file.?.setEndPos(total_size);
219 try macho_file.base.file.?.pwriteAll(buffer.items, 0);219 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
220220
221 if (diags.hasErrors()) return error.FlushFailure;221 if (diags.hasErrors()) return error.LinkFailure;
222}222}
223223
224fn parseInputFilesAr(macho_file: *MachO) !void {224fn parseInputFilesAr(macho_file: *MachO) !void {
src/link/NvPtx.zig+8-6
...@@ -82,11 +82,17 @@ pub fn deinit(self: *NvPtx) void {...@@ -82,11 +82,17 @@ pub fn deinit(self: *NvPtx) void {
82 self.llvm_object.deinit();82 self.llvm_object.deinit();
83}83}
8484
85pub fn updateFunc(self: *NvPtx, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {85pub fn updateFunc(
86 self: *NvPtx,
87 pt: Zcu.PerThread,
88 func_index: InternPool.Index,
89 air: Air,
90 liveness: Liveness,
91) link.File.UpdateNavError!void {
86 try self.llvm_object.updateFunc(pt, func_index, air, liveness);92 try self.llvm_object.updateFunc(pt, func_index, air, liveness);
87}93}
8894
89pub fn updateNav(self: *NvPtx, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {95pub fn updateNav(self: *NvPtx, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
90 return self.llvm_object.updateNav(pt, nav);96 return self.llvm_object.updateNav(pt, nav);
91}97}
9298
...@@ -102,10 +108,6 @@ pub fn updateExports(...@@ -102,10 +108,6 @@ pub fn updateExports(
102 return self.llvm_object.updateExports(pt, exported, export_indices);108 return self.llvm_object.updateExports(pt, exported, export_indices);
103}109}
104110
105pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void {
106 return self.llvm_object.freeDecl(decl_index);
107}
108
109pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {111pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
110 return self.flushModule(arena, tid, prog_node);112 return self.flushModule(arena, tid, prog_node);
111}113}
src/link/Plan9.zig+10-48
...@@ -385,7 +385,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi...@@ -385,7 +385,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi
385 }385 }
386}386}
387387
388pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {388pub fn updateFunc(
389 self: *Plan9,
390 pt: Zcu.PerThread,
391 func_index: InternPool.Index,
392 air: Air,
393 liveness: Liveness,
394) link.File.UpdateNavError!void {
389 if (build_options.skip_non_native and builtin.object_format != .plan9) {395 if (build_options.skip_non_native and builtin.object_format != .plan9) {
390 @panic("Attempted to compile for object format that was disabled by build configuration");396 @panic("Attempted to compile for object format that was disabled by build configuration");
391 }397 }
...@@ -437,7 +443,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -437,7 +443,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index,
437 return self.updateFinish(pt, func.owner_nav);443 return self.updateFinish(pt, func.owner_nav);
438}444}
439445
440pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {446pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void {
441 const zcu = pt.zcu;447 const zcu = pt.zcu;
442 const gpa = zcu.gpa;448 const gpa = zcu.gpa;
443 const ip = &zcu.intern_pool;449 const ip = &zcu.intern_pool;
...@@ -619,7 +625,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -619,7 +625,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
619 .{ .kind = .code, .ty = .anyerror_type },625 .{ .kind = .code, .ty = .anyerror_type },
620 metadata.text_atom,626 metadata.text_atom,
621 ) catch |err| return switch (err) {627 ) catch |err| return switch (err) {
622 error.CodegenFail => error.FlushFailure,628 error.CodegenFail => error.LinkFailure,
623 else => |e| e,629 else => |e| e,
624 };630 };
625 if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(631 if (metadata.rodata_state != .unused) self.updateLazySymbolAtom(
...@@ -627,7 +633,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -627,7 +633,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
627 .{ .kind = .const_data, .ty = .anyerror_type },633 .{ .kind = .const_data, .ty = .anyerror_type },
628 metadata.rodata_atom,634 metadata.rodata_atom,
629 ) catch |err| return switch (err) {635 ) catch |err| return switch (err) {
630 error.CodegenFail => error.FlushFailure,636 error.CodegenFail => error.LinkFailure,
631 else => |e| e,637 else => |e| e,
632 };638 };
633 }639 }
...@@ -947,50 +953,6 @@ fn addNavExports(...@@ -947,50 +953,6 @@ fn addNavExports(
947 }953 }
948}954}
949955
950pub fn freeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) void {
951 const gpa = self.base.comp.gpa;
952 // TODO audit the lifetimes of decls table entries. It's possible to get
953 // freeDecl without any updateDecl in between.
954 const zcu = self.base.comp.zcu.?;
955 const decl = zcu.declPtr(decl_index);
956 const is_fn = decl.val.isFuncBody(zcu);
957 if (is_fn) {
958 const symidx_and_submap = self.fn_decl_table.get(decl.getFileScope(zcu)).?;
959 var submap = symidx_and_submap.functions;
960 if (submap.fetchSwapRemove(decl_index)) |removed_entry| {
961 gpa.free(removed_entry.value.code);
962 gpa.free(removed_entry.value.lineinfo);
963 }
964 if (submap.count() == 0) {
965 self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol;
966 self.syms_index_free_list.append(gpa, symidx_and_submap.sym_index) catch {};
967 submap.deinit(gpa);
968 }
969 } else {
970 if (self.data_decl_table.fetchSwapRemove(decl_index)) |removed_entry| {
971 gpa.free(removed_entry.value);
972 }
973 }
974 if (self.decls.fetchRemove(decl_index)) |const_kv| {
975 var kv = const_kv;
976 const atom = self.getAtom(kv.value.index);
977 if (atom.got_index) |i| {
978 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
979 self.got_index_free_list.append(gpa, i) catch {};
980 }
981 if (atom.sym_index) |i| {
982 self.syms_index_free_list.append(gpa, i) catch {};
983 self.syms.items[i] = aout.Sym.undefined_symbol;
984 }
985 kv.value.exports.deinit(gpa);
986 }
987 {
988 const atom_index = self.decls.get(decl_index).?.index;
989 const relocs = self.relocs.getPtr(atom_index) orelse return;
990 relocs.clearAndFree(gpa);
991 assert(self.relocs.remove(atom_index));
992 }
993}
994fn createAtom(self: *Plan9) !Atom.Index {956fn createAtom(self: *Plan9) !Atom.Index {
995 const gpa = self.base.comp.gpa;957 const gpa = self.base.comp.gpa;
996 const index = @as(Atom.Index, @intCast(self.atoms.items.len));958 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
src/link/SpirV.zig+9-8
...@@ -122,7 +122,13 @@ pub fn deinit(self: *SpirV) void {...@@ -122,7 +122,13 @@ pub fn deinit(self: *SpirV) void {
122 self.object.deinit();122 self.object.deinit();
123}123}
124124
125pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {125pub fn updateFunc(
126 self: *SpirV,
127 pt: Zcu.PerThread,
128 func_index: InternPool.Index,
129 air: Air,
130 liveness: Liveness,
131) link.File.UpdateNavError!void {
126 if (build_options.skip_non_native) {132 if (build_options.skip_non_native) {
127 @panic("Attempted to compile for architecture that was disabled by build configuration");133 @panic("Attempted to compile for architecture that was disabled by build configuration");
128 }134 }
...@@ -134,7 +140,7 @@ pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -134,7 +140,7 @@ pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index,
134 try self.object.updateFunc(pt, func_index, air, liveness);140 try self.object.updateFunc(pt, func_index, air, liveness);
135}141}
136142
137pub fn updateNav(self: *SpirV, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {143pub fn updateNav(self: *SpirV, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
138 if (build_options.skip_non_native) {144 if (build_options.skip_non_native) {
139 @panic("Attempted to compile for architecture that was disabled by build configuration");145 @panic("Attempted to compile for architecture that was disabled by build configuration");
140 }146 }
...@@ -196,11 +202,6 @@ pub fn updateExports(...@@ -196,11 +202,6 @@ pub fn updateExports(
196 // TODO: Export regular functions, variables, etc using Linkage attributes.202 // TODO: Export regular functions, variables, etc using Linkage attributes.
197}203}
198204
199pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void {
200 _ = self;
201 _ = decl_index;
202}
203
204pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {205pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
205 return self.flushModule(arena, tid, prog_node);206 return self.flushModule(arena, tid, prog_node);
206}207}
...@@ -266,7 +267,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -266,7 +267,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
266 error.OutOfMemory => return error.OutOfMemory,267 error.OutOfMemory => return error.OutOfMemory,
267 else => |other| {268 else => |other| {
268 log.err("error while linking: {s}", .{@errorName(other)});269 log.err("error while linking: {s}", .{@errorName(other)});
269 return error.FlushFailure;270 return error.LinkFailure;
270 },271 },
271 };272 };
272273
src/link/Wasm.zig+1289-3856
...@@ -1,44 +1,49 @@...@@ -1,44 +1,49 @@
1const Wasm = @This();1const Wasm = @This();
2const build_options = @import("build_options");2const Archive = @import("Wasm/Archive.zig");
3const Object = @import("Wasm/Object.zig");
4const Flush = @import("Wasm/Flush.zig");
35
4const builtin = @import("builtin");6const builtin = @import("builtin");
5const native_endian = builtin.cpu.arch.endian();7const native_endian = builtin.cpu.arch.endian();
68
9const build_options = @import("build_options");
10
7const std = @import("std");11const std = @import("std");
8const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
9const Cache = std.Build.Cache;13const Cache = std.Build.Cache;
10const Path = Cache.Path;14const Path = Cache.Path;
11const assert = std.debug.assert;15const assert = std.debug.assert;
12const fs = std.fs;16const fs = std.fs;
13const gc_log = std.log.scoped(.gc);
14const leb = std.leb;17const leb = std.leb;
15const log = std.log.scoped(.link);18const log = std.log.scoped(.link);
16const mem = std.mem;19const mem = std.mem;
1720
18const Air = @import("../Air.zig");21const Air = @import("../Air.zig");
19const Archive = @import("Wasm/Archive.zig");
20const CodeGen = @import("../arch/wasm/CodeGen.zig");22const CodeGen = @import("../arch/wasm/CodeGen.zig");
21const Compilation = @import("../Compilation.zig");23const Compilation = @import("../Compilation.zig");
22const Dwarf = @import("Dwarf.zig");24const Dwarf = @import("Dwarf.zig");
23const InternPool = @import("../InternPool.zig");25const InternPool = @import("../InternPool.zig");
24const Liveness = @import("../Liveness.zig");26const Liveness = @import("../Liveness.zig");
25const LlvmObject = @import("../codegen/llvm.zig").Object;27const LlvmObject = @import("../codegen/llvm.zig").Object;
26const Object = @import("Wasm/Object.zig");
27const Symbol = @import("Wasm/Symbol.zig");
28const Type = @import("../Type.zig");
29const Value = @import("../Value.zig");
30const Zcu = @import("../Zcu.zig");28const Zcu = @import("../Zcu.zig");
31const ZigObject = @import("Wasm/ZigObject.zig");
32const codegen = @import("../codegen.zig");29const codegen = @import("../codegen.zig");
33const dev = @import("../dev.zig");30const dev = @import("../dev.zig");
34const link = @import("../link.zig");31const link = @import("../link.zig");
35const lldMain = @import("../main.zig").lldMain;32const lldMain = @import("../main.zig").lldMain;
36const trace = @import("../tracy.zig").trace;33const trace = @import("../tracy.zig").trace;
37const wasi_libc = @import("../wasi_libc.zig");34const wasi_libc = @import("../wasi_libc.zig");
35const Value = @import("../Value.zig");
3836
39base: link.File,37base: link.File,
40/// Null-terminated strings, indexes have type String and string_table provides38/// Null-terminated strings, indexes have type String and string_table provides
41/// lookup.39/// lookup.
40///
41/// There are a couple of sites that add things here without adding
42/// corresponding string_table entries. For such cases, when implementing
43/// serialization/deserialization, they should be adjusted to prefix that data
44/// with a null byte so that deserialization does not attempt to create
45/// string_table entries for them. Alternately those sites could be moved to
46/// use a different byte array for this purpose.
42string_bytes: std.ArrayListUnmanaged(u8),47string_bytes: std.ArrayListUnmanaged(u8),
43/// Omitted when serializing linker state.48/// Omitted when serializing linker state.
44string_table: String.Table,49string_table: String.Table,
...@@ -62,118 +67,637 @@ export_table: bool,...@@ -62,118 +67,637 @@ export_table: bool,
62name: []const u8,67name: []const u8,
63/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.68/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
64llvm_object: ?LlvmObject.Ptr = null,69llvm_object: ?LlvmObject.Ptr = null,
65zig_object: ?*ZigObject,
66/// List of relocatable files to be linked into the final binary.70/// List of relocatable files to be linked into the final binary.
67objects: std.ArrayListUnmanaged(Object) = .{},71objects: std.ArrayListUnmanaged(Object) = .{},
72
73func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty,
74/// Provides a mapping of both imports and provided functions to symbol name.
75/// Local functions may be unnamed.
76object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty,
77/// All functions for all objects.
78object_functions: std.ArrayListUnmanaged(Function) = .empty,
79
80/// Provides a mapping of both imports and provided globals to symbol name.
81/// Local globals may be unnamed.
82object_global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImport) = .empty,
83/// All globals for all objects.
84object_globals: std.ArrayListUnmanaged(Global) = .empty,
85
86/// All table imports for all objects.
87object_table_imports: std.ArrayListUnmanaged(TableImport) = .empty,
88/// All parsed table sections for all objects.
89object_tables: std.ArrayListUnmanaged(Table) = .empty,
90
91/// All memory imports for all objects.
92object_memory_imports: std.ArrayListUnmanaged(MemoryImport) = .empty,
93/// All parsed memory sections for all objects.
94object_memories: std.ArrayListUnmanaged(std.wasm.Memory) = .empty,
95
96/// List of initialization functions. These must be called in order of priority
97/// by the (synthetic) __wasm_call_ctors function.
98object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty,
99/// All relocations from all objects concatenated. `relocs_start` marks the end
100/// point of object relocations and start point of Zcu relocations.
101relocations: std.MultiArrayList(Relocation) = .empty,
102
103/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.
104object_data_segments: std.ArrayListUnmanaged(DataSegment) = .empty,
105/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.
106object_custom_segments: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, CustomSegment) = .empty,
107
108/// All comdat information for all objects.
109object_comdats: std.ArrayListUnmanaged(Comdat) = .empty,
110/// A table that maps the relocations to be performed where the key represents
111/// the section (across all objects) that the slice of relocations applies to.
112object_relocations_table: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, Relocation.Slice) = .empty,
113/// Incremented across all objects in order to enable calculation of `ObjectSectionIndex` values.
114object_total_sections: u32 = 0,
115/// All comdat symbols from all objects concatenated.
116object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty,
117
68/// When importing objects from the host environment, a name must be supplied.118/// When importing objects from the host environment, a name must be supplied.
69/// LLVM uses "env" by default when none is given. This would be a good default for Zig119/// LLVM uses "env" by default when none is given. This would be a good default for Zig
70/// to support existing code.120/// to support existing code.
71/// TODO: Allow setting this through a flag?121/// TODO: Allow setting this through a flag?
72host_name: String,122host_name: String,
73/// List of symbols generated by the linker.123
74synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .empty,
75/// Maps atoms to their segment index
76atoms: std.AutoHashMapUnmanaged(Segment.Index, Atom.Index) = .empty,
77/// List of all atoms.
78managed_atoms: std.ArrayListUnmanaged(Atom) = .empty,
79
80/// The count of imported functions. This number will be appended
81/// to the function indexes as their index starts at the lowest non-extern function.
82imported_functions_count: u32 = 0,
83/// The count of imported wasm globals. This number will be appended
84/// to the global indexes when sections are merged.
85imported_globals_count: u32 = 0,
86/// The count of imported tables. This number will be appended
87/// to the table indexes when sections are merged.
88imported_tables_count: u32 = 0,
89/// Map of symbol locations, represented by its `Import`
90imports: std.AutoHashMapUnmanaged(SymbolLoc, Import) = .empty,
91/// Represents non-synthetic section entries.
92/// Used for code, data and custom sections.
93segments: std.ArrayListUnmanaged(Segment) = .empty,
94/// Maps a data segment key (such as .rodata) to the index into `segments`.
95data_segments: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty,
96/// A table of `NamedSegment` which provide meta data
97/// about a data symbol such as its name where the key is
98/// the segment index, which can be found from `data_segments`
99segment_info: std.AutoArrayHashMapUnmanaged(Segment.Index, NamedSegment) = .empty,
100
101// Output sections
102/// Output type section
103func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty,
104/// Output function section where the key is the original
105/// function index and the value is function.
106/// This allows us to map multiple symbols to the same function.
107functions: std.AutoArrayHashMapUnmanaged(
108 struct {
109 /// `none` in the case of synthetic sections.
110 file: OptionalObjectId,
111 index: u32,
112 },
113 struct {
114 func: std.wasm.Func,
115 sym_index: Symbol.Index,
116 },
117) = .{},
118/// Output global section
119wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty,
120/// Memory section124/// Memory section
121memories: std.wasm.Memory = .{ .limits = .{125memories: std.wasm.Memory = .{ .limits = .{
122 .min = 0,126 .min = 0,
123 .max = undefined,127 .max = undefined,
124 .flags = 0,128 .flags = .{ .has_max = false, .is_shared = false },
125} },129} },
126/// Output table section
127tables: std.ArrayListUnmanaged(std.wasm.Table) = .empty,
128/// Output export section
129exports: std.ArrayListUnmanaged(Export) = .empty,
130/// List of initialization functions. These must be called in order of priority
131/// by the (synthetic) __wasm_call_ctors function.
132init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .empty,
133/// Index to a function defining the entry of the wasm file
134entry: ?u32 = null,
135
136/// Indirect function table, used to call function pointers
137/// When this is non-zero, we must emit a table entry,
138/// as well as an 'elements' section.
139///
140/// Note: Key is symbol location, value represents the index into the table
141function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .empty,
142
143/// All archive files that are lazy loaded.
144/// e.g. when an undefined symbol references a symbol from the archive.
145/// None of this data is serialized to disk because it is trivially reloaded
146/// from unchanged archive files on the next start of the compiler process,
147/// or if those files have changed, the prelink phase needs to be restarted.
148lazy_archives: std.ArrayListUnmanaged(LazyArchive) = .empty,
149
150/// A map of global names to their symbol location
151globals: std.AutoArrayHashMapUnmanaged(String, SymbolLoc) = .empty,
152/// The list of GOT symbols and their location
153got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .empty,
154/// Maps discarded symbols and their positions to the location of the symbol
155/// it was resolved to
156discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .empty,
157/// List of all symbol locations which have been resolved by the linker and will be emit
158/// into the final binary.
159resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .empty,
160/// Symbols that remain undefined after symbol resolution.
161undefs: std.AutoArrayHashMapUnmanaged(String, SymbolLoc) = .empty,
162/// Maps a symbol's location to an atom. This can be used to find meta
163/// data of a symbol, such as its size, or its offset to perform a relocation.
164/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
165symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .empty,
166130
167/// `--verbose-link` output.131/// `--verbose-link` output.
168/// Initialized on creation, appended to as inputs are added, printed during `flush`.132/// Initialized on creation, appended to as inputs are added, printed during `flush`.
169/// String data is allocated into Compilation arena.133/// String data is allocated into Compilation arena.
170dump_argv_list: std.ArrayListUnmanaged([]const u8),134dump_argv_list: std.ArrayListUnmanaged([]const u8),
171135
172/// Represents the index into `segments` where the 'code' section lives.
173code_section_index: Segment.OptionalIndex = .none,
174custom_sections: CustomSections,
175preloaded_strings: PreloadedStrings,136preloaded_strings: PreloadedStrings,
176137
138navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Nav) = .empty,
139nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
140uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
141imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
142
143dwarf: ?Dwarf = null,
144debug_sections: DebugSections = .{},
145
146flush_buffer: Flush = .{},
147
148missing_exports_init: []String = &.{},
149entry_resolution: FunctionImport.Resolution = .unresolved,
150
151/// Empty when outputting an object.
152function_exports: std.ArrayListUnmanaged(FunctionIndex) = .empty,
153/// Tracks the value at the end of prelink.
154function_exports_len: u32 = 0,
155global_exports: std.ArrayListUnmanaged(GlobalIndex) = .empty,
156/// Tracks the value at the end of prelink.
157global_exports_len: u32 = 0,
158
159/// Ordered list of non-import functions that will appear in the final binary.
160/// Empty until prelink.
161functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .empty,
162/// Tracks the value at the end of prelink, at which point `functions`
163/// contains only object file functions, and nothing from the Zcu yet.
164functions_len: u32 = 0,
165/// Immutable after prelink. The undefined functions coming only from all object files.
166/// The Zcu must satisfy these.
167function_imports_init: []FunctionImportId = &.{},
168/// Initialized as copy of `function_imports_init`; entries are deleted as
169/// they are satisfied by the Zcu.
170function_imports: std.AutoArrayHashMapUnmanaged(FunctionImportId, void) = .empty,
171
172/// Ordered list of non-import globals that will appear in the final binary.
173/// Empty until prelink.
174globals: std.AutoArrayHashMapUnmanaged(GlobalImport.Resolution, void) = .empty,
175/// Tracks the value at the end of prelink, at which point `globals`
176/// contains only object file globals, and nothing from the Zcu yet.
177globals_len: u32 = 0,
178global_imports_init: []GlobalImportId = &.{},
179global_imports: std.AutoArrayHashMapUnmanaged(GlobalImportId, void) = .empty,
180
181/// Ordered list of non-import tables that will appear in the final binary.
182/// Empty until prelink.
183tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty,
184table_imports: std.AutoArrayHashMapUnmanaged(ObjectTableImportIndex, void) = .empty,
185
186any_exports_updated: bool = true,
187
188/// Index into `functions`.
189pub const FunctionIndex = enum(u32) {
190 _,
191
192 pub fn fromNav(nav_index: InternPool.Nav.Index, wasm: *const Wasm) FunctionIndex {
193 return @enumFromInt(wasm.functions.getIndex(.pack(wasm, .{ .nav = nav_index })).?);
194 }
195};
196
197/// 0. Index into `function_imports`
198/// 1. Index into `functions`.
199pub const OutputFunctionIndex = enum(u32) {
200 _,
201};
202
203/// Index into `globals`.
204const GlobalIndex = enum(u32) {
205 _,
206
207 fn key(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution {
208 return &f.globals.items[@intFromEnum(index)];
209 }
210};
211
212/// The first N indexes correspond to input objects (`objects`) array.
213/// After that, the indexes correspond to the `source_locations` array,
214/// representing a location in a Zig source file that can be pinpointed
215/// precisely via AST node and token.
216pub const SourceLocation = enum(u32) {
217 /// From the Zig compilation unit but no precise source location.
218 zig_object_nofile = std.math.maxInt(u32) - 1,
219 none = std.math.maxInt(u32),
220 _,
221};
222
223/// The lower bits of this ABI-match the flags here:
224/// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection
225/// The upper bits are used for nefarious purposes.
226pub const SymbolFlags = packed struct(u32) {
227 binding: Binding = .strong,
228 /// Indicating that this is a hidden symbol. Hidden symbols are not to be
229 /// exported when performing the final link, but may be linked to other
230 /// modules.
231 visibility_hidden: bool = false,
232 padding0: u1 = 0,
233 /// For non-data symbols, this must match whether the symbol is an import
234 /// or is defined; for data symbols, determines whether a segment is
235 /// specified.
236 undefined: bool = false,
237 /// The symbol is intended to be exported from the wasm module to the host
238 /// environment. This differs from the visibility flags in that it affects
239 /// static linking.
240 exported: bool = false,
241 /// The symbol uses an explicit symbol name, rather than reusing the name
242 /// from a wasm import. This allows it to remap imports from foreign
243 /// WebAssembly modules into local symbols with different names.
244 explicit_name: bool = false,
245 /// The symbol is intended to be included in the linker output, regardless
246 /// of whether it is used by the program. Same meaning as `retain`.
247 no_strip: bool = false,
248 /// The symbol resides in thread local storage.
249 tls: bool = false,
250 /// The symbol represents an absolute address. This means its offset is
251 /// relative to the start of the wasm memory as opposed to being relative
252 /// to a data segment.
253 absolute: bool = false,
254
255 // Above here matches the tooling conventions ABI.
256
257 padding1: u8 = 0,
258 /// Zig-specific. Dead things are allowed to be garbage collected.
259 alive: bool = false,
260 /// Zig-specific. Segments only. Signals that the segment contains only
261 /// null terminated strings allowing the linker to perform merging.
262 strings: bool = false,
263 /// Zig-specific. This symbol comes from an object that must be included in
264 /// the final link.
265 must_link: bool = false,
266 /// Zig-specific. Data segments only.
267 is_passive: bool = false,
268 /// Zig-specific. Data segments only.
269 alignment: Alignment = .none,
270 /// Zig-specific. Globals only.
271 global_type: Global.Type = .zero,
272
273 pub const Binding = enum(u2) {
274 strong = 0,
275 /// Indicating that this is a weak symbol. When linking multiple modules
276 /// defining the same symbol, all weak definitions are discarded if any
277 /// strong definitions exist; then if multiple weak definitions exist all
278 /// but one (unspecified) are discarded; and finally it is an error if more
279 /// than one definition remains.
280 weak = 1,
281 /// Indicating that this is a local symbol. Local symbols are not to be
282 /// exported, or linked to other modules/sections. The names of all
283 /// non-local symbols must be unique, but the names of local symbols
284 /// are not considered for uniqueness. A local function or global
285 /// symbol cannot reference an import.
286 local = 2,
287 };
288
289 pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void {
290 flags.alive = false;
291 flags.strings = false;
292 flags.must_link = must_link;
293 flags.no_strip = no_strip;
294 flags.alignment = .none;
295 flags.global_type = .zero;
296 flags.is_passive = false;
297 }
298
299 pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool) bool {
300 return flags.exported or
301 (is_dynamic and !flags.visibility_hidden) or
302 (flags.no_strip and flags.must_link);
303 }
304
305 pub fn isExported(flags: SymbolFlags, is_dynamic: bool) bool {
306 if (flags.undefined or flags.binding == .local) return false;
307 if (is_dynamic and !flags.visibility_hidden) return true;
308 return flags.exported;
309 }
310
311 pub fn requiresImport(flags: SymbolFlags, is_data: bool) bool {
312 if (is_data) return false;
313 if (!flags.undefined) return false;
314 if (flags.binding == .weak) return false;
315 return true;
316 }
317
318 /// Returns the name as how it will be output into the final object
319 /// file or binary. When `merge` is true, this will return the
320 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
321 pub fn outputName(flags: SymbolFlags, name: []const u8, merge: bool) []const u8 {
322 if (flags.tls) return ".tdata";
323 if (!merge) return name;
324 if (mem.startsWith(u8, name, ".rodata.")) return ".rodata";
325 if (mem.startsWith(u8, name, ".text.")) return ".text";
326 if (mem.startsWith(u8, name, ".data.")) return ".data";
327 if (mem.startsWith(u8, name, ".bss.")) return ".bss";
328 return name;
329 }
330
331 /// Masks off the Zig-specific stuff.
332 pub fn toAbiInteger(flags: SymbolFlags) u32 {
333 var copy = flags;
334 copy.initZigSpecific(false, false);
335 return @bitCast(copy);
336 }
337};
338
339pub const Nav = extern struct {
340 code: DataSegment.Payload,
341 relocs: Relocation.Slice,
342
343 pub const Code = DataSegment.Payload;
344
345 /// Index into `navs`.
346 /// Note that swapRemove is sometimes performed on `navs`.
347 pub const Index = enum(u32) {
348 _,
349
350 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
351 return &wasm.navs.keys()[@intFromEnum(i)];
352 }
353
354 pub fn value(i: @This(), wasm: *const Wasm) *Nav {
355 return &wasm.navs.values()[@intFromEnum(i)];
356 }
357 };
358};
359
360pub const NavExport = extern struct {
361 name: String,
362 nav_index: InternPool.Nav.Index,
363};
364
365pub const UavExport = extern struct {
366 name: String,
367 uav_index: InternPool.Index,
368};
369
370const DebugSections = struct {
371 abbrev: DebugSection = .{},
372 info: DebugSection = .{},
373 line: DebugSection = .{},
374 loc: DebugSection = .{},
375 pubnames: DebugSection = .{},
376 pubtypes: DebugSection = .{},
377 ranges: DebugSection = .{},
378 str: DebugSection = .{},
379};
380
381const DebugSection = struct {};
382
383pub const FunctionImport = extern struct {
384 flags: SymbolFlags,
385 module_name: String,
386 source_location: SourceLocation,
387 resolution: Resolution,
388 type: FunctionType.Index,
389
390 /// Represents a synthetic function, a function from an object, or a
391 /// function from the Zcu.
392 pub const Resolution = enum(u32) {
393 unresolved,
394 __wasm_apply_global_tls_relocs,
395 __wasm_call_ctors,
396 __wasm_init_memory,
397 __wasm_init_tls,
398 __zig_error_names,
399 // Next, index into `object_functions`.
400 // Next, index into `navs`.
401 _,
402
403 const first_object_function = @intFromEnum(Resolution.__zig_error_names) + 1;
404
405 pub const Unpacked = union(enum) {
406 unresolved,
407 __wasm_apply_global_tls_relocs,
408 __wasm_call_ctors,
409 __wasm_init_memory,
410 __wasm_init_tls,
411 __zig_error_names,
412 object_function: ObjectFunctionIndex,
413 nav: Nav.Index,
414 };
415
416 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
417 return switch (r) {
418 .unresolved => .unresolved,
419 .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,
420 .__wasm_call_ctors => .__wasm_call_ctors,
421 .__wasm_init_memory => .__wasm_init_memory,
422 .__wasm_init_tls => .__wasm_init_tls,
423 .__zig_error_names => .__zig_error_names,
424 _ => {
425 const i: u32 = @intFromEnum(r);
426 const object_function_index = i - first_object_function;
427 if (object_function_index < wasm.object_functions.items.len)
428 return .{ .object_function = @enumFromInt(object_function_index) };
429 const nav_index = object_function_index - wasm.object_functions.items.len;
430 return .{ .nav = @enumFromInt(nav_index) };
431 },
432 };
433 }
434
435 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution {
436 return switch (unpacked) {
437 .unresolved => .unresolved,
438 .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,
439 .__wasm_call_ctors => .__wasm_call_ctors,
440 .__wasm_init_memory => .__wasm_init_memory,
441 .__wasm_init_tls => .__wasm_init_tls,
442 .__zig_error_names => .__zig_error_names,
443 .object_function => |i| @enumFromInt(first_object_function + @intFromEnum(i)),
444 .nav => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)),
445 };
446 }
447
448 pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool {
449 return switch (r.unpack(wasm)) {
450 .unresolved, .nav => true,
451 else => false,
452 };
453 }
454 };
455
456 /// Index into `object_function_imports`.
457 pub const Index = enum(u32) {
458 _,
459 };
460};
461
462pub const Function = extern struct {
463 flags: SymbolFlags,
464 /// `none` if this function has no symbol describing it.
465 name: OptionalString,
466 type_index: FunctionType.Index,
467 code: Code,
468 /// The offset within the section where the data starts.
469 offset: u32,
470 section_index: ObjectSectionIndex,
471 source_location: SourceLocation,
472
473 pub const Code = DataSegment.Payload;
474};
475
476pub const GlobalImport = extern struct {
477 flags: SymbolFlags,
478 module_name: String,
479 source_location: SourceLocation,
480 resolution: Resolution,
481
482 /// Represents a synthetic global, or a global from an object.
483 pub const Resolution = enum(u32) {
484 unresolved,
485 __heap_base,
486 __heap_end,
487 __stack_pointer,
488 __tls_align,
489 __tls_base,
490 __tls_size,
491 __zig_error_name_table,
492 // Next, index into `object_globals`.
493 // Next, index into `navs`.
494 _,
495 };
496};
497
498pub const Global = extern struct {
499 /// `none` if this function has no symbol describing it.
500 name: OptionalString,
501 flags: SymbolFlags,
502 expr: Expr,
503
504 pub const Type = packed struct(u4) {
505 valtype: Valtype,
506 mutable: bool,
507
508 pub const zero: Type = @bitCast(@as(u4, 0));
509 };
510
511 pub const Valtype = enum(u3) {
512 i32,
513 i64,
514 f32,
515 f64,
516 v128,
517
518 pub fn from(v: std.wasm.Valtype) Valtype {
519 return switch (v) {
520 .i32 => .i32,
521 .i64 => .i64,
522 .f32 => .f32,
523 .f64 => .f64,
524 .v128 => .v128,
525 };
526 }
527
528 pub fn to(v: Valtype) std.wasm.Valtype {
529 return switch (v) {
530 .i32 => .i32,
531 .i64 => .i64,
532 .f32 => .f32,
533 .f64 => .f64,
534 .v128 => .v128,
535 };
536 }
537 };
538};
539
540pub const TableImport = extern struct {
541 flags: SymbolFlags,
542 module_name: String,
543 source_location: SourceLocation,
544 resolution: Resolution,
545
546 /// Represents a synthetic table, or a table from an object.
547 pub const Resolution = enum(u32) {
548 unresolved,
549 __indirect_function_table,
550 // Next, index into `object_tables`.
551 _,
552 };
553};
554
555pub const Table = extern struct {
556 module_name: String,
557 name: String,
558 flags: SymbolFlags,
559 limits_min: u32,
560 limits_max: u32,
561 limits_has_max: bool,
562 limits_is_shared: bool,
563 reftype: std.wasm.RefType,
564 padding: [1]u8 = .{0},
565};
566
567/// Uniquely identifies a section across all objects. Each Object has a section_start field.
568/// By subtracting that value from this one, the Object section index is obtained.
569pub const ObjectSectionIndex = enum(u32) {
570 _,
571};
572
573/// Index into `object_function_imports`.
574pub const ObjectFunctionImportIndex = enum(u32) {
575 _,
576
577 pub fn ptr(index: ObjectFunctionImportIndex, wasm: *const Wasm) *FunctionImport {
578 return &wasm.object_function_imports.items[@intFromEnum(index)];
579 }
580};
581
582/// Index into `object_global_imports`.
583pub const ObjectGlobalImportIndex = enum(u32) {
584 _,
585};
586
587/// Index into `object_table_imports`.
588pub const ObjectTableImportIndex = enum(u32) {
589 _,
590};
591
592/// Index into `object_tables`.
593pub const ObjectTableIndex = enum(u32) {
594 _,
595
596 pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table {
597 return &wasm.object_tables.items[@intFromEnum(index)];
598 }
599};
600
601/// Index into `global_imports`.
602pub const GlobalImportIndex = enum(u32) {
603 _,
604};
605
606/// Index into `object_globals`.
607pub const ObjectGlobalIndex = enum(u32) {
608 _,
609};
610
611/// Index into `object_functions`.
612pub const ObjectFunctionIndex = enum(u32) {
613 _,
614
615 pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *Function {
616 return &wasm.object_functions.items[@intFromEnum(index)];
617 }
618
619 pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex {
620 const result: OptionalObjectFunctionIndex = @enumFromInt(@intFromEnum(i));
621 assert(result != .none);
622 return result;
623 }
624};
625
626/// Index into `object_functions`, or null.
627pub const OptionalObjectFunctionIndex = enum(u32) {
628 none = std.math.maxInt(u32),
629 _,
630
631 pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex {
632 if (i == .none) return null;
633 return @enumFromInt(@intFromEnum(i));
634 }
635};
636
637pub const DataSegment = extern struct {
638 /// `none` if no symbol describes it.
639 name: OptionalString,
640 flags: SymbolFlags,
641 payload: Payload,
642 /// From the data segment start to the first byte of payload.
643 segment_offset: u32,
644 section_index: ObjectSectionIndex,
645
646 pub const Payload = extern struct {
647 /// Points into string_bytes. No corresponding string_table entry.
648 off: u32,
649 /// The size in bytes of the data representing the segment within the section.
650 len: u32,
651
652 fn slice(p: DataSegment.Payload, wasm: *const Wasm) []const u8 {
653 return wasm.string_bytes.items[p.off..][0..p.len];
654 }
655 };
656
657 /// Index into `object_data_segments`.
658 pub const Index = enum(u32) {
659 _,
660 };
661};
662
663pub const CustomSegment = extern struct {
664 payload: Payload,
665 flags: SymbolFlags,
666 section_name: String,
667
668 pub const Payload = DataSegment.Payload;
669};
670
671/// An index into string_bytes where a wasm expression is found.
672pub const Expr = enum(u32) {
673 _,
674};
675
676pub const FunctionType = extern struct {
677 params: ValtypeList,
678 returns: ValtypeList,
679
680 /// Index into func_types
681 pub const Index = enum(u32) {
682 _,
683
684 pub fn ptr(i: FunctionType.Index, wasm: *const Wasm) *FunctionType {
685 return &wasm.func_types.keys()[@intFromEnum(i)];
686 }
687 };
688
689 pub const format = @compileError("can't format without *Wasm reference");
690
691 pub fn eql(a: FunctionType, b: FunctionType) bool {
692 return a.params == b.params and a.returns == b.returns;
693 }
694};
695
696/// Represents a function entry, holding the index to its type
697pub const Func = extern struct {
698 type_index: FunctionType.Index,
699};
700
177/// Type reflection is used on the field names to autopopulate each field701/// Type reflection is used on the field names to autopopulate each field
178/// during initialization.702/// during initialization.
179const PreloadedStrings = struct {703const PreloadedStrings = struct {
...@@ -190,31 +714,14 @@ const PreloadedStrings = struct {...@@ -190,31 +714,14 @@ const PreloadedStrings = struct {
190 __wasm_init_memory: String,714 __wasm_init_memory: String,
191 __wasm_init_memory_flag: String,715 __wasm_init_memory_flag: String,
192 __wasm_init_tls: String,716 __wasm_init_tls: String,
193 __zig_err_name_table: String,717 __zig_error_name_table: String,
194 __zig_err_names: String,718 __zig_error_names: String,
195 __zig_errors_len: String,719 __zig_errors_len: String,
196 _initialize: String,720 _initialize: String,
197 _start: String,721 _start: String,
198 memory: String,722 memory: String,
199};723};
200724
201/// Type reflection is used on the field names to autopopulate each inner `name` field.
202const CustomSections = struct {
203 @".debug_info": CustomSection,
204 @".debug_pubtypes": CustomSection,
205 @".debug_abbrev": CustomSection,
206 @".debug_line": CustomSection,
207 @".debug_str": CustomSection,
208 @".debug_pubnames": CustomSection,
209 @".debug_loc": CustomSection,
210 @".debug_ranges": CustomSection,
211};
212
213const CustomSection = struct {
214 name: String,
215 index: Segment.OptionalIndex,
216};
217
218/// Index into string_bytes725/// Index into string_bytes
219pub const String = enum(u32) {726pub const String = enum(u32) {
220 _,727 _,
...@@ -246,6 +753,11 @@ pub const String = enum(u32) {...@@ -246,6 +753,11 @@ pub const String = enum(u32) {
246 }753 }
247 };754 };
248755
756 pub fn slice(index: String, wasm: *const Wasm) [:0]const u8 {
757 const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];
758 return start_slice[0..mem.indexOfScalar(u8, start_slice, 0).? :0];
759 }
760
249 pub fn toOptional(i: String) OptionalString {761 pub fn toOptional(i: String) OptionalString {
250 const result: OptionalString = @enumFromInt(@intFromEnum(i));762 const result: OptionalString = @enumFromInt(@intFromEnum(i));
251 assert(result != .none);763 assert(result != .none);
...@@ -261,160 +773,242 @@ pub const OptionalString = enum(u32) {...@@ -261,160 +773,242 @@ pub const OptionalString = enum(u32) {
261 if (i == .none) return null;773 if (i == .none) return null;
262 return @enumFromInt(@intFromEnum(i));774 return @enumFromInt(@intFromEnum(i));
263 }775 }
264};
265
266/// Index into objects array or the zig object.
267pub const ObjectId = enum(u16) {
268 zig_object = std.math.maxInt(u16) - 1,
269 _,
270776
271 pub fn toOptional(i: ObjectId) OptionalObjectId {777 pub fn slice(index: OptionalString, wasm: *const Wasm) ?[:0]const u8 {
272 const result: OptionalObjectId = @enumFromInt(@intFromEnum(i));778 return (index.unwrap() orelse return null).slice(wasm);
273 assert(result != .none);
274 return result;
275 }779 }
276};780};
277781
278/// Optional index into objects array or the zig object.782/// Stored identically to `String`. The bytes are reinterpreted as
279pub const OptionalObjectId = enum(u16) {783/// `std.wasm.Valtype` elements.
280 zig_object = std.math.maxInt(u16) - 1,784pub const ValtypeList = enum(u32) {
281 none = std.math.maxInt(u16),
282 _,785 _,
283786
284 pub fn unwrap(i: OptionalObjectId) ?ObjectId {787 pub fn fromString(s: String) ValtypeList {
285 if (i == .none) return null;788 return @enumFromInt(@intFromEnum(s));
286 return @enumFromInt(@intFromEnum(i));
287 }789 }
288};
289790
290/// None of this data is serialized since it can be re-loaded from disk, or if791 pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype {
291/// it has been changed, the data must be discarded.792 return @bitCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));
292const LazyArchive = struct {
293 path: Path,
294 file_contents: []const u8,
295 archive: Archive,
296
297 fn deinit(la: *LazyArchive, gpa: Allocator) void {
298 la.archive.deinit(gpa);
299 gpa.free(la.path.sub_path);
300 gpa.free(la.file_contents);
301 la.* = undefined;
302 }793 }
303};794};
304795
305pub const Segment = struct {796/// 0. Index into `object_function_imports`.
306 alignment: Alignment,797/// 1. Index into `imports`.
307 size: u32,798pub const FunctionImportId = enum(u32) {
308 offset: u32,799 _,
309 flags: u32,800};
310801
311 const Index = enum(u32) {802/// 0. Index into `object_global_imports`.
312 _,803/// 1. Index into `imports`.
804pub const GlobalImportId = enum(u32) {
805 _,
806};
313807
314 pub fn toOptional(i: Index) OptionalIndex {808pub const Relocation = struct {
315 const result: OptionalIndex = @enumFromInt(@intFromEnum(i));809 tag: Tag,
316 assert(result != .none);810 /// Offset of the value to rewrite relative to the relevant section's contents.
317 return result;811 /// When `offset` is zero, its position is immediately after the id and size of the section.
318 }812 offset: u32,
813 pointee: Pointee,
814 /// Populated only for `MEMORY_ADDR_*`, `FUNCTION_OFFSET_I32` and `SECTION_OFFSET_I32`.
815 addend: i32,
816
817 pub const Pointee = union {
818 symbol_name: String,
819 type_index: FunctionType.Index,
820 section: ObjectSectionIndex,
821 nav_index: InternPool.Nav.Index,
822 uav_index: InternPool.Index,
319 };823 };
320824
321 const OptionalIndex = enum(u32) {825 pub const Slice = extern struct {
322 none = std.math.maxInt(u32),826 /// Index into `relocations`.
323 _,827 off: u32,
828 len: u32,
324829
325 pub fn unwrap(i: OptionalIndex) ?Index {830 pub fn slice(s: Slice, wasm: *const Wasm) []Relocation {
326 if (i == .none) return null;831 return wasm.relocations.items[s.off..][0..s.len];
327 return @enumFromInt(@intFromEnum(i));
328 }832 }
329 };833 };
330834
331 pub const Flag = enum(u32) {835 pub const Tag = enum(u8) {
332 WASM_DATA_SEGMENT_IS_PASSIVE = 0x01,836 /// Uses `symbol_name`.
333 WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02,837 FUNCTION_INDEX_LEB = 0,
838 /// Uses `table_index`.
839 TABLE_INDEX_SLEB = 1,
840 /// Uses `table_index`.
841 TABLE_INDEX_I32 = 2,
842 MEMORY_ADDR_LEB = 3,
843 MEMORY_ADDR_SLEB = 4,
844 MEMORY_ADDR_I32 = 5,
845 /// Uses `type_index`.
846 TYPE_INDEX_LEB = 6,
847 /// Uses `symbol_name`.
848 GLOBAL_INDEX_LEB = 7,
849 FUNCTION_OFFSET_I32 = 8,
850 SECTION_OFFSET_I32 = 9,
851 TAG_INDEX_LEB = 10,
852 MEMORY_ADDR_REL_SLEB = 11,
853 TABLE_INDEX_REL_SLEB = 12,
854 /// Uses `symbol_name`.
855 GLOBAL_INDEX_I32 = 13,
856 MEMORY_ADDR_LEB64 = 14,
857 MEMORY_ADDR_SLEB64 = 15,
858 MEMORY_ADDR_I64 = 16,
859 MEMORY_ADDR_REL_SLEB64 = 17,
860 /// Uses `table_index`.
861 TABLE_INDEX_SLEB64 = 18,
862 /// Uses `table_index`.
863 TABLE_INDEX_I64 = 19,
864 TABLE_NUMBER_LEB = 20,
865 MEMORY_ADDR_TLS_SLEB = 21,
866 FUNCTION_OFFSET_I64 = 22,
867 MEMORY_ADDR_LOCREL_I32 = 23,
868 TABLE_INDEX_REL_SLEB64 = 24,
869 MEMORY_ADDR_TLS_SLEB64 = 25,
870 /// Uses `symbol_name`.
871 FUNCTION_INDEX_I32 = 26,
872
873 // Above here, the tags correspond to symbol table ABI described in
874 // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
875 // Below, the tags are compiler-internal.
876
877 /// Uses `nav_index`. 4 or 8 bytes depending on wasm32 or wasm64.
878 nav_index,
879 /// Uses `uav_index`. 4 or 8 bytes depending on wasm32 or wasm64.
880 uav_index,
334 };881 };
335
336 pub fn isPassive(segment: Segment) bool {
337 return segment.flags & @intFromEnum(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0;
338 }
339
340 /// For a given segment, determines if it needs passive initialization
341 fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool {
342 if (import_mem and !std.mem.eql(u8, name, ".bss")) {
343 return true;
344 }
345 return segment.isPassive();
346 }
347};882};
348883
349pub const SymbolLoc = struct {884pub const MemoryImport = extern struct {
350 /// The index of the symbol within the specified file885 module_name: String,
351 index: Symbol.Index,886 name: String,
352 /// The index of the object file where the symbol resides.887 limits_min: u32,
353 file: OptionalObjectId,888 limits_max: u32,
889 limits_has_max: bool,
890 limits_is_shared: bool,
891 padding: [2]u8 = .{ 0, 0 },
354};892};
355893
356/// From a given location, returns the corresponding symbol in the wasm binary894pub const Alignment = InternPool.Alignment;
357pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol {
358 if (wasm.discarded.get(loc)) |new_loc| {
359 return symbolLocSymbol(wasm, new_loc);
360 }
361 return switch (loc.file) {
362 .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)],
363 .zig_object => wasm.zig_object.?.symbol(loc.index),
364 _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)],
365 };
366}
367895
368/// From a given location, returns the name of the symbol.896pub const InitFunc = extern struct {
369pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) [:0]const u8 {897 priority: u32,
370 return wasm.stringSlice(wasm.symbolLocSymbol(loc).name);898 function_index: ObjectFunctionIndex,
371}
372899
373/// From a given symbol location, returns the final location.900 fn lessThan(ctx: void, lhs: InitFunc, rhs: InitFunc) bool {
374/// e.g. when a symbol was resolved and replaced by the symbol901 _ = ctx;
375/// in a different file, this will return said location.902 if (lhs.priority == rhs.priority) {
376/// If the symbol wasn't replaced by another, this will return903 return @intFromEnum(lhs.function_index) < @intFromEnum(rhs.function_index);
377/// the given location itwasm.904 } else {
378pub fn symbolLocFinalLoc(wasm: *const Wasm, loc: SymbolLoc) SymbolLoc {905 return lhs.priority < rhs.priority;
379 if (wasm.discarded.get(loc)) |new_loc| {906 }
380 return symbolLocFinalLoc(wasm, new_loc);
381 }907 }
382 return loc;908};
383}
384909
385// Contains the location of the function symbol, as well as910pub const Comdat = struct {
386/// the priority itself of the initialization function.911 name: String,
387pub const InitFuncLoc = struct {912 /// Must be zero, no flags are currently defined by the tool-convention.
388 /// object file index in the list of objects.913 flags: u32,
389 /// Unlike `SymbolLoc` this cannot be `null` as we never define914 symbols: Comdat.Symbol.Slice,
390 /// our own ctors.
391 file: ObjectId,
392 /// Symbol index within the corresponding object file.
393 index: Symbol.Index,
394 /// The priority in which the constructor must be called.
395 priority: u32,
396915
397 /// From a given `InitFuncLoc` returns the corresponding function symbol916 pub const Symbol = struct {
398 fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol {917 kind: Comdat.Symbol.Type,
399 return wasm.symbolLocSymbol(getSymbolLoc(loc));918 /// Index of the data segment/function/global/event/table within a WASM module.
400 }919 /// The object must not be an import.
920 index: u32,
401921
402 /// Turns the given `InitFuncLoc` into a `SymbolLoc`922 pub const Slice = struct {
403 fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc {923 /// Index into Wasm object_comdat_symbols
404 return .{924 off: u32,
405 .file = loc.file.toOptional(),925 len: u32,
406 .index = loc.index,
407 };926 };
408 }
409927
410 /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`.928 pub const Type = enum(u8) {
411 fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool {929 data = 0,
412 _ = ctx;930 function = 1,
413 return lhs.priority < rhs.priority;931 global = 2,
414 }932 event = 3,
933 table = 4,
934 section = 5,
935 };
936 };
415};937};
416938
417pub fn open(939/// Stored as a u8 so it can reuse the string table mechanism.
940pub const Feature = packed struct(u8) {
941 prefix: Prefix,
942 /// Type of the feature, must be unique in the sequence of features.
943 tag: Tag,
944
945 /// Stored identically to `String`. The bytes are reinterpreted as `Feature`
946 /// elements. Elements must be sorted before string-interning.
947 pub const Set = enum(u32) {
948 _,
949
950 pub fn fromString(s: String) Set {
951 return @enumFromInt(@intFromEnum(s));
952 }
953 };
954
955 /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem.
956 /// Additionally the name uses convention matching the wasm binary format.
957 pub const Tag = enum(u6) {
958 atomics,
959 @"bulk-memory",
960 @"exception-handling",
961 @"extended-const",
962 @"half-precision",
963 multimemory,
964 multivalue,
965 @"mutable-globals",
966 @"nontrapping-fptoint",
967 @"reference-types",
968 @"relaxed-simd",
969 @"sign-ext",
970 simd128,
971 @"tail-call",
972 @"shared-mem",
973
974 pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
975 return @enumFromInt(@intFromEnum(feature));
976 }
977
978 pub const format = @compileError("use @tagName instead");
979 };
980
981 /// Provides information about the usage of the feature.
982 pub const Prefix = enum(u2) {
983 /// Reserved so that a 0-byte Feature is invalid and therefore can be a sentinel.
984 invalid,
985 /// '0x2b': Object uses this feature, and the link fails if feature is
986 /// not in the allowed set.
987 @"+",
988 /// '0x2d': Object does not use this feature, and the link fails if
989 /// this feature is in the allowed set.
990 @"-",
991 /// '0x3d': Object uses this feature, and the link fails if this
992 /// feature is not in the allowed set, or if any object does not use
993 /// this feature.
994 @"=",
995 };
996
997 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
998 _ = opt;
999 _ = fmt;
1000 try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
1001 }
1002
1003 pub fn lessThan(_: void, a: Feature, b: Feature) bool {
1004 assert(a != b);
1005 const a_int: u8 = @bitCast(a);
1006 const b_int: u8 = @bitCast(b);
1007 return a_int < b_int;
1008 }
1009};
1010
1011pub fn open(
418 arena: Allocator,1012 arena: Allocator,
419 comp: *Compilation,1013 comp: *Compilation,
420 emit: Path,1014 emit: Path,
...@@ -431,14 +1025,12 @@ pub fn createEmpty(...@@ -431,14 +1025,12 @@ pub fn createEmpty(
431 emit: Path,1025 emit: Path,
432 options: link.File.OpenOptions,1026 options: link.File.OpenOptions,
433) !*Wasm {1027) !*Wasm {
434 const gpa = comp.gpa;
435 const target = comp.root_mod.resolved_target.result;1028 const target = comp.root_mod.resolved_target.result;
436 assert(target.ofmt == .wasm);1029 assert(target.ofmt == .wasm);
4371030
438 const use_lld = build_options.have_llvm and comp.config.use_lld;1031 const use_lld = build_options.have_llvm and comp.config.use_lld;
439 const use_llvm = comp.config.use_llvm;1032 const use_llvm = comp.config.use_llvm;
440 const output_mode = comp.config.output_mode;1033 const output_mode = comp.config.output_mode;
441 const shared_memory = comp.config.shared_memory;
442 const wasi_exec_model = comp.config.wasi_exec_model;1034 const wasi_exec_model = comp.config.wasi_exec_model;
4431035
444 // If using LLD to link, this code should produce an object file so that it1036 // If using LLD to link, this code should produce an object file so that it
...@@ -458,6 +1050,11 @@ pub fn createEmpty(...@@ -458,6 +1050,11 @@ pub fn createEmpty(
458 .comp = comp,1050 .comp = comp,
459 .emit = emit,1051 .emit = emit,
460 .zcu_object_sub_path = zcu_object_sub_path,1052 .zcu_object_sub_path = zcu_object_sub_path,
1053 // Garbage collection is so crucial to WebAssembly that we design
1054 // the linker around the assumption that it will be on in the vast
1055 // majority of cases, and therefore express "no garbage collection"
1056 // in terms of setting the no_strip and must_link flags on all
1057 // symbols.
461 .gc_sections = options.gc_sections orelse (output_mode != .Obj),1058 .gc_sections = options.gc_sections orelse (output_mode != .Obj),
462 .print_gc_sections = options.print_gc_sections,1059 .print_gc_sections = options.print_gc_sections,
463 .stack_size = options.stack_size orelse switch (target.os.tag) {1060 .stack_size = options.stack_size orelse switch (target.os.tag) {
...@@ -481,10 +1078,8 @@ pub fn createEmpty(...@@ -481,10 +1078,8 @@ pub fn createEmpty(
481 .max_memory = options.max_memory,1078 .max_memory = options.max_memory,
4821079
483 .entry_name = undefined,1080 .entry_name = undefined,
484 .zig_object = null,
485 .dump_argv_list = .empty,1081 .dump_argv_list = .empty,
486 .host_name = undefined,1082 .host_name = undefined,
487 .custom_sections = undefined,
488 .preloaded_strings = undefined,1083 .preloaded_strings = undefined,
489 };1084 };
490 if (use_llvm and comp.config.have_zcu) {1085 if (use_llvm and comp.config.have_zcu) {
...@@ -494,13 +1089,6 @@ pub fn createEmpty(...@@ -494,13 +1089,6 @@ pub fn createEmpty(
4941089
495 wasm.host_name = try wasm.internString("env");1090 wasm.host_name = try wasm.internString("env");
4961091
497 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {
498 @field(wasm.custom_sections, field.name) = .{
499 .index = .none,
500 .name = try wasm.internString(field.name),
501 };
502 }
503
504 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {1092 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {
505 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);1093 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);
506 }1094 }
...@@ -535,181 +1123,9 @@ pub fn createEmpty(...@@ -535,181 +1123,9 @@ pub fn createEmpty(
535 });1123 });
536 wasm.name = sub_path;1124 wasm.name = sub_path;
5371125
538 // create stack pointer symbol
539 {
540 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__stack_pointer, .global);
541 const symbol = wasm.symbolLocSymbol(loc);
542 // For object files we will import the stack pointer symbol
543 if (output_mode == .Obj) {
544 symbol.setUndefined(true);
545 symbol.index = @intCast(wasm.imported_globals_count);
546 wasm.imported_globals_count += 1;
547 try wasm.imports.putNoClobber(gpa, loc, .{
548 .module_name = wasm.host_name,
549 .name = symbol.name,
550 .kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
551 });
552 } else {
553 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
554 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
555 const global = try wasm.wasm_globals.addOne(gpa);
556 global.* = .{
557 .global_type = .{
558 .valtype = .i32,
559 .mutable = true,
560 },
561 .init = .{ .i32_const = 0 },
562 };
563 }
564 }
565
566 // create indirect function pointer symbol
567 {
568 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__indirect_function_table, .table);
569 const symbol = wasm.symbolLocSymbol(loc);
570 const table: std.wasm.Table = .{
571 .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable`
572 .reftype = .funcref,
573 };
574 if (output_mode == .Obj or options.import_table) {
575 symbol.setUndefined(true);
576 symbol.index = @intCast(wasm.imported_tables_count);
577 wasm.imported_tables_count += 1;
578 try wasm.imports.put(gpa, loc, .{
579 .module_name = wasm.host_name,
580 .name = symbol.name,
581 .kind = .{ .table = table },
582 });
583 } else {
584 symbol.index = @as(u32, @intCast(wasm.imported_tables_count + wasm.tables.items.len));
585 try wasm.tables.append(gpa, table);
586 if (wasm.export_table) {
587 symbol.setFlag(.WASM_SYM_EXPORTED);
588 } else {
589 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
590 }
591 }
592 }
593
594 // create __wasm_call_ctors
595 {
596 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_call_ctors, .function);
597 const symbol = wasm.symbolLocSymbol(loc);
598 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
599 // we do not know the function index until after we merged all sections.
600 // Therefore we set `symbol.index` and create its corresponding references
601 // at the end during `initializeCallCtorsFunction`.
602 }
603
604 // shared-memory symbols for TLS support
605 if (shared_memory) {
606 {
607 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_base, .global);
608 const symbol = wasm.symbolLocSymbol(loc);
609 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
610 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
611 symbol.mark();
612 try wasm.wasm_globals.append(gpa, .{
613 .global_type = .{ .valtype = .i32, .mutable = true },
614 .init = .{ .i32_const = undefined },
615 });
616 }
617 {
618 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_size, .global);
619 const symbol = wasm.symbolLocSymbol(loc);
620 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
621 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
622 symbol.mark();
623 try wasm.wasm_globals.append(gpa, .{
624 .global_type = .{ .valtype = .i32, .mutable = false },
625 .init = .{ .i32_const = undefined },
626 });
627 }
628 {
629 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_align, .global);
630 const symbol = wasm.symbolLocSymbol(loc);
631 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
632 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
633 symbol.mark();
634 try wasm.wasm_globals.append(gpa, .{
635 .global_type = .{ .valtype = .i32, .mutable = false },
636 .init = .{ .i32_const = undefined },
637 });
638 }
639 {
640 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_tls, .function);
641 const symbol = wasm.symbolLocSymbol(loc);
642 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
643 }
644 }
645
646 if (comp.zcu) |zcu| {
647 if (!use_llvm) {
648 const zig_object = try arena.create(ZigObject);
649 wasm.zig_object = zig_object;
650 zig_object.* = .{
651 .path = .{
652 .root_dir = std.Build.Cache.Directory.cwd(),
653 .sub_path = try std.fmt.allocPrint(gpa, "{s}.o", .{fs.path.stem(zcu.main_mod.root_src_path)}),
654 },
655 .stack_pointer_sym = .null,
656 };
657 try zig_object.init(wasm);
658 }
659 }
660
661 return wasm;1126 return wasm;
662}1127}
6631128
664pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
665 var index: u32 = 0;
666 while (index < wasm.func_types.items.len) : (index += 1) {
667 if (wasm.func_types.items[index].eql(func_type)) return index;
668 }
669 return null;
670}
671
672/// Either creates a new import, or updates one if existing.
673/// When `type_index` is non-null, we assume an external function.
674/// In all other cases, a data-symbol will be created instead.
675pub fn addOrUpdateImport(
676 wasm: *Wasm,
677 /// Name of the import
678 name: []const u8,
679 /// Symbol index that is external
680 symbol_index: Symbol.Index,
681 /// Optional library name (i.e. `extern "c" fn foo() void`
682 lib_name: ?[:0]const u8,
683 /// The index of the type that represents the function signature
684 /// when the extern is a function. When this is null, a data-symbol
685 /// is asserted instead.
686 type_index: ?u32,
687) !void {
688 return wasm.zig_object.?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index);
689}
690
691/// For a given name, creates a new global synthetic symbol.
692/// Leaves index undefined and the default flags (0).
693fn createSyntheticSymbol(wasm: *Wasm, name: String, tag: Symbol.Tag) !SymbolLoc {
694 return wasm.createSyntheticSymbolOffset(name, tag);
695}
696
697fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: String, tag: Symbol.Tag) !SymbolLoc {
698 const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);
699 const loc: SymbolLoc = .{ .index = sym_index, .file = .none };
700 const gpa = wasm.base.comp.gpa;
701 try wasm.synthetic_symbols.append(gpa, .{
702 .name = name_offset,
703 .flags = 0,
704 .tag = tag,
705 .index = undefined,
706 .virtual_address = undefined,
707 });
708 try wasm.resolved_symbols.putNoClobber(gpa, loc, {});
709 try wasm.globals.put(gpa, name_offset, loc);
710 return loc;
711}
712
713fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {1129fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {
714 const diags = &wasm.base.comp.link_diags;1130 const diags = &wasm.base.comp.link_diags;
715 const obj = link.openObject(path, false, false) catch |err| {1131 const obj = link.openObject(path, false, false) catch |err| {
...@@ -725,8 +1141,11 @@ fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {...@@ -725,8 +1141,11 @@ fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {
725}1141}
7261142
727fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {1143fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
728 defer obj.file.close();
729 const gpa = wasm.base.comp.gpa;1144 const gpa = wasm.base.comp.gpa;
1145 const gc_sections = wasm.base.gc_sections;
1146
1147 defer obj.file.close();
1148
730 try wasm.objects.ensureUnusedCapacity(gpa, 1);1149 try wasm.objects.ensureUnusedCapacity(gpa, 1);
731 const stat = try obj.file.stat();1150 const stat = try obj.file.stat();
732 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;1151 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
...@@ -737,33 +1156,16 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -737,33 +1156,16 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
737 const n = try obj.file.preadAll(file_contents, 0);1156 const n = try obj.file.preadAll(file_contents, 0);
738 if (n != file_contents.len) return error.UnexpectedEndOfFile;1157 if (n != file_contents.len) return error.UnexpectedEndOfFile;
7391158
740 wasm.objects.appendAssumeCapacity(try Object.create(wasm, file_contents, obj.path, null));1159 var ss: Object.ScratchSpace = .{};
741}1160 defer ss.deinit(gpa);
742
743/// Creates a new empty `Atom` and returns its `Atom.Index`
744pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, object_index: OptionalObjectId) !Atom.Index {
745 const gpa = wasm.base.comp.gpa;
746 const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len);
747 const atom = try wasm.managed_atoms.addOne(gpa);
748 atom.* = .{
749 .file = object_index,
750 .sym_index = sym_index,
751 };
752 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index);
753
754 return index;
755}
7561161
757pub fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {1162 const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.host_name, &ss, obj.must_link, gc_sections);
758 return wasm.managed_atoms.items[@intFromEnum(index)];1163 wasm.objects.appendAssumeCapacity(object);
759}
760
761pub fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
762 return &wasm.managed_atoms.items[@intFromEnum(index)];
763}1164}
7641165
765fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {1166fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
766 const gpa = wasm.base.comp.gpa;1167 const gpa = wasm.base.comp.gpa;
1168 const gc_sections = wasm.base.gc_sections;
7671169
768 defer obj.file.close();1170 defer obj.file.close();
7691171
...@@ -771,28 +1173,12 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -771,28 +1173,12 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
771 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;1173 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
7721174
773 const file_contents = try gpa.alloc(u8, size);1175 const file_contents = try gpa.alloc(u8, size);
774 var keep_file_contents = false;1176 defer gpa.free(file_contents);
775 defer if (!keep_file_contents) gpa.free(file_contents);
7761177
777 const n = try obj.file.preadAll(file_contents, 0);1178 const n = try obj.file.preadAll(file_contents, 0);
778 if (n != file_contents.len) return error.UnexpectedEndOfFile;1179 if (n != file_contents.len) return error.UnexpectedEndOfFile;
7791180
780 var archive = try Archive.parse(gpa, file_contents);1181 var archive = try Archive.parse(gpa, file_contents);
781
782 if (!obj.must_link) {
783 errdefer archive.deinit(gpa);
784 try wasm.lazy_archives.append(gpa, .{
785 .path = .{
786 .root_dir = obj.path.root_dir,
787 .sub_path = try gpa.dupe(u8, obj.path.sub_path),
788 },
789 .file_contents = file_contents,
790 .archive = archive,
791 });
792 keep_file_contents = true;
793 return;
794 }
795
796 defer archive.deinit(gpa);1182 defer archive.deinit(gpa);
7971183
798 // In this case we must force link all embedded object files within the archive1184 // In this case we must force link all embedded object files within the archive
...@@ -806,2597 +1192,538 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -806,2597 +1192,538 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
806 }1192 }
807 }1193 }
8081194
1195 var ss: Object.ScratchSpace = .{};
1196 defer ss.deinit(gpa);
1197
1198 try wasm.objects.ensureUnusedCapacity(gpa, offsets.count());
809 for (offsets.keys()) |file_offset| {1199 for (offsets.keys()) |file_offset| {
810 const object = try archive.parseObject(wasm, file_contents[file_offset..], obj.path);1200 const contents = file_contents[file_offset..];
811 try wasm.objects.append(gpa, object);1201 const object = try archive.parseObject(wasm, contents, obj.path, wasm.host_name, &ss, obj.must_link, gc_sections);
1202 wasm.objects.appendAssumeCapacity(object);
812 }1203 }
813}1204}
8141205
815fn requiresTLSReloc(wasm: *const Wasm) bool {1206pub fn deinit(wasm: *Wasm) void {
816 for (wasm.got_symbols.items) |loc| {1207 const gpa = wasm.base.comp.gpa;
817 if (wasm.symbolLocSymbol(loc).isTLS()) {1208 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
818 return true;
819 }
820 }
821 return false;
822}
8231209
824fn objectPath(wasm: *const Wasm, object_id: ObjectId) Path {1210 wasm.navs.deinit(gpa);
825 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.path;1211 wasm.nav_exports.deinit(gpa);
826 return obj.path;1212 wasm.uav_exports.deinit(gpa);
827}1213 wasm.imports.deinit(gpa);
8281214
829fn objectSymbols(wasm: *const Wasm, object_id: ObjectId) []const Symbol {1215 wasm.flush_buffer.deinit(gpa);
830 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items;1216
831 return obj.symtable;1217 if (wasm.dwarf) |*dwarf| dwarf.deinit();
832}1218
1219 wasm.object_function_imports.deinit(gpa);
1220 wasm.object_functions.deinit(gpa);
1221 wasm.object_global_imports.deinit(gpa);
1222 wasm.object_globals.deinit(gpa);
1223 wasm.object_table_imports.deinit(gpa);
1224 wasm.object_tables.deinit(gpa);
1225 wasm.object_memory_imports.deinit(gpa);
1226 wasm.object_memories.deinit(gpa);
1227
1228 wasm.object_data_segments.deinit(gpa);
1229 wasm.object_relocatable_codes.deinit(gpa);
1230 wasm.object_custom_segments.deinit(gpa);
1231 wasm.object_symbols.deinit(gpa);
1232 wasm.object_named_segments.deinit(gpa);
1233 wasm.object_init_funcs.deinit(gpa);
1234 wasm.object_comdats.deinit(gpa);
1235 wasm.object_relocations.deinit(gpa);
1236 wasm.object_relocations_table.deinit(gpa);
1237 wasm.object_comdat_symbols.deinit(gpa);
1238 wasm.objects.deinit(gpa);
8331239
834fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol {1240 wasm.atoms.deinit(gpa);
835 const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)];
836 return &obj.symtable[@intFromEnum(index)];
837}
8381241
839fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func {1242 wasm.synthetic_symbols.deinit(gpa);
840 const obj = wasm.objectById(object_id) orelse {1243 wasm.globals.deinit(gpa);
841 const zo = wasm.zig_object.?;1244 wasm.undefs.deinit(gpa);
842 const sym = zo.symbols.items[@intFromEnum(sym_index)];1245 wasm.discarded.deinit(gpa);
843 return zo.functions.items[sym.index];1246 wasm.segments.deinit(gpa);
844 };1247 wasm.segment_info.deinit(gpa);
845 const sym = obj.symtable[@intFromEnum(sym_index)];
846 return obj.functions[sym.index - obj.imported_functions_count];
847}
8481248
849fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 {1249 wasm.global_imports.deinit(gpa);
850 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count;1250 wasm.func_types.deinit(gpa);
851 return obj.imported_functions_count;1251 wasm.functions.deinit(gpa);
852}1252 wasm.output_globals.deinit(gpa);
1253 wasm.exports.deinit(gpa);
8531254
854fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global {1255 wasm.string_bytes.deinit(gpa);
855 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items;1256 wasm.string_table.deinit(gpa);
856 return obj.globals;1257 wasm.dump_argv_list.deinit(gpa);
857}1258}
8581259
859fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type {1260pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
860 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items;1261 if (build_options.skip_non_native and builtin.object_format != .wasm) {
861 return obj.func_types;1262 @panic("Attempted to compile for object format that was disabled by build configuration");
862}1263 }
1264 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
8631265
864fn objectSegmentInfo(wasm: *const Wasm, object_id: ObjectId) []const NamedSegment {1266 const zcu = pt.zcu;
865 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items;1267 const gpa = zcu.gpa;
866 return obj.segment_info;1268 const func = pt.zcu.funcInfo(func_index);
867}1269 const nav_index = func.owner_nav;
1270
1271 const code_start: u32 = @intCast(wasm.string_bytes.items.len);
1272 const relocs_start: u32 = @intCast(wasm.relocations.items.len);
1273 wasm.string_bytes_lock.lock();
1274
1275 const wasm_codegen = @import("../../arch/wasm/CodeGen.zig");
1276 dev.check(.wasm_backend);
1277 const result = try wasm_codegen.generate(
1278 &wasm.base,
1279 pt,
1280 zcu.navSrcLoc(nav_index),
1281 func_index,
1282 air,
1283 liveness,
1284 &wasm.string_bytes,
1285 .none,
1286 );
8681287
869/// For a given symbol index, find its corresponding import.1288 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
870/// Asserts import exists.1289 const relocs_len: u32 = @intCast(wasm.relocations.items.len - relocs_start);
871fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import {1290 wasm.string_bytes_lock.unlock();
872 const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?;1291
873 return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]);1292 const code: Nav.Code = switch (result) {
874}1293 .ok => .{
1294 .off = code_start,
1295 .len = code_len,
1296 },
1297 .fail => |em| {
1298 try pt.zcu.failed_codegen.put(gpa, nav_index, em);
1299 return;
1300 },
1301 };
8751302
876/// Returns the object element pointer, or null if it is the ZigObject.1303 const gop = try wasm.navs.getOrPut(gpa, nav_index);
877fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object {1304 if (gop.found_existing) {
878 if (object_id == .zig_object) return null;1305 @panic("TODO reuse these resources");
879 return &wasm.objects.items[@intFromEnum(object_id)];1306 } else {
1307 _ = wasm.imports.swapRemove(nav_index);
1308 }
1309 gop.value_ptr.* = .{
1310 .code = code,
1311 .relocs = .{
1312 .off = relocs_start,
1313 .len = relocs_len,
1314 },
1315 };
880}1316}
8811317
882fn resolveSymbolsInObject(wasm: *Wasm, object_id: ObjectId) !void {1318// Generate code for the "Nav", storing it in memory to be later written to
1319// the file on flush().
1320pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1321 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1322 @panic("Attempted to compile for object format that was disabled by build configuration");
1323 }
1324 if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav_index);
1325 const zcu = pt.zcu;
1326 const ip = &zcu.intern_pool;
1327 const nav = ip.getNav(nav_index);
883 const gpa = wasm.base.comp.gpa;1328 const gpa = wasm.base.comp.gpa;
884 const diags = &wasm.base.comp.link_diags;
885 const obj_path = objectPath(wasm, object_id);
886 log.debug("Resolving symbols in object: '{'}'", .{obj_path});
887 const symbols = objectSymbols(wasm, object_id);
888
889 for (symbols, 0..) |symbol, i| {
890 const sym_index: Symbol.Index = @enumFromInt(i);
891 const location: SymbolLoc = .{
892 .file = object_id.toOptional(),
893 .index = sym_index,
894 };
895 if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue;
896
897 if (symbol.isLocal()) {
898 if (symbol.isUndefined()) {
899 diags.addParseError(obj_path, "local symbol '{s}' references import", .{
900 wasm.stringSlice(symbol.name),
901 });
902 }
903 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
904 continue;
905 }
9061329
907 const maybe_existing = try wasm.globals.getOrPut(gpa, symbol.name);1330 const nav_val = zcu.navValue(nav_index);
908 if (!maybe_existing.found_existing) {1331 const is_extern, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
909 maybe_existing.value_ptr.* = location;1332 .variable => |variable| .{ false, Value.fromInterned(variable.init) },
910 try wasm.resolved_symbols.putNoClobber(gpa, location, {});1333 .func => unreachable,
9111334 .@"extern" => b: {
912 if (symbol.isUndefined()) {1335 assert(!ip.isFunctionType(nav.typeOf(ip)));
913 try wasm.undefs.putNoClobber(gpa, symbol.name, location);1336 break :b .{ true, nav_val };
914 }1337 },
915 continue;1338 else => .{ false, nav_val },
916 }1339 };
9171340
918 const existing_loc = maybe_existing.value_ptr.*;1341 if (!nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
919 const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc);1342 _ = wasm.imports.swapRemove(nav_index);
920 const existing_file_path: Path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else .{1343 _ = wasm.navs.swapRemove(nav_index); // TODO reclaim resources
921 .root_dir = std.Build.Cache.Directory.cwd(),1344 return;
922 .sub_path = wasm.name,1345 }
923 };
9241346
925 if (!existing_sym.isUndefined()) outer: {1347 if (is_extern) {
926 if (!symbol.isUndefined()) inner: {1348 try wasm.imports.put(nav_index, {});
927 if (symbol.isWeak()) {1349 _ = wasm.navs.swapRemove(nav_index); // TODO reclaim resources
928 break :inner; // ignore the new symbol (discard it)1350 return;
929 }1351 }
930 if (existing_sym.isWeak()) {
931 break :outer; // existing is weak, while new one isn't. Replace it.
932 }
933 // both are defined and weak, we have a symbol collision.
934 var err = try diags.addErrorWithNotes(2);
935 try err.addMsg("symbol '{s}' defined multiple times", .{wasm.stringSlice(symbol.name)});
936 try err.addNote("first definition in '{'}'", .{existing_file_path});
937 try err.addNote("next definition in '{'}'", .{obj_path});
938 }
9391352
940 try wasm.discarded.put(gpa, location, existing_loc);1353 const code_start: u32 = @intCast(wasm.string_bytes.items.len);
941 continue; // Do not overwrite defined symbols with undefined symbols1354 const relocs_start: u32 = @intCast(wasm.relocations.items.len);
942 }1355 wasm.string_bytes_lock.lock();
9431356
944 if (symbol.tag != existing_sym.tag) {1357 const res = try codegen.generateSymbol(
945 var err = try diags.addErrorWithNotes(2);1358 &wasm.base,
946 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{1359 pt,
947 wasm.stringSlice(symbol.name), @tagName(symbol.tag), @tagName(existing_sym.tag),1360 zcu.navSrcLoc(nav_index),
948 });1361 nav_init,
949 try err.addNote("first definition in '{'}'", .{existing_file_path});1362 &wasm.string_bytes,
950 try err.addNote("next definition in '{'}'", .{obj_path});1363 .none,
951 }1364 );
9521365
953 if (existing_sym.isUndefined() and symbol.isUndefined()) {1366 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
954 // only verify module/import name for function symbols1367 const relocs_len: u32 = @intCast(wasm.relocations.items.len - relocs_start);
955 if (symbol.tag == .function) {1368 wasm.string_bytes_lock.unlock();
956 const existing_name = if (existing_loc.file.unwrap()) |existing_obj_id|
957 objectImport(wasm, existing_obj_id, existing_loc.index).module_name
958 else
959 wasm.imports.get(existing_loc).?.module_name;
960
961 const module_name = objectImport(wasm, object_id, sym_index).module_name;
962 if (existing_name != module_name) {
963 var err = try diags.addErrorWithNotes(2);
964 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
965 wasm.stringSlice(symbol.name),
966 wasm.stringSlice(existing_name),
967 wasm.stringSlice(module_name),
968 });
969 try err.addNote("first definition in '{'}'", .{existing_file_path});
970 try err.addNote("next definition in '{'}'", .{obj_path});
971 }
972 }
9731369
974 // both undefined so skip overwriting existing symbol and discard the new symbol1370 const code: Nav.Code = switch (res) {
975 try wasm.discarded.put(gpa, location, existing_loc);1371 .ok => .{
976 continue;1372 .off = code_start,
977 }1373 .len = code_len,
1374 },
1375 .fail => |em| {
1376 try zcu.failed_codegen.put(gpa, nav_index, em);
1377 return;
1378 },
1379 };
9781380
979 if (existing_sym.tag == .global) {1381 const gop = try wasm.navs.getOrPut(gpa, nav_index);
980 const existing_ty = wasm.getGlobalType(existing_loc);1382 if (gop.found_existing) {
981 const new_ty = wasm.getGlobalType(location);1383 @panic("TODO reuse these resources");
982 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {1384 } else {
983 var err = try diags.addErrorWithNotes(2);1385 _ = wasm.imports.swapRemove(nav_index);
984 try err.addMsg("symbol '{s}' mismatching global types", .{wasm.stringSlice(symbol.name)});1386 }
985 try err.addNote("first definition in '{'}'", .{existing_file_path});1387 gop.value_ptr.* = .{
986 try err.addNote("next definition in '{'}'", .{obj_path});1388 .code = code,
987 }1389 .relocs = .{
988 }1390 .off = relocs_start,
1391 .len = relocs_len,
1392 },
1393 };
1394}
9891395
990 if (existing_sym.tag == .function) {1396pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
991 const existing_ty = wasm.getFunctionSignature(existing_loc);1397 if (wasm.dwarf) |*dw| {
992 const new_ty = wasm.getFunctionSignature(location);1398 try dw.updateLineNumber(pt.zcu, ti_id);
993 if (!existing_ty.eql(new_ty)) {1399 }
994 var err = try diags.addErrorWithNotes(3);1400}
995 try err.addMsg("symbol '{s}' mismatching function signatures.", .{wasm.stringSlice(symbol.name)});
996 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
997 try err.addNote("first definition in '{'}'", .{existing_file_path});
998 try err.addNote("next definition in '{'}'", .{obj_path});
999 }
1000 }
10011401
1002 // when both symbols are weak, we skip overwriting unless the existing1402pub fn deleteExport(
1003 // symbol is weak and the new one isn't, in which case we *do* overwrite it.1403 wasm: *Wasm,
1004 if (existing_sym.isWeak() and symbol.isWeak()) blk: {1404 exported: Zcu.Exported,
1005 if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk;1405 name: InternPool.NullTerminatedString,
1006 try wasm.discarded.put(gpa, location, existing_loc);1406) void {
1007 continue;1407 if (wasm.llvm_object != null) return;
1008 }
10091408
1010 // simply overwrite with the new symbol1409 const zcu = wasm.base.comp.zcu.?;
1011 log.debug("Overwriting symbol '{s}'", .{wasm.stringSlice(symbol.name)});1410 const ip = &zcu.intern_pool;
1012 log.debug(" old definition in '{'}'", .{existing_file_path});1411 const export_name = wasm.getExistingString(name.toSlice(ip)).?;
1013 log.debug(" new definition in '{'}'", .{obj_path});1412 switch (exported) {
1014 try wasm.discarded.putNoClobber(gpa, existing_loc, location);1413 .nav => |nav_index| assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name })),
1015 maybe_existing.value_ptr.* = location;1414 .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })),
1016 try wasm.globals.put(gpa, symbol.name, location);
1017 try wasm.resolved_symbols.put(gpa, location, {});
1018 assert(wasm.resolved_symbols.swapRemove(existing_loc));
1019 if (existing_sym.isUndefined()) {
1020 _ = wasm.undefs.swapRemove(symbol.name);
1021 }
1022 }1415 }
1416 wasm.any_exports_updated = true;
1023}1417}
10241418
1025fn resolveSymbolsInArchives(wasm: *Wasm) !void {1419pub fn updateExports(
1026 if (wasm.lazy_archives.items.len == 0) return;1420 wasm: *Wasm,
1027 const gpa = wasm.base.comp.gpa;1421 pt: Zcu.PerThread,
1028 const diags = &wasm.base.comp.link_diags;1422 exported: Zcu.Exported,
10291423 export_indices: []const u32,
1030 log.debug("Resolving symbols in lazy_archives", .{});1424) !void {
1031 var index: u32 = 0;1425 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1032 undef_loop: while (index < wasm.undefs.count()) {1426 @panic("Attempted to compile for object format that was disabled by build configuration");
1033 const sym_name_index = wasm.undefs.keys()[index];1427 }
10341428 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
1035 for (wasm.lazy_archives.items) |lazy_archive| {
1036 const sym_name = wasm.stringSlice(sym_name_index);
1037 log.debug("Detected symbol '{s}' in archive '{'}', parsing objects..", .{
1038 sym_name, lazy_archive.path,
1039 });
1040 const offset = lazy_archive.archive.toc.get(sym_name) orelse continue; // symbol does not exist in this archive
1041
1042 // Symbol is found in unparsed object file within current archive.
1043 // Parse object and and resolve symbols again before we check remaining
1044 // undefined symbols.
1045 const file_contents = lazy_archive.file_contents[offset.items[0]..];
1046 const object = lazy_archive.archive.parseObject(wasm, file_contents, lazy_archive.path) catch |err| {
1047 // TODO this fails to include information to identify which object failed
1048 return diags.failParse(lazy_archive.path, "failed to parse object in archive: {s}", .{@errorName(err)});
1049 };
1050 try wasm.objects.append(gpa, object);
1051 try wasm.resolveSymbolsInObject(@enumFromInt(wasm.objects.items.len - 1));
10521429
1053 // continue loop for any remaining undefined symbols that still exist1430 const zcu = pt.zcu;
1054 // after resolving last object file1431 const gpa = zcu.gpa;
1055 continue :undef_loop;1432 const ip = &zcu.intern_pool;
1433 for (export_indices) |export_idx| {
1434 const exp = export_idx.ptr(zcu);
1435 const name = try wasm.internString(exp.opts.name.toSlice(ip));
1436 switch (exported) {
1437 .nav => |nav_index| wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx),
1438 .uav => |uav_index| wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),
1056 }1439 }
1057 index += 1;
1058 }1440 }
1441 wasm.any_exports_updated = true;
1059}1442}
10601443
1061/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.1444pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
1062fn writeI32Const(writer: anytype, val: u32) !void {
1063 try writer.writeByte(std.wasm.opcode(.i32_const));
1064 try leb.writeIleb128(writer, @as(i32, @bitCast(val)));
1065}
1066
1067fn setupInitMemoryFunction(wasm: *Wasm) !void {
1068 const comp = wasm.base.comp;1445 const comp = wasm.base.comp;
1069 const gpa = comp.gpa;1446 const gpa = comp.gpa;
1070 const shared_memory = comp.config.shared_memory;
1071 const import_memory = comp.config.import_memory;
1072
1073 // Passive segments are used to avoid memory being reinitialized on each
1074 // thread's instantiation. These passive segments are initialized and
1075 // dropped in __wasm_init_memory, which is registered as the start function
1076 // We also initialize bss segments (using memory.fill) as part of this
1077 // function.
1078 if (!wasm.hasPassiveInitializationSegments()) {
1079 return;
1080 }
1081 const sym_loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory, .function);
1082 wasm.symbolLocSymbol(sym_loc).mark();
1083
1084 const flag_address: u32 = if (shared_memory) address: {
1085 // when we have passive initialization segments and shared memory
1086 // `setupMemory` will create this symbol and set its virtual address.
1087 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory_flag).?;
1088 break :address wasm.symbolLocSymbol(loc).virtual_address;
1089 } else 0;
1090
1091 var function_body = std.ArrayList(u8).init(gpa);
1092 defer function_body.deinit();
1093 const writer = function_body.writer();
1094
1095 // we have 0 locals
1096 try leb.writeUleb128(writer, @as(u32, 0));
1097
1098 if (shared_memory) {
1099 // destination blocks
1100 // based on values we jump to corresponding label
1101 try writer.writeByte(std.wasm.opcode(.block)); // $drop
1102 try writer.writeByte(std.wasm.block_empty); // block type
1103
1104 try writer.writeByte(std.wasm.opcode(.block)); // $wait
1105 try writer.writeByte(std.wasm.block_empty); // block type
1106
1107 try writer.writeByte(std.wasm.opcode(.block)); // $init
1108 try writer.writeByte(std.wasm.block_empty); // block type
1109
1110 // atomically check
1111 try writeI32Const(writer, flag_address);
1112 try writeI32Const(writer, 0);
1113 try writeI32Const(writer, 1);
1114 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1115 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
1116 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1117 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1118
1119 // based on the value from the atomic check, jump to the label.
1120 try writer.writeByte(std.wasm.opcode(.br_table));
1121 try leb.writeUleb128(writer, @as(u32, 2)); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).
1122 try leb.writeUleb128(writer, @as(u32, 0)); // $init
1123 try leb.writeUleb128(writer, @as(u32, 1)); // $wait
1124 try leb.writeUleb128(writer, @as(u32, 2)); // $drop
1125 try writer.writeByte(std.wasm.opcode(.end));
1126 }
11271447
1128 for (wasm.data_segments.keys(), wasm.data_segments.values(), 0..) |key, value, segment_index_usize| {1448 if (comp.verbose_link) {
1129 const segment_index: u32 = @intCast(segment_index_usize);1449 comp.mutex.lock(); // protect comp.arena
1130 const segment = wasm.segmentPtr(value);1450 defer comp.mutex.unlock();
1131 if (segment.needsPassiveInitialization(import_memory, key)) {
1132 // For passive BSS segments we can simple issue a memory.fill(0).
1133 // For non-BSS segments we do a memory.init. Both these
1134 // instructions take as their first argument the destination
1135 // address.
1136 try writeI32Const(writer, segment.offset);
1137
1138 if (shared_memory and std.mem.eql(u8, key, ".tdata")) {
1139 // When we initialize the TLS segment we also set the `__tls_base`
1140 // global. This allows the runtime to use this static copy of the
1141 // TLS data for the first/main thread.
1142 try writeI32Const(writer, segment.offset);
1143 try writer.writeByte(std.wasm.opcode(.global_set));
1144 const loc = wasm.globals.get(wasm.preloaded_strings.__tls_base).?;
1145 try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index);
1146 }
11471451
1148 try writeI32Const(writer, 0);1452 const argv = &wasm.dump_argv_list;
1149 try writeI32Const(writer, segment.size);1453 switch (input) {
1150 try writer.writeByte(std.wasm.opcode(.misc_prefix));1454 .res => unreachable,
1151 if (std.mem.eql(u8, key, ".bss")) {1455 .dso_exact => unreachable,
1152 // fill bss segment with zeroes1456 .dso => unreachable,
1153 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_fill));1457 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
1154 } else {
1155 // initialize the segment
1156 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init));
1157 try leb.writeUleb128(writer, segment_index);
1158 }
1159 try writer.writeByte(0); // memory index immediate
1160 }1458 }
1161 }1459 }
11621460
1163 if (shared_memory) {1461 switch (input) {
1164 // we set the init memory flag to value '2'1462 .res => unreachable,
1165 try writeI32Const(writer, flag_address);1463 .dso_exact => unreachable,
1166 try writeI32Const(writer, 2);1464 .dso => unreachable,
1167 try writer.writeByte(std.wasm.opcode(.atomics_prefix));1465 .object => |obj| try parseObject(wasm, obj),
1168 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));1466 .archive => |obj| try parseArchive(wasm, obj),
1169 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1170 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1171
1172 // notify any waiters for segment initialization completion
1173 try writeI32Const(writer, flag_address);
1174 try writer.writeByte(std.wasm.opcode(.i32_const));
1175 try leb.writeIleb128(writer, @as(i32, -1)); // number of waiters
1176 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1177 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.memory_atomic_notify));
1178 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1179 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1180 try writer.writeByte(std.wasm.opcode(.drop));
1181
1182 // branch and drop segments
1183 try writer.writeByte(std.wasm.opcode(.br));
1184 try leb.writeUleb128(writer, @as(u32, 1));
1185
1186 // wait for thread to initialize memory segments
1187 try writer.writeByte(std.wasm.opcode(.end)); // end $wait
1188 try writeI32Const(writer, flag_address);
1189 try writeI32Const(writer, 1); // expected flag value
1190 try writer.writeByte(std.wasm.opcode(.i64_const));
1191 try leb.writeIleb128(writer, @as(i64, -1)); // timeout
1192 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
1193 try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
1194 try leb.writeUleb128(writer, @as(u32, 2)); // alignment
1195 try leb.writeUleb128(writer, @as(u32, 0)); // offset
1196 try writer.writeByte(std.wasm.opcode(.drop));
1197
1198 try writer.writeByte(std.wasm.opcode(.end)); // end $drop
1199 }1467 }
1468}
12001469
1201 for (wasm.data_segments.keys(), wasm.data_segments.values(), 0..) |name, value, segment_index_usize| {1470pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
1202 const segment_index: u32 = @intCast(segment_index_usize);1471 const comp = wasm.base.comp;
1203 const segment = wasm.segmentPtr(value);1472 const use_lld = build_options.have_llvm and comp.config.use_lld;
1204 if (segment.needsPassiveInitialization(import_memory, name) and
1205 !std.mem.eql(u8, name, ".bss"))
1206 {
1207 // The TLS region should not be dropped since its is needed
1208 // during the initialization of each thread (__wasm_init_tls).
1209 if (shared_memory and std.mem.eql(u8, name, ".tdata")) {
1210 continue;
1211 }
12121473
1213 try writer.writeByte(std.wasm.opcode(.misc_prefix));1474 if (use_lld) {
1214 try leb.writeUleb128(writer, std.wasm.miscOpcode(.data_drop));1475 return wasm.linkWithLLD(arena, tid, prog_node);
1215 try leb.writeUleb128(writer, segment_index);
1216 }
1217 }1476 }
1477 return wasm.flushModule(arena, tid, prog_node);
1478}
12181479
1219 // End of the function body1480pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void {
1220 try writer.writeByte(std.wasm.opcode(.end));1481 const tracy = trace(@src());
12211482 defer tracy.end();
1222 try wasm.createSyntheticFunction(
1223 wasm.preloaded_strings.__wasm_init_memory,
1224 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1225 &function_body,
1226 );
1227}
1228
1229/// Constructs a synthetic function that performs runtime relocations for
1230/// TLS symbols. This function is called by `__wasm_init_tls`.
1231fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
1232 const comp = wasm.base.comp;
1233 const gpa = comp.gpa;
1234 const shared_memory = comp.config.shared_memory;
1235
1236 // When we have TLS GOT entries and shared memory is enabled,
1237 // we must perform runtime relocations or else we don't create the function.
1238 if (!shared_memory or !wasm.requiresTLSReloc()) {
1239 return;
1240 }
1241
1242 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_apply_global_tls_relocs, .function);
1243 wasm.symbolLocSymbol(loc).mark();
1244 var function_body = std.ArrayList(u8).init(gpa);
1245 defer function_body.deinit();
1246 const writer = function_body.writer();
1247
1248 // locals (we have none)
1249 try writer.writeByte(0);
1250 for (wasm.got_symbols.items, 0..) |got_loc, got_index| {
1251 const sym: *Symbol = wasm.symbolLocSymbol(got_loc);
1252 if (!sym.isTLS()) continue; // only relocate TLS symbols
1253 if (sym.tag == .data and sym.isDefined()) {
1254 // get __tls_base
1255 try writer.writeByte(std.wasm.opcode(.global_get));
1256 try leb.writeUleb128(writer, wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__tls_base).?).index);
1257
1258 // add the virtual address of the symbol
1259 try writer.writeByte(std.wasm.opcode(.i32_const));
1260 try leb.writeUleb128(writer, sym.virtual_address);
1261 } else if (sym.tag == .function) {
1262 @panic("TODO: relocate GOT entry of function");
1263 } else continue;
1264
1265 try writer.writeByte(std.wasm.opcode(.i32_add));
1266 try writer.writeByte(std.wasm.opcode(.global_set));
1267 try leb.writeUleb128(writer, wasm.imported_globals_count + @as(u32, @intCast(wasm.wasm_globals.items.len + got_index)));
1268 }
1269 try writer.writeByte(std.wasm.opcode(.end));
12701483
1271 try wasm.createSyntheticFunction(1484 const sub_prog_node = prog_node.start("Wasm Prelink", 0);
1272 wasm.preloaded_strings.__wasm_apply_global_tls_relocs,1485 defer sub_prog_node.end();
1273 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1274 &function_body,
1275 );
1276}
12771486
1278fn validateFeatures(
1279 wasm: *const Wasm,
1280 to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool,
1281 emit_features_count: *u32,
1282) !void {
1283 const comp = wasm.base.comp;1487 const comp = wasm.base.comp;
1284 const diags = &wasm.base.comp.link_diags;1488 const gpa = comp.gpa;
1285 const target = comp.root_mod.resolved_target.result;1489 const rdynamic = comp.config.rdynamic;
1286 const shared_memory = comp.config.shared_memory;
1287 const cpu_features = target.cpu.features;
1288 const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.
1289 const known_features_count = @typeInfo(Feature.Tag).@"enum".fields.len;
1290
1291 var allowed = [_]bool{false} ** known_features_count;
1292 var used = [_]u17{0} ** known_features_count;
1293 var disallowed = [_]u17{0} ** known_features_count;
1294 var required = [_]u17{0} ** known_features_count;
1295
1296 // when false, we fail linking. We only verify this after a loop to catch all invalid features.
1297 var valid_feature_set = true;
1298 // will be set to true when there's any TLS segment found in any of the object files
1299 var has_tls = false;
1300
1301 // When the user has given an explicit list of features to enable,
1302 // we extract them and insert each into the 'allowed' list.
1303 if (!infer) {
1304 inline for (@typeInfo(std.Target.wasm.Feature).@"enum".fields) |feature_field| {
1305 if (cpu_features.isEnabled(feature_field.value)) {
1306 allowed[feature_field.value] = true;
1307 emit_features_count.* += 1;
1308 }
1309 }
1310 }
1311
1312 // extract all the used, disallowed and required features from each
1313 // linked object file so we can test them.
1314 for (wasm.objects.items, 0..) |*object, file_index| {
1315 for (object.features) |feature| {
1316 const value = (@as(u16, @intCast(file_index)) << 1) | 1;
1317 switch (feature.prefix) {
1318 .used => {
1319 used[@intFromEnum(feature.tag)] = value;
1320 },
1321 .disallowed => {
1322 disallowed[@intFromEnum(feature.tag)] = value;
1323 },
1324 .required => {
1325 required[@intFromEnum(feature.tag)] = value;
1326 used[@intFromEnum(feature.tag)] = value;
1327 },
1328 }
1329 }
1330
1331 for (object.segment_info) |segment| {
1332 if (segment.isTLS()) {
1333 has_tls = true;
1334 }
1335 }
1336 }
1337
1338 // when we infer the features, we allow each feature found in the 'used' set
1339 // and insert it into the 'allowed' set. When features are not inferred,
1340 // we validate that a used feature is allowed.
1341 for (used, 0..) |used_set, used_index| {
1342 const is_enabled = @as(u1, @truncate(used_set)) != 0;
1343 if (infer) {
1344 allowed[used_index] = is_enabled;
1345 emit_features_count.* += @intFromBool(is_enabled);
1346 } else if (is_enabled and !allowed[used_index]) {
1347 diags.addParseError(
1348 wasm.objects.items[used_set >> 1].path,
1349 "feature '{}' not allowed, but used by linked object",
1350 .{@as(Feature.Tag, @enumFromInt(used_index))},
1351 );
1352 valid_feature_set = false;
1353 }
1354 }
1355
1356 if (!valid_feature_set) {
1357 return error.FlushFailure;
1358 }
1359
1360 if (shared_memory) {
1361 const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)];
1362 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1363 diags.addParseError(
1364 wasm.objects.items[disallowed_feature >> 1].path,
1365 "shared-memory is disallowed because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1366 .{},
1367 );
1368 valid_feature_set = false;
1369 }
13701490
1371 for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {1491 {
1372 if (!allowed[@intFromEnum(feature)]) {1492 var missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty;
1373 var err = try diags.addErrorWithNotes(0);1493 defer missing_exports.deinit(gpa);
1374 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});1494 for (wasm.export_symbol_names) |exp_name| {
1495 const exp_name_interned = try wasm.internString(exp_name);
1496 if (wasm.object_function_imports.getPtr(exp_name_interned)) |import| {
1497 if (import.resolution != .unresolved) {
1498 import.flags.exported = true;
1499 continue;
1500 }
1375 }1501 }
1376 }1502 if (wasm.object_global_imports.getPtr(exp_name_interned)) |import| {
1377 }1503 if (import.resolution != .unresolved) {
13781504 import.flags.exported = true;
1379 if (has_tls) {1505 continue;
1380 for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| {1506 }
1381 if (!allowed[@intFromEnum(feature)]) {
1382 var err = try diags.addErrorWithNotes(0);
1383 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
1384 }1507 }
1508 try wasm.missing_exports.put(exp_name_interned, {});
1385 }1509 }
1510 wasm.missing_exports_init = try gpa.dupe(String, wasm.missing_exports.keys());
1386 }1511 }
1387 // For each linked object, validate the required and disallowed features
1388 for (wasm.objects.items) |*object| {
1389 var object_used_features = [_]bool{false} ** known_features_count;
1390 for (object.features) |feature| {
1391 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
1392 // from here a feature is always used
1393 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
1394 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1395 var err = try diags.addErrorWithNotes(2);
1396 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1397 try err.addNote("disallowed by '{'}'", .{wasm.objects.items[disallowed_feature >> 1].path});
1398 try err.addNote("used in '{'}'", .{object.path});
1399 valid_feature_set = false;
1400 }
1401
1402 object_used_features[@intFromEnum(feature.tag)] = true;
1403 }
14041512
1405 // validate the linked object file has each required feature1513 if (wasm.entry_name.unwrap()) |entry_name| {
1406 for (required, 0..) |required_feature, feature_index| {1514 if (wasm.object_function_imports.getPtr(entry_name)) |import| {
1407 const is_required = @as(u1, @truncate(required_feature)) != 0;1515 if (import.resolution != .unresolved) {
1408 if (is_required and !object_used_features[feature_index]) {1516 import.flags.exported = true;
1409 var err = try diags.addErrorWithNotes(2);1517 wasm.entry_resolution = import.resolution;
1410 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))});
1411 try err.addNote("required by '{'}'", .{wasm.objects.items[required_feature >> 1].path});
1412 try err.addNote("missing in '{'}'", .{object.path});
1413 valid_feature_set = false;
1414 }1518 }
1415 }1519 }
1416 }1520 }
14171521
1418 if (!valid_feature_set) {1522 // These loops do both recursive marking of alive symbols well as checking for undefined symbols.
1419 return error.FlushFailure;1523 // At the end, output functions and globals will be populated.
1420 }1524 for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {
14211525 if (import.flags.isIncluded(rdynamic)) {
1422 to_emit.* = allowed;1526 try markFunction(wasm, name, import, @enumFromInt(i));
1423}1527 continue;
1424
1425/// Creates synthetic linker-symbols, but only if they are being referenced from
1426/// any object file. For instance, the `__heap_base` symbol will only be created,
1427/// if one or multiple undefined references exist. When none exist, the symbol will
1428/// not be created, ensuring we don't unnecessarily emit unreferenced symbols.
1429fn resolveLazySymbols(wasm: *Wasm) !void {
1430 const comp = wasm.base.comp;
1431 const gpa = comp.gpa;
1432 const shared_memory = comp.config.shared_memory;
1433
1434 if (wasm.getExistingString("__heap_base")) |name_offset| {
1435 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
1436 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
1437 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
1438 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
1439 }1528 }
1440 }1529 }
1530 wasm.functions_len = @intCast(wasm.functions.items.len);
1531 wasm.function_imports_init = try gpa.dupe(FunctionImportId, wasm.functions.keys());
1532 wasm.function_exports_len = @intCast(wasm.function_exports.items.len);
14411533
1442 if (wasm.getExistingString("__heap_end")) |name_offset| {1534 for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
1443 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {1535 if (import.flags.isIncluded(rdynamic)) {
1444 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);1536 try markGlobal(wasm, name, import, @enumFromInt(i));
1445 try wasm.discarded.putNoClobber(gpa, kv.value, loc);1537 continue;
1446 _ = wasm.resolved_symbols.swapRemove(loc);
1447 }1538 }
1448 }1539 }
1540 wasm.globals_len = @intCast(wasm.globals.items.len);
1541 wasm.global_imports_init = try gpa.dupe(GlobalImportId, wasm.globals.keys());
1542 wasm.global_exports_len = @intCast(wasm.global_exports.items.len);
14491543
1450 if (!shared_memory) {1544 for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| {
1451 if (wasm.getExistingString("__tls_base")) |name_offset| {1545 if (import.flags.isIncluded(rdynamic)) {
1452 if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {1546 try markTable(wasm, name, import, @enumFromInt(i));
1453 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global);1547 continue;
1454 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
1455 _ = wasm.resolved_symbols.swapRemove(kv.value);
1456 const symbol = wasm.symbolLocSymbol(loc);
1457 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
1458 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
1459 try wasm.wasm_globals.append(gpa, .{
1460 .global_type = .{ .valtype = .i32, .mutable = true },
1461 .init = .{ .i32_const = undefined },
1462 });
1463 }
1464 }1548 }
1465 }1549 }
1550 wasm.tables_len = @intCast(wasm.tables.items.len);
1466}1551}
14671552
1468pub fn findGlobalSymbol(wasm: *const Wasm, name: []const u8) ?SymbolLoc {1553/// Recursively mark alive everything referenced by the function.
1469 const name_index = wasm.getExistingString(name) orelse return null;1554fn markFunction(
1470 return wasm.globals.get(name_index);1555 wasm: *Wasm,
1471}1556 name: String,
1557 import: *FunctionImport,
1558 func_index: ObjectFunctionImportIndex,
1559) error{OutOfMemory}!void {
1560 if (import.flags.alive) return;
1561 import.flags.alive = true;
14721562
1473fn checkUndefinedSymbols(wasm: *const Wasm) !void {
1474 const comp = wasm.base.comp;1563 const comp = wasm.base.comp;
1475 const diags = &wasm.base.comp.link_diags;1564 const gpa = comp.gpa;
1476 if (comp.config.output_mode == .Obj) return;1565 const rdynamic = comp.config.rdynamic;
1477 if (wasm.import_symbols) return;1566 const is_obj = comp.config.output_mode == .Obj;
1478
1479 var found_undefined_symbols = false;
1480 for (wasm.undefs.values()) |undef| {
1481 const symbol = wasm.symbolLocSymbol(undef);
1482 if (symbol.tag == .data) {
1483 found_undefined_symbols = true;
1484 const symbol_name = wasm.symbolLocName(undef);
1485 switch (undef.file) {
1486 .zig_object => {
1487 // TODO: instead of saying the zig compilation unit, attach an actual source location
1488 // to this diagnostic
1489 diags.addError("unresolved symbol in Zig compilation unit: {s}", .{symbol_name});
1490 },
1491 .none => {
1492 diags.addError("internal linker bug: unresolved synthetic symbol: {s}", .{symbol_name});
1493 },
1494 _ => {
1495 const path = wasm.objects.items[@intFromEnum(undef.file)].path;
1496 diags.addParseError(path, "unresolved symbol: {s}", .{symbol_name});
1497 },
1498 }
1499 }
1500 }
1501 if (found_undefined_symbols) {
1502 return error.LinkFailure;
1503 }
1504}
1505
1506pub fn deinit(wasm: *Wasm) void {
1507 const gpa = wasm.base.comp.gpa;
1508 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
1509
1510 for (wasm.func_types.items) |*func_type| {
1511 func_type.deinit(gpa);
1512 }
1513 for (wasm.segment_info.values()) |segment_info| {
1514 gpa.free(segment_info.name);
1515 }
1516 if (wasm.zig_object) |zig_obj| {
1517 zig_obj.deinit(wasm);
1518 }
1519 for (wasm.objects.items) |*object| {
1520 object.deinit(gpa);
1521 }
1522
1523 for (wasm.lazy_archives.items) |*lazy_archive| lazy_archive.deinit(gpa);
1524 wasm.lazy_archives.deinit(gpa);
1525
1526 if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls)) |loc| {
1527 const atom = wasm.symbol_atom.get(loc).?;
1528 wasm.getAtomPtr(atom).deinit(gpa);
1529 }
1530
1531 wasm.synthetic_symbols.deinit(gpa);
1532 wasm.globals.deinit(gpa);
1533 wasm.resolved_symbols.deinit(gpa);
1534 wasm.undefs.deinit(gpa);
1535 wasm.discarded.deinit(gpa);
1536 wasm.symbol_atom.deinit(gpa);
1537 wasm.atoms.deinit(gpa);
1538 wasm.managed_atoms.deinit(gpa);
1539 wasm.segments.deinit(gpa);
1540 wasm.data_segments.deinit(gpa);
1541 wasm.segment_info.deinit(gpa);
1542 wasm.objects.deinit(gpa);
1543
1544 // free output sections
1545 wasm.imports.deinit(gpa);
1546 wasm.func_types.deinit(gpa);
1547 wasm.functions.deinit(gpa);
1548 wasm.wasm_globals.deinit(gpa);
1549 wasm.function_table.deinit(gpa);
1550 wasm.tables.deinit(gpa);
1551 wasm.init_funcs.deinit(gpa);
1552 wasm.exports.deinit(gpa);
1553
1554 wasm.string_bytes.deinit(gpa);
1555 wasm.string_table.deinit(gpa);
1556 wasm.dump_argv_list.deinit(gpa);
1557}
1558
1559pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
1560 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1561 @panic("Attempted to compile for object format that was disabled by build configuration");
1562 }
1563 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
1564 try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness);
1565}
1566
1567// Generate code for the "Nav", storing it in memory to be later written to
1568// the file on flush().
1569pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {
1570 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1571 @panic("Attempted to compile for object format that was disabled by build configuration");
1572 }
1573 if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav);
1574 try wasm.zig_object.?.updateNav(wasm, pt, nav);
1575}
15761567
1577pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {1568 try wasm.functions.ensureUnusedCapacity(gpa, 1);
1578 if (wasm.llvm_object) |_| return;1569
1579 try wasm.zig_object.?.updateLineNumber(pt, ti_id);1570 if (import.resolution == .unresolved) {
1580}1571 if (name == wasm.preloaded_strings.__wasm_init_memory) {
1572 import.resolution = .__wasm_init_memory;
1573 wasm.functions.putAssumeCapacity(.__wasm_init_memory, {});
1574 } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) {
1575 import.resolution = .__wasm_apply_global_tls_relocs;
1576 wasm.functions.putAssumeCapacity(.__wasm_apply_global_tls_relocs, {});
1577 } else if (name == wasm.preloaded_strings.__wasm_call_ctors) {
1578 import.resolution = .__wasm_call_ctors;
1579 wasm.functions.putAssumeCapacity(.__wasm_call_ctors, {});
1580 } else if (name == wasm.preloaded_strings.__wasm_init_tls) {
1581 import.resolution = .__wasm_init_tls;
1582 wasm.functions.putAssumeCapacity(.__wasm_init_tls, {});
1583 } else {
1584 try wasm.function_imports.put(gpa, .fromObject(func_index), {});
1585 }
1586 } else {
1587 const gop = wasm.functions.getOrPutAssumeCapacity(import.resolution);
15811588
1582/// From a given symbol location, returns its `wasm.GlobalType`.1589 if (!is_obj and import.flags.isExported(rdynamic))
1583/// Asserts the Symbol represents a global.1590 try wasm.function_exports.append(gpa, @intCast(gop.index));
1584fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
1585 const symbol = wasm.symbolLocSymbol(loc);
1586 assert(symbol.tag == .global);
1587 const is_undefined = symbol.isUndefined();
1588 switch (loc.file) {
1589 .zig_object => {
1590 const zo = wasm.zig_object.?;
1591 return if (is_undefined)
1592 zo.imports.get(loc.index).?.kind.global
1593 else
1594 zo.globals.items[symbol.index - zo.imported_globals_count].global_type;
1595 },
1596 .none => {
1597 return if (is_undefined)
1598 wasm.imports.get(loc).?.kind.global
1599 else
1600 wasm.wasm_globals.items[symbol.index].global_type;
1601 },
1602 _ => {
1603 const obj = &wasm.objects.items[@intFromEnum(loc.file)];
1604 return if (is_undefined)
1605 obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.global
1606 else
1607 obj.globals[symbol.index - obj.imported_globals_count].global_type;
1608 },
1609 }
1610}
16111591
1612/// From a given symbol location, returns its `wasm.Type`.1592 for (wasm.functionResolutionRelocSlice(import.resolution)) |reloc|
1613/// Asserts the Symbol represents a function.1593 try wasm.markReloc(reloc);
1614fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
1615 const symbol = wasm.symbolLocSymbol(loc);
1616 assert(symbol.tag == .function);
1617 const is_undefined = symbol.isUndefined();
1618 switch (loc.file) {
1619 .zig_object => {
1620 const zo = wasm.zig_object.?;
1621 if (is_undefined) {
1622 const type_index = zo.imports.get(loc.index).?.kind.function;
1623 return zo.func_types.items[type_index];
1624 }
1625 const sym = zo.symbols.items[@intFromEnum(loc.index)];
1626 const type_index = zo.functions.items[sym.index].type_index;
1627 return zo.func_types.items[type_index];
1628 },
1629 .none => {
1630 if (is_undefined) {
1631 const type_index = wasm.imports.get(loc).?.kind.function;
1632 return wasm.func_types.items[type_index];
1633 }
1634 return wasm.func_types.items[
1635 wasm.functions.get(.{
1636 .file = .none,
1637 .index = symbol.index,
1638 }).?.func.type_index
1639 ];
1640 },
1641 _ => {
1642 const obj = &wasm.objects.items[@intFromEnum(loc.file)];
1643 if (is_undefined) {
1644 const type_index = obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.function;
1645 return obj.func_types[type_index];
1646 }
1647 const sym = obj.symtable[@intFromEnum(loc.index)];
1648 const type_index = obj.functions[sym.index - obj.imported_functions_count].type_index;
1649 return obj.func_types[type_index];
1650 },
1651 }1594 }
1652}1595}
16531596
1654/// Returns the symbol index from a symbol of which its flag is set global,1597/// Recursively mark alive everything referenced by the global.
1655/// such as an exported or imported symbol.1598fn markGlobal(
1656/// If the symbol does not yet exist, creates a new one symbol instead
1657/// and then returns the index to it.
1658pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
1659 _ = lib_name;
1660 const name_index = try wasm.internString(name);
1661 return wasm.zig_object.?.getGlobalSymbol(wasm.base.comp.gpa, name_index);
1662}
1663
1664/// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type.
1665/// Returns the given pointer address
1666pub fn getNavVAddr(
1667 wasm: *Wasm,
1668 pt: Zcu.PerThread,
1669 nav: InternPool.Nav.Index,
1670 reloc_info: link.File.RelocInfo,
1671) !u64 {
1672 return wasm.zig_object.?.getNavVAddr(wasm, pt, nav, reloc_info);
1673}
1674
1675pub fn lowerUav(
1676 wasm: *Wasm,
1677 pt: Zcu.PerThread,
1678 uav: InternPool.Index,
1679 explicit_alignment: Alignment,
1680 src_loc: Zcu.LazySrcLoc,
1681) !codegen.GenResult {
1682 return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc);
1683}
1684
1685pub fn getUavVAddr(wasm: *Wasm, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
1686 return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info);
1687}
1688
1689pub fn deleteExport(
1690 wasm: *Wasm,
1691 exported: Zcu.Exported,
1692 name: InternPool.NullTerminatedString,
1693) void {
1694 if (wasm.llvm_object) |_| return;
1695 return wasm.zig_object.?.deleteExport(wasm, exported, name);
1696}
1697
1698pub fn updateExports(
1699 wasm: *Wasm,1599 wasm: *Wasm,
1700 pt: Zcu.PerThread,1600 name: String,
1701 exported: Zcu.Exported,1601 import: *GlobalImport,
1702 export_indices: []const u32,1602 global_index: ObjectGlobalImportIndex,
1703) !void {1603) !void {
1704 if (build_options.skip_non_native and builtin.object_format != .wasm) {1604 if (import.flags.alive) return;
1705 @panic("Attempted to compile for object format that was disabled by build configuration");1605 import.flags.alive = true;
1706 }
1707 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
1708 return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices);
1709}
17101606
1711pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {1607 const comp = wasm.base.comp;
1712 if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);1608 const gpa = comp.gpa;
1713 return wasm.zig_object.?.freeDecl(wasm, decl_index);1609 const rdynamic = comp.config.rdynamic;
1714}1610 const is_obj = comp.config.output_mode == .Obj;
17151611
1716/// Assigns indexes to all indirect functions.1612 try wasm.globals.ensureUnusedCapacity(gpa, 1);
1717/// Starts at offset 1, where the value `0` represents an unresolved function pointer1613
1718/// or null-pointer1614 if (import.resolution == .unresolved) {
1719fn mapFunctionTable(wasm: *Wasm) void {1615 if (name == wasm.preloaded_strings.__heap_base) {
1720 var it = wasm.function_table.iterator();1616 import.resolution = .__heap_base;
1721 var index: u32 = 1;1617 wasm.globals.putAssumeCapacity(.__heap_base, {});
1722 while (it.next()) |entry| {1618 } else if (name == wasm.preloaded_strings.__heap_end) {
1723 const symbol = wasm.symbolLocSymbol(entry.key_ptr.*);1619 import.resolution = .__heap_end;
1724 if (symbol.isAlive()) {1620 wasm.globals.putAssumeCapacity(.__heap_end, {});
1725 entry.value_ptr.* = index;1621 } else if (name == wasm.preloaded_strings.__stack_pointer) {
1726 index += 1;1622 import.resolution = .__stack_pointer;
1623 wasm.globals.putAssumeCapacity(.__stack_pointer, {});
1624 } else if (name == wasm.preloaded_strings.__tls_align) {
1625 import.resolution = .__tls_align;
1626 wasm.globals.putAssumeCapacity(.__tls_align, {});
1627 } else if (name == wasm.preloaded_strings.__tls_base) {
1628 import.resolution = .__tls_base;
1629 wasm.globals.putAssumeCapacity(.__tls_base, {});
1630 } else if (name == wasm.preloaded_strings.__tls_size) {
1631 import.resolution = .__tls_size;
1632 wasm.globals.putAssumeCapacity(.__tls_size, {});
1727 } else {1633 } else {
1728 wasm.function_table.removeByPtr(entry.key_ptr);1634 try wasm.global_imports.put(gpa, .fromObject(global_index), {});
1729 }1635 }
1730 }
1731
1732 if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) {
1733 const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
1734 const import = wasm.imports.getPtr(sym_loc).?;
1735 import.kind.table.limits.min = index - 1; // we start at index 1.
1736 } else if (index > 1) {
1737 log.debug("Appending indirect function table", .{});
1738 const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
1739 const symbol = wasm.symbolLocSymbol(sym_loc);
1740 const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
1741 table.limits = .{ .min = index, .max = index, .flags = 0x1 };
1742 }
1743}
1744
1745/// From a given index, append the given `Atom` at the back of the linked list.
1746/// Simply inserts it into the map of atoms when it doesn't exist yet.
1747pub fn appendAtomAtIndex(wasm: *Wasm, index: Segment.Index, atom_index: Atom.Index) !void {
1748 const gpa = wasm.base.comp.gpa;
1749 const atom = wasm.getAtomPtr(atom_index);
1750 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
1751 atom.prev = last_index_ptr.*;
1752 last_index_ptr.* = atom_index;
1753 } else {1636 } else {
1754 try wasm.atoms.putNoClobber(gpa, index, atom_index);1637 const gop = wasm.globals.getOrPutAssumeCapacity(import.resolution);
1755 }
1756}
1757
1758fn allocateAtoms(wasm: *Wasm) !void {
1759 // first sort the data segments
1760 try sortDataSegments(wasm);
1761
1762 var it = wasm.atoms.iterator();
1763 while (it.next()) |entry| {
1764 const segment = wasm.segmentPtr(entry.key_ptr.*);
1765 var atom_index = entry.value_ptr.*;
1766 if (entry.key_ptr.toOptional() == wasm.code_section_index) {
1767 // Code section is allocated upon writing as they are required to be ordered
1768 // to synchronise with the function section.
1769 continue;
1770 }
1771 var offset: u32 = 0;
1772 while (true) {
1773 const atom = wasm.getAtomPtr(atom_index);
1774 const symbol_loc = atom.symbolLoc();
1775 // Ensure we get the original symbol, so we verify the correct symbol on whether
1776 // it is dead or not and ensure an atom is removed when dead.
1777 // This is required as we may have parsed aliases into atoms.
1778 const sym = switch (symbol_loc.file) {
1779 .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)],
1780 .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)],
1781 _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)],
1782 };
1783
1784 // Dead symbols must be unlinked from the linked-list to prevent them
1785 // from being emit into the binary.
1786 if (sym.isDead()) {
1787 if (entry.value_ptr.* == atom_index and atom.prev != .null) {
1788 // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
1789 // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
1790 // was removed and therefore do not emit any code at all.
1791 entry.value_ptr.* = atom.prev;
1792 }
1793 if (atom.prev == .null) break;
1794 atom_index = atom.prev;
1795 atom.prev = .null;
1796 continue;
1797 }
1798 offset = @intCast(atom.alignment.forward(offset));
1799 atom.offset = offset;
1800 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
1801 wasm.symbolLocName(symbol_loc),
1802 offset,
1803 offset + atom.size,
1804 atom.size,
1805 });
1806 offset += atom.size;
1807 if (atom.prev == .null) break;
1808 atom_index = atom.prev;
1809 }
1810 segment.size = @intCast(segment.alignment.forward(offset));
1811 }
1812}
18131638
1814/// For each data symbol, sets the virtual address.1639 if (!is_obj and import.flags.isExported(rdynamic))
1815fn allocateVirtualAddresses(wasm: *Wasm) void {1640 try wasm.global_exports.append(gpa, @intCast(gop.index));
1816 for (wasm.resolved_symbols.keys()) |loc| {
1817 const symbol = wasm.symbolLocSymbol(loc);
1818 if (symbol.tag != .data or symbol.isDead()) {
1819 // Only data symbols have virtual addresses.
1820 // Dead symbols do not get allocated, so we don't need to set their virtual address either.
1821 continue;
1822 }
1823 const atom_index = wasm.symbol_atom.get(loc) orelse {
1824 // synthetic symbol that does not contain an atom
1825 continue;
1826 };
18271641
1828 const atom = wasm.getAtom(atom_index);1642 for (wasm.globalResolutionRelocSlice(import.resolution)) |reloc|
1829 const merge_segment = wasm.base.comp.config.output_mode != .Obj;1643 try wasm.markReloc(reloc);
1830 const segment_info = switch (atom.file) {
1831 .zig_object => wasm.zig_object.?.segment_info.items,
1832 .none => wasm.segment_info.values(),
1833 _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info,
1834 };
1835 const segment_name = segment_info[symbol.index].outputName(merge_segment);
1836 const segment_index = wasm.data_segments.get(segment_name).?;
1837 const segment = wasm.segmentPtr(segment_index);
1838
1839 // TLS symbols have their virtual address set relative to their own TLS segment,
1840 // rather than the entire Data section.
1841 if (symbol.hasFlag(.WASM_SYM_TLS)) {
1842 symbol.virtual_address = atom.offset;
1843 } else {
1844 symbol.virtual_address = atom.offset + segment.offset;
1845 }
1846 }1644 }
1847}1645}
18481646
1849fn sortDataSegments(wasm: *Wasm) !void {1647fn markTable(
1850 const gpa = wasm.base.comp.gpa;1648 wasm: *Wasm,
1851 var new_mapping: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty;1649 name: String,
1852 try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count());1650 import: *TableImport,
1853 errdefer new_mapping.deinit(gpa);1651 table_index: ObjectTableImportIndex,
18541652) !void {
1855 const keys = try gpa.dupe([]const u8, wasm.data_segments.keys());1653 if (import.flags.alive) return;
1856 defer gpa.free(keys);1654 import.flags.alive = true;
1857
1858 const SortContext = struct {
1859 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
1860 return order(lhs) < order(rhs);
1861 }
18621655
1863 fn order(name: []const u8) u8 {1656 const comp = wasm.base.comp;
1864 if (mem.startsWith(u8, name, ".rodata")) return 0;1657 const gpa = comp.gpa;
1865 if (mem.startsWith(u8, name, ".data")) return 1;
1866 if (mem.startsWith(u8, name, ".text")) return 2;
1867 return 3;
1868 }
1869 };
18701658
1871 mem.sort([]const u8, keys, {}, SortContext.sort);1659 try wasm.tables.ensureUnusedCapacity(gpa, 1);
1872 for (keys) |key| {
1873 const segment_index = wasm.data_segments.get(key).?;
1874 new_mapping.putAssumeCapacity(key, segment_index);
1875 }
1876 wasm.data_segments.deinit(gpa);
1877 wasm.data_segments = new_mapping;
1878}
18791660
1880/// Obtains all initfuncs from each object file, verifies its function signature,1661 if (import.resolution == .unresolved) {
1881/// and then appends it to our final `init_funcs` list.1662 if (name == wasm.preloaded_strings.__indirect_function_table) {
1882/// After all functions have been inserted, the functions will be ordered based1663 import.resolution = .__indirect_function_table;
1883/// on their priority.1664 wasm.tables.putAssumeCapacity(.__indirect_function_table, {});
1884/// NOTE: This function must be called before we merged any other section.1665 } else {
1885/// This is because all init funcs in the object files contain references to the1666 try wasm.table_imports.put(gpa, .fromObject(table_index), {});
1886/// original functions and their types. We need to know the type to verify it doesn't
1887/// contain any parameters.
1888fn setupInitFunctions(wasm: *Wasm) !void {
1889 const gpa = wasm.base.comp.gpa;
1890 const diags = &wasm.base.comp.link_diags;
1891 // There's no constructors for Zig so we can simply search through linked object files only.
1892 for (wasm.objects.items, 0..) |*object, object_index| {
1893 try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
1894 for (object.init_funcs) |init_func| {
1895 const symbol = object.symtable[init_func.symbol_index];
1896 const ty: std.wasm.Type = if (symbol.isUndefined()) ty: {
1897 const imp: Import = object.findImport(symbol);
1898 break :ty object.func_types[imp.kind.function];
1899 } else ty: {
1900 const func_index = symbol.index - object.imported_functions_count;
1901 const func = object.functions[func_index];
1902 break :ty object.func_types[func.type_index];
1903 };
1904 if (ty.params.len != 0) {
1905 var err = try diags.addErrorWithNotes(0);
1906 try err.addMsg("constructor functions cannot take arguments: '{s}'", .{wasm.stringSlice(symbol.name)});
1907 }
1908 log.debug("appended init func '{s}'\n", .{wasm.stringSlice(symbol.name)});
1909 wasm.init_funcs.appendAssumeCapacity(.{
1910 .index = @enumFromInt(init_func.symbol_index),
1911 .file = @enumFromInt(object_index),
1912 .priority = init_func.priority,
1913 });
1914 try wasm.mark(.{
1915 .index = @enumFromInt(init_func.symbol_index),
1916 .file = @enumFromInt(object_index),
1917 });
1918 }1667 }
1919 }1668 } else {
19201669 wasm.tables.putAssumeCapacity(import.resolution, {});
1921 // sort the initfunctions based on their priority1670 // Tables have no relocations.
1922 mem.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);
1923
1924 if (wasm.init_funcs.items.len > 0) {
1925 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?;
1926 try wasm.mark(loc);
1927 }1671 }
1928}1672}
19291673
1930/// Creates a function body for the `__wasm_call_ctors` symbol.1674fn globalResolutionRelocSlice(wasm: *Wasm, resolution: GlobalImport.Resolution) ![]const Relocation {
1931/// Loops over all constructors found in `init_funcs` and calls them1675 assert(resolution != .none);
1932/// respectively based on their priority which was sorted by `setupInitFunctions`.1676 _ = wasm;
1933/// NOTE: This function must be called after we merged all sections to ensure the1677 @panic("TODO");
1934/// references to the function stored in the symbol have been finalized so we end
1935/// up calling the resolved function.
1936fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1937 const gpa = wasm.base.comp.gpa;
1938 // No code to emit, so also no ctors to call
1939 if (wasm.code_section_index == .none) {
1940 // Make sure to remove it from the resolved symbols so we do not emit
1941 // it within any section. TODO: Remove this once we implement garbage collection.
1942 const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?;
1943 assert(wasm.resolved_symbols.swapRemove(loc));
1944 return;
1945 }
1946
1947 var function_body = std.ArrayList(u8).init(gpa);
1948 defer function_body.deinit();
1949 const writer = function_body.writer();
1950
1951 // Create the function body
1952 {
1953 // Write locals count (we have none)
1954 try leb.writeUleb128(writer, @as(u32, 0));
1955
1956 // call constructors
1957 for (wasm.init_funcs.items) |init_func_loc| {
1958 const symbol = init_func_loc.getSymbol(wasm);
1959 const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
1960 const ty = wasm.func_types.items[func.type_index];
1961
1962 // Call function by its function index
1963 try writer.writeByte(std.wasm.opcode(.call));
1964 try leb.writeUleb128(writer, symbol.index);
1965
1966 // drop all returned values from the stack as __wasm_call_ctors has no return value
1967 for (ty.returns) |_| {
1968 try writer.writeByte(std.wasm.opcode(.drop));
1969 }
1970 }
1971
1972 // End function body
1973 try writer.writeByte(std.wasm.opcode(.end));
1974 }
1975
1976 try wasm.createSyntheticFunction(
1977 wasm.preloaded_strings.__wasm_call_ctors,
1978 std.wasm.Type{ .params = &.{}, .returns = &.{} },
1979 &function_body,
1980 );
1981}1678}
19821679
1983fn createSyntheticFunction(1680fn functionResolutionRelocSlice(wasm: *Wasm, resolution: FunctionImport.Resolution) ![]const Relocation {
1984 wasm: *Wasm,1681 assert(resolution != .none);
1985 symbol_name: String,1682 _ = wasm;
1986 func_ty: std.wasm.Type,1683 @panic("TODO");
1987 function_body: *std.ArrayList(u8),
1988) !void {
1989 const gpa = wasm.base.comp.gpa;
1990 const loc = wasm.globals.get(symbol_name).?;
1991 const symbol = wasm.symbolLocSymbol(loc);
1992 if (symbol.isDead()) {
1993 return;
1994 }
1995 const ty_index = try wasm.putOrGetFuncType(func_ty);
1996 // create function with above type
1997 const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));
1998 try wasm.functions.putNoClobber(
1999 gpa,
2000 .{ .file = .none, .index = func_index },
2001 .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },
2002 );
2003 symbol.index = func_index;
2004
2005 // create the atom that will be output into the final binary
2006 const atom_index = try wasm.createAtom(loc.index, .none);
2007 const atom = wasm.getAtomPtr(atom_index);
2008 atom.size = @intCast(function_body.items.len);
2009 atom.code = function_body.moveToUnmanaged();
2010 try wasm.appendAtomAtIndex(wasm.code_section_index.unwrap().?, atom_index);
2011}1684}
20121685
2013/// Unlike `createSyntheticFunction` this function is to be called by1686pub fn flushModule(
2014/// the codegeneration backend. This will not allocate the created Atom yet.
2015/// Returns the index of the symbol.
2016pub fn createFunction(
2017 wasm: *Wasm,1687 wasm: *Wasm,
2018 symbol_name: []const u8,1688 arena: Allocator,
2019 func_ty: std.wasm.Type,1689 tid: Zcu.PerThread.Id,
2020 function_body: *std.ArrayList(u8),1690 prog_node: std.Progress.Node,
2021 relocations: *std.ArrayList(Relocation),1691) link.File.FlushError!void {
2022) !Symbol.Index {1692 // The goal is to never use this because it's only needed if we need to
2023 return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);1693 // write to InternPool, but flushModule is too late to be writing to the
2024}1694 // InternPool.
20251695 _ = tid;
2026/// If required, sets the function index in the `start` section.1696 const comp = wasm.base.comp;
2027fn setupStartSection(wasm: *Wasm) !void {1697 const use_lld = build_options.have_llvm and comp.config.use_lld;
2028 if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory)) |loc| {
2029 wasm.entry = wasm.symbolLocSymbol(loc).index;
2030 }
2031}
2032
2033fn initializeTLSFunction(wasm: *Wasm) !void {
2034 const comp = wasm.base.comp;
2035 const gpa = comp.gpa;
2036 const shared_memory = comp.config.shared_memory;
2037
2038 if (!shared_memory) return;
2039
2040 // ensure function is marked as we must emit it
2041 wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls).?).mark();
2042
2043 var function_body = std.ArrayList(u8).init(gpa);
2044 defer function_body.deinit();
2045 const writer = function_body.writer();
2046
2047 // locals
2048 try writer.writeByte(0);
2049
2050 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature
2051 if (wasm.data_segments.getIndex(".tdata")) |data_index| {
2052 const segment_index = wasm.data_segments.entries.items(.value)[data_index];
2053 const segment = wasm.segmentPtr(segment_index);
2054
2055 const param_local: u32 = 0;
2056
2057 try writer.writeByte(std.wasm.opcode(.local_get));
2058 try leb.writeUleb128(writer, param_local);
2059
2060 const tls_base_loc = wasm.globals.get(wasm.preloaded_strings.__tls_base).?;
2061 try writer.writeByte(std.wasm.opcode(.global_set));
2062 try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index);
2063
2064 // load stack values for the bulk-memory operation
2065 {
2066 try writer.writeByte(std.wasm.opcode(.local_get));
2067 try leb.writeUleb128(writer, param_local);
2068
2069 try writer.writeByte(std.wasm.opcode(.i32_const));
2070 try leb.writeUleb128(writer, @as(u32, 0)); //segment offset
2071
2072 try writer.writeByte(std.wasm.opcode(.i32_const));
2073 try leb.writeUleb128(writer, @as(u32, segment.size)); //segment offset
2074 }
2075
2076 // perform the bulk-memory operation to initialize the data segment
2077 try writer.writeByte(std.wasm.opcode(.misc_prefix));
2078 try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init));
2079 // segment immediate
2080 try leb.writeUleb128(writer, @as(u32, @intCast(data_index)));
2081 // memory index immediate (always 0)
2082 try leb.writeUleb128(writer, @as(u32, 0));
2083 }
2084
2085 // If we have to perform any TLS relocations, call the corresponding function
2086 // which performs all runtime TLS relocations. This is a synthetic function,
2087 // generated by the linker.
2088 if (wasm.globals.get(wasm.preloaded_strings.__wasm_apply_global_tls_relocs)) |loc| {
2089 try writer.writeByte(std.wasm.opcode(.call));
2090 try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index);
2091 wasm.symbolLocSymbol(loc).mark();
2092 }
2093
2094 try writer.writeByte(std.wasm.opcode(.end));
2095
2096 try wasm.createSyntheticFunction(
2097 wasm.preloaded_strings.__wasm_init_tls,
2098 std.wasm.Type{ .params = &.{.i32}, .returns = &.{} },
2099 &function_body,
2100 );
2101}
2102
2103fn setupImports(wasm: *Wasm) !void {
2104 const gpa = wasm.base.comp.gpa;
2105 log.debug("Merging imports", .{});
2106 for (wasm.resolved_symbols.keys()) |symbol_loc| {
2107 const object_id = symbol_loc.file.unwrap() orelse {
2108 // Synthetic symbols will already exist in the `import` section
2109 continue;
2110 };
2111
2112 const symbol = wasm.symbolLocSymbol(symbol_loc);
2113 if (symbol.isDead()) continue;
2114 if (!symbol.requiresImport()) continue;
2115 if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue;
2116
2117 log.debug("Symbol '{s}' will be imported from the host", .{wasm.stringSlice(symbol.name)});
2118 const import = objectImport(wasm, object_id, symbol_loc.index);
2119
2120 // We copy the import to a new import to ensure the names contain references
2121 // to the internal string table, rather than of the object file.
2122 const new_imp: Import = .{
2123 .module_name = import.module_name,
2124 .name = import.name,
2125 .kind = import.kind,
2126 };
2127 // TODO: De-duplicate imports when they contain the same names and type
2128 try wasm.imports.putNoClobber(gpa, symbol_loc, new_imp);
2129 }
2130
2131 // Assign all indexes of the imports to their representing symbols
2132 var function_index: u32 = 0;
2133 var global_index: u32 = 0;
2134 var table_index: u32 = 0;
2135 var it = wasm.imports.iterator();
2136 while (it.next()) |entry| {
2137 const symbol = wasm.symbolLocSymbol(entry.key_ptr.*);
2138 const import: Import = entry.value_ptr.*;
2139 switch (import.kind) {
2140 .function => {
2141 symbol.index = function_index;
2142 function_index += 1;
2143 },
2144 .global => {
2145 symbol.index = global_index;
2146 global_index += 1;
2147 },
2148 .table => {
2149 symbol.index = table_index;
2150 table_index += 1;
2151 },
2152 else => unreachable,
2153 }
2154 }
2155 wasm.imported_functions_count = function_index;
2156 wasm.imported_globals_count = global_index;
2157 wasm.imported_tables_count = table_index;
2158
2159 log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{
2160 function_index,
2161 global_index,
2162 table_index,
2163 });
2164}
2165
2166/// Takes the global, function and table section from each linked object file
2167/// and merges it into a single section for each.
2168fn mergeSections(wasm: *Wasm) !void {
2169 const gpa = wasm.base.comp.gpa;
2170
2171 var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa);
2172 defer removed_duplicates.deinit();
2173
2174 for (wasm.resolved_symbols.keys()) |sym_loc| {
2175 const object_id = sym_loc.file.unwrap() orelse {
2176 // Synthetic symbols already live in the corresponding sections.
2177 continue;
2178 };
2179
2180 const symbol = objectSymbol(wasm, object_id, sym_loc.index);
2181 if (symbol.isDead() or symbol.isUndefined()) {
2182 // Skip undefined symbols as they go in the `import` section
2183 continue;
2184 }
2185
2186 switch (symbol.tag) {
2187 .function => {
2188 const gop = try wasm.functions.getOrPut(
2189 gpa,
2190 .{ .file = sym_loc.file, .index = symbol.index },
2191 );
2192 if (gop.found_existing) {
2193 // We found an alias to the same function, discard this symbol in favor of
2194 // the original symbol and point the discard function to it. This ensures
2195 // we only emit a single function, instead of duplicates.
2196 // we favor keeping the global over a local.
2197 const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index };
2198 const original_sym = wasm.symbolLocSymbol(original_loc);
2199 if (original_sym.isLocal() and symbol.isGlobal()) {
2200 original_sym.unmark();
2201 try wasm.discarded.put(gpa, original_loc, sym_loc);
2202 try removed_duplicates.append(original_loc);
2203 } else {
2204 symbol.unmark();
2205 try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc);
2206 try removed_duplicates.append(sym_loc);
2207 continue;
2208 }
2209 }
2210 gop.value_ptr.* = .{
2211 .func = objectFunction(wasm, object_id, sym_loc.index),
2212 .sym_index = sym_loc.index,
2213 };
2214 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
2215 },
2216 .global => {
2217 const index = symbol.index - objectImportedFunctions(wasm, object_id);
2218 const original_global = objectGlobals(wasm, object_id)[index];
2219 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2220 try wasm.wasm_globals.append(gpa, original_global);
2221 },
2222 .table => {
2223 const index = symbol.index - objectImportedFunctions(wasm, object_id);
2224 // assert it's a regular relocatable object file as `ZigObject` will never
2225 // contain a table.
2226 const original_table = wasm.objectById(object_id).?.tables[index];
2227 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
2228 try wasm.tables.append(gpa, original_table);
2229 },
2230 .dead, .undefined => unreachable,
2231 else => {},
2232 }
2233 }
2234
2235 // For any removed duplicates, remove them from the resolved symbols list
2236 for (removed_duplicates.items) |sym_loc| {
2237 assert(wasm.resolved_symbols.swapRemove(sym_loc));
2238 gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)});
2239 }
2240
2241 log.debug("Merged ({d}) functions", .{wasm.functions.count()});
2242 log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len});
2243 log.debug("Merged ({d}) tables", .{wasm.tables.items.len});
2244}
2245
2246/// Merges function types of all object files into the final
2247/// 'types' section, while assigning the type index to the representing
2248/// section (import, export, function).
2249fn mergeTypes(wasm: *Wasm) !void {
2250 const gpa = wasm.base.comp.gpa;
2251 // A map to track which functions have already had their
2252 // type inserted. If we do this for the same function multiple times,
2253 // it will be overwritten with the incorrect type.
2254 var dirty = std.AutoHashMap(u32, void).init(gpa);
2255 try dirty.ensureUnusedCapacity(@as(u32, @intCast(wasm.functions.count())));
2256 defer dirty.deinit();
2257
2258 for (wasm.resolved_symbols.keys()) |sym_loc| {
2259 const object_id = sym_loc.file.unwrap() orelse {
2260 // zig code-generated symbols are already present in final type section
2261 continue;
2262 };
2263
2264 const symbol = objectSymbol(wasm, object_id, sym_loc.index);
2265 if (symbol.tag != .function or symbol.isDead()) {
2266 // Only functions have types. Only retrieve the type of referenced functions.
2267 continue;
2268 }
2269
2270 if (symbol.isUndefined()) {
2271 log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)});
2272 const import: *Import = wasm.imports.getPtr(sym_loc) orelse continue;
2273 const original_type = objectFuncTypes(wasm, object_id)[import.kind.function];
2274 import.kind.function = try wasm.putOrGetFuncType(original_type);
2275 } else if (!dirty.contains(symbol.index)) {
2276 log.debug("Adding type from function '{s}'", .{wasm.symbolLocName(sym_loc)});
2277 const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
2278 func.type_index = try wasm.putOrGetFuncType(objectFuncTypes(wasm, object_id)[func.type_index]);
2279 dirty.putAssumeCapacityNoClobber(symbol.index, {});
2280 }
2281 }
2282 log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
2283}
2284
2285fn checkExportNames(wasm: *Wasm) !void {
2286 const force_exp_names = wasm.export_symbol_names;
2287 const diags = &wasm.base.comp.link_diags;
2288 if (force_exp_names.len > 0) {
2289 var failed_exports = false;
2290
2291 for (force_exp_names) |exp_name| {
2292 const exp_name_interned = try wasm.internString(exp_name);
2293 const loc = wasm.globals.get(exp_name_interned) orelse {
2294 var err = try diags.addErrorWithNotes(0);
2295 try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
2296 failed_exports = true;
2297 continue;
2298 };
2299
2300 const symbol = wasm.symbolLocSymbol(loc);
2301 symbol.setFlag(.WASM_SYM_EXPORTED);
2302 }
2303
2304 if (failed_exports) {
2305 return error.FlushFailure;
2306 }
2307 }
2308}
2309
2310fn setupExports(wasm: *Wasm) !void {
2311 const comp = wasm.base.comp;
2312 const gpa = comp.gpa;
2313 if (comp.config.output_mode == .Obj) return;
2314 log.debug("Building exports from symbols", .{});
2315
2316 for (wasm.resolved_symbols.keys()) |sym_loc| {
2317 const symbol = wasm.symbolLocSymbol(sym_loc);
2318 if (!symbol.isExported(comp.config.rdynamic)) continue;
2319
2320 const exp: Export = if (symbol.tag == .data) exp: {
2321 const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));
2322 try wasm.wasm_globals.append(gpa, .{
2323 .global_type = .{ .valtype = .i32, .mutable = false },
2324 .init = .{ .i32_const = @as(i32, @intCast(symbol.virtual_address)) },
2325 });
2326 break :exp .{
2327 .name = symbol.name,
2328 .kind = .global,
2329 .index = global_index,
2330 };
2331 } else .{
2332 .name = symbol.name,
2333 .kind = symbol.tag.externalType(),
2334 .index = symbol.index,
2335 };
2336 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{
2337 wasm.stringSlice(symbol.name),
2338 wasm.stringSlice(exp.name),
2339 exp.index,
2340 });
2341 try wasm.exports.append(gpa, exp);
2342 }
2343
2344 log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len});
2345}
2346
2347fn setupStart(wasm: *Wasm) !void {
2348 const comp = wasm.base.comp;
2349 const diags = &wasm.base.comp.link_diags;
2350 // do not export entry point if user set none or no default was set.
2351 const entry_name = wasm.entry_name.unwrap() orelse return;
2352
2353 const symbol_loc = wasm.globals.get(entry_name) orelse {
2354 var err = try diags.addErrorWithNotes(1);
2355 try err.addMsg("entry symbol '{s}' missing", .{wasm.stringSlice(entry_name)});
2356 try err.addNote("'-fno-entry' suppresses this error", .{});
2357 return error.LinkFailure;
2358 };
2359
2360 const symbol = wasm.symbolLocSymbol(symbol_loc);
2361 if (symbol.tag != .function)
2362 return diags.fail("entry symbol '{s}' is not a function", .{wasm.stringSlice(entry_name)});
2363
2364 // Ensure the symbol is exported so host environment can access it
2365 if (comp.config.output_mode != .Obj) {
2366 symbol.setFlag(.WASM_SYM_EXPORTED);
2367 }
2368}
2369
2370/// Sets up the memory section of the wasm module, as well as the stack.
2371fn setupMemory(wasm: *Wasm) !void {
2372 const comp = wasm.base.comp;
2373 const diags = &wasm.base.comp.link_diags;
2374 const shared_memory = comp.config.shared_memory;
2375 log.debug("Setting up memory layout", .{});
2376 const page_size = std.wasm.page_size; // 64kb
2377 const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention
2378 const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention
2379
2380 // Always place the stack at the start by default
2381 // unless the user specified the global-base flag
2382 var place_stack_first = true;
2383 var memory_ptr: u64 = if (wasm.global_base) |base| blk: {
2384 place_stack_first = false;
2385 break :blk base;
2386 } else 0;
2387
2388 const is_obj = comp.config.output_mode == .Obj;
2389
2390 const stack_ptr = if (wasm.globals.get(wasm.preloaded_strings.__stack_pointer)) |loc| index: {
2391 const sym = wasm.symbolLocSymbol(loc);
2392 break :index sym.index - wasm.imported_globals_count;
2393 } else null;
2394
2395 if (place_stack_first and !is_obj) {
2396 memory_ptr = stack_alignment.forward(memory_ptr);
2397 memory_ptr += wasm.base.stack_size;
2398 // We always put the stack pointer global at index 0
2399 if (stack_ptr) |index| {
2400 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2401 }
2402 }
2403
2404 var offset: u32 = @as(u32, @intCast(memory_ptr));
2405 var data_seg_it = wasm.data_segments.iterator();
2406 while (data_seg_it.next()) |entry| {
2407 const segment = wasm.segmentPtr(entry.value_ptr.*);
2408 memory_ptr = segment.alignment.forward(memory_ptr);
2409
2410 // set TLS-related symbols
2411 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
2412 if (wasm.globals.get(wasm.preloaded_strings.__tls_size)) |loc| {
2413 const sym = wasm.symbolLocSymbol(loc);
2414 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
2415 }
2416 if (wasm.globals.get(wasm.preloaded_strings.__tls_align)) |loc| {
2417 const sym = wasm.symbolLocSymbol(loc);
2418 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?);
2419 }
2420 if (wasm.globals.get(wasm.preloaded_strings.__tls_base)) |loc| {
2421 const sym = wasm.symbolLocSymbol(loc);
2422 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory)
2423 @as(i32, 0)
2424 else
2425 @as(i32, @intCast(memory_ptr));
2426 }
2427 }
2428
2429 memory_ptr += segment.size;
2430 segment.offset = offset;
2431 offset += segment.size;
2432 }
2433
2434 // create the memory init flag which is used by the init memory function
2435 if (shared_memory and wasm.hasPassiveInitializationSegments()) {
2436 // align to pointer size
2437 memory_ptr = mem.alignForward(u64, memory_ptr, 4);
2438 const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory_flag, .data);
2439 const sym = wasm.symbolLocSymbol(loc);
2440 sym.mark();
2441 sym.virtual_address = @as(u32, @intCast(memory_ptr));
2442 memory_ptr += 4;
2443 }
2444
2445 if (!place_stack_first and !is_obj) {
2446 memory_ptr = stack_alignment.forward(memory_ptr);
2447 memory_ptr += wasm.base.stack_size;
2448 if (stack_ptr) |index| {
2449 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2450 }
2451 }
2452
2453 // One of the linked object files has a reference to the __heap_base symbol.
2454 // We must set its virtual address so it can be used in relocations.
2455 if (wasm.globals.get(wasm.preloaded_strings.__heap_base)) |loc| {
2456 const symbol = wasm.symbolLocSymbol(loc);
2457 symbol.virtual_address = @intCast(heap_alignment.forward(memory_ptr));
2458 }
2459
2460 // Setup the max amount of pages
2461 // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1
2462 const max_memory_allowed: u64 = (1 << 32) - 1;
2463
2464 if (wasm.initial_memory) |initial_memory| {
2465 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2466 var err = try diags.addErrorWithNotes(0);
2467 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
2468 }
2469 if (memory_ptr > initial_memory) {
2470 var err = try diags.addErrorWithNotes(0);
2471 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
2472 }
2473 if (initial_memory > max_memory_allowed) {
2474 var err = try diags.addErrorWithNotes(0);
2475 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
2476 }
2477 memory_ptr = initial_memory;
2478 }
2479 memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size);
2480 // In case we do not import memory, but define it ourselves,
2481 // set the minimum amount of pages on the memory section.
2482 wasm.memories.limits.min = @as(u32, @intCast(memory_ptr / page_size));
2483 log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
2484
2485 if (wasm.globals.get(wasm.preloaded_strings.__heap_end)) |loc| {
2486 const symbol = wasm.symbolLocSymbol(loc);
2487 symbol.virtual_address = @as(u32, @intCast(memory_ptr));
2488 }
2489
2490 if (wasm.max_memory) |max_memory| {
2491 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2492 var err = try diags.addErrorWithNotes(0);
2493 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
2494 }
2495 if (memory_ptr > max_memory) {
2496 var err = try diags.addErrorWithNotes(0);
2497 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
2498 }
2499 if (max_memory > max_memory_allowed) {
2500 var err = try diags.addErrorWithNotes(0);
2501 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
2502 }
2503 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
2504 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
2505 if (shared_memory) {
2506 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED);
2507 }
2508 log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});
2509 }
2510}
2511
2512/// From a given object's index and the index of the segment, returns the corresponding
2513/// index of the segment within the final data section. When the segment does not yet
2514/// exist, a new one will be initialized and appended. The new index will be returned in that case.
2515pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Segment.Index {
2516 const comp = wasm.base.comp;
2517 const gpa = comp.gpa;
2518 const diags = &wasm.base.comp.link_diags;
2519 const symbol = objectSymbols(wasm, object_id)[@intFromEnum(symbol_index)];
2520 const index: Segment.Index = @enumFromInt(wasm.segments.items.len);
2521 const shared_memory = comp.config.shared_memory;
2522
2523 switch (symbol.tag) {
2524 .data => {
2525 const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index];
2526 const merge_segment = comp.config.output_mode != .Obj;
2527 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
2528 if (!result.found_existing) {
2529 result.value_ptr.* = index;
2530 var flags: u32 = 0;
2531 if (shared_memory) {
2532 flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
2533 }
2534 try wasm.segments.append(gpa, .{
2535 .alignment = .@"1",
2536 .size = 0,
2537 .offset = 0,
2538 .flags = flags,
2539 });
2540 try wasm.segment_info.putNoClobber(gpa, index, .{
2541 .name = try gpa.dupe(u8, segment_info.name),
2542 .alignment = segment_info.alignment,
2543 .flags = segment_info.flags,
2544 });
2545 return index;
2546 } else return result.value_ptr.*;
2547 },
2548 .function => return wasm.code_section_index.unwrap() orelse blk: {
2549 wasm.code_section_index = index.toOptional();
2550 try wasm.appendDummySegment();
2551 break :blk index;
2552 },
2553 .section => {
2554 const section_name = wasm.objectSymbol(object_id, symbol_index).name;
2555
2556 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {
2557 if (@field(wasm.custom_sections, field.name).name == section_name) {
2558 const field_ptr = &@field(wasm.custom_sections, field.name).index;
2559 return field_ptr.unwrap() orelse {
2560 field_ptr.* = index.toOptional();
2561 try wasm.appendDummySegment();
2562 return index;
2563 };
2564 }
2565 } else {
2566 return diags.failParse(objectPath(wasm, object_id), "unknown section: {s}", .{
2567 wasm.stringSlice(section_name),
2568 });
2569 }
2570 },
2571 else => unreachable,
2572 }
2573}
2574
2575/// Appends a new segment with default field values
2576fn appendDummySegment(wasm: *Wasm) !void {
2577 const gpa = wasm.base.comp.gpa;
2578 try wasm.segments.append(gpa, .{
2579 .alignment = .@"1",
2580 .size = 0,
2581 .offset = 0,
2582 .flags = 0,
2583 });
2584}
2585
2586pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
2587 const comp = wasm.base.comp;
2588 const gpa = comp.gpa;
2589
2590 if (comp.verbose_link) {
2591 comp.mutex.lock(); // protect comp.arena
2592 defer comp.mutex.unlock();
2593
2594 const argv = &wasm.dump_argv_list;
2595 switch (input) {
2596 .res => unreachable,
2597 .dso_exact => unreachable,
2598 .dso => unreachable,
2599 .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)),
2600 }
2601 }
2602
2603 switch (input) {
2604 .res => unreachable,
2605 .dso_exact => unreachable,
2606 .dso => unreachable,
2607 .object => |obj| try parseObject(wasm, obj),
2608 .archive => |obj| try parseArchive(wasm, obj),
2609 }
2610}
2611
2612pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
2613 const comp = wasm.base.comp;
2614 const use_lld = build_options.have_llvm and comp.config.use_lld;
2615
2616 if (use_lld) {
2617 return wasm.linkWithLLD(arena, tid, prog_node);
2618 }
2619 return wasm.flushModule(arena, tid, prog_node);
2620}
2621
2622pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
2623 const tracy = trace(@src());
2624 defer tracy.end();
2625
2626 const comp = wasm.base.comp;
2627 const diags = &comp.link_diags;
2628 if (wasm.llvm_object) |llvm_object| {
2629 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
2630 const use_lld = build_options.have_llvm and comp.config.use_lld;
2631 if (use_lld) return;
2632 }
2633
2634 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
2635
2636 const sub_prog_node = prog_node.start("Wasm Flush", 0);
2637 defer sub_prog_node.end();
2638
2639 const module_obj_path: ?Path = if (wasm.base.zcu_object_sub_path) |path| .{
2640 .root_dir = wasm.base.emit.root_dir,
2641 .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname|
2642 try fs.path.join(arena, &.{ dirname, path })
2643 else
2644 path,
2645 } else null;
2646
2647 if (wasm.zig_object) |zig_object| try zig_object.flushModule(wasm, tid);
2648
2649 if (module_obj_path) |path| openParseObjectReportingFailure(wasm, path);
2650
2651 if (wasm.zig_object != null) {
2652 try wasm.resolveSymbolsInObject(.zig_object);
2653 }
2654 if (diags.hasErrors()) return error.FlushFailure;
2655 for (0..wasm.objects.items.len) |object_index| {
2656 try wasm.resolveSymbolsInObject(@enumFromInt(object_index));
2657 }
2658 if (diags.hasErrors()) return error.FlushFailure;
2659
2660 var emit_features_count: u32 = 0;
2661 var enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool = undefined;
2662 try wasm.validateFeatures(&enabled_features, &emit_features_count);
2663 try wasm.resolveSymbolsInArchives();
2664 if (diags.hasErrors()) return error.FlushFailure;
2665 try wasm.resolveLazySymbols();
2666 try wasm.checkUndefinedSymbols();
2667 try wasm.checkExportNames();
2668
2669 try wasm.setupInitFunctions();
2670 if (diags.hasErrors()) return error.FlushFailure;
2671 try wasm.setupStart();
2672
2673 try wasm.markReferences();
2674 try wasm.setupImports();
2675 try wasm.mergeSections();
2676 try wasm.mergeTypes();
2677 try wasm.allocateAtoms();
2678 try wasm.setupMemory();
2679 if (diags.hasErrors()) return error.FlushFailure;
2680 wasm.allocateVirtualAddresses();
2681 wasm.mapFunctionTable();
2682 try wasm.initializeCallCtorsFunction();
2683 try wasm.setupInitMemoryFunction();
2684 try wasm.setupTLSRelocationsFunction();
2685 try wasm.initializeTLSFunction();
2686 try wasm.setupStartSection();
2687 try wasm.setupExports();
2688 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2689 if (diags.hasErrors()) return error.FlushFailure;
2690}
2691
2692/// Writes the WebAssembly in-memory module to the file
2693fn writeToFile(
2694 wasm: *Wasm,
2695 enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool,
2696 feature_count: u32,
2697 arena: Allocator,
2698) !void {
2699 const comp = wasm.base.comp;
2700 const diags = &comp.link_diags;
2701 const gpa = comp.gpa;
2702 const use_llvm = comp.config.use_llvm;
2703 const use_lld = build_options.have_llvm and comp.config.use_lld;
2704 const shared_memory = comp.config.shared_memory;
2705 const import_memory = comp.config.import_memory;
2706 const export_memory = comp.config.export_memory;
2707
2708 // Size of each section header
2709 const header_size = 5 + 1;
2710 // The amount of sections that will be written
2711 var section_count: u32 = 0;
2712 // Index of the code section. Used to tell relocation table where the section lives.
2713 var code_section_index: ?u32 = null;
2714 // Index of the data section. Used to tell relocation table where the section lives.
2715 var data_section_index: ?u32 = null;
2716 const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld);
2717
2718 var binary_bytes = std.ArrayList(u8).init(gpa);
2719 defer binary_bytes.deinit();
2720 const binary_writer = binary_bytes.writer();
2721
2722 // We write the magic bytes at the end so they will only be written
2723 // if everything succeeded as expected. So populate with 0's for now.
2724 try binary_writer.writeAll(&[_]u8{0} ** 8);
2725 // (Re)set file pointer to 0
2726 try wasm.base.file.?.setEndPos(0);
2727 try wasm.base.file.?.seekTo(0);
2728
2729 // Type section
2730 if (wasm.func_types.items.len != 0) {
2731 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2732 log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len});
2733 for (wasm.func_types.items) |func_type| {
2734 try leb.writeUleb128(binary_writer, std.wasm.function_type);
2735 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.params.len)));
2736 for (func_type.params) |param_ty| {
2737 try leb.writeUleb128(binary_writer, std.wasm.valtype(param_ty));
2738 }
2739 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.returns.len)));
2740 for (func_type.returns) |ret_ty| {
2741 try leb.writeUleb128(binary_writer, std.wasm.valtype(ret_ty));
2742 }
2743 }
2744
2745 try writeVecSectionHeader(
2746 binary_bytes.items,
2747 header_offset,
2748 .type,
2749 @intCast(binary_bytes.items.len - header_offset - header_size),
2750 @intCast(wasm.func_types.items.len),
2751 );
2752 section_count += 1;
2753 }
2754
2755 // Import section
2756 if (wasm.imports.count() != 0 or import_memory) {
2757 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2758
2759 var it = wasm.imports.iterator();
2760 while (it.next()) |entry| {
2761 assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined());
2762 const import = entry.value_ptr.*;
2763 try wasm.emitImport(binary_writer, import);
2764 }
2765
2766 if (import_memory) {
2767 const mem_imp: Import = .{
2768 .module_name = wasm.host_name,
2769 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
2770 .kind = .{ .memory = wasm.memories.limits },
2771 };
2772 try wasm.emitImport(binary_writer, mem_imp);
2773 }
2774
2775 try writeVecSectionHeader(
2776 binary_bytes.items,
2777 header_offset,
2778 .import,
2779 @intCast(binary_bytes.items.len - header_offset - header_size),
2780 @intCast(wasm.imports.count() + @intFromBool(import_memory)),
2781 );
2782 section_count += 1;
2783 }
2784
2785 // Function section
2786 if (wasm.functions.count() != 0) {
2787 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2788 for (wasm.functions.values()) |function| {
2789 try leb.writeUleb128(binary_writer, function.func.type_index);
2790 }
2791
2792 try writeVecSectionHeader(
2793 binary_bytes.items,
2794 header_offset,
2795 .function,
2796 @intCast(binary_bytes.items.len - header_offset - header_size),
2797 @intCast(wasm.functions.count()),
2798 );
2799 section_count += 1;
2800 }
2801
2802 // Table section
2803 if (wasm.tables.items.len > 0) {
2804 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2805
2806 for (wasm.tables.items) |table| {
2807 try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));
2808 try emitLimits(binary_writer, table.limits);
2809 }
2810
2811 try writeVecSectionHeader(
2812 binary_bytes.items,
2813 header_offset,
2814 .table,
2815 @intCast(binary_bytes.items.len - header_offset - header_size),
2816 @intCast(wasm.tables.items.len),
2817 );
2818 section_count += 1;
2819 }
2820
2821 // Memory section
2822 if (!import_memory) {
2823 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2824
2825 try emitLimits(binary_writer, wasm.memories.limits);
2826 try writeVecSectionHeader(
2827 binary_bytes.items,
2828 header_offset,
2829 .memory,
2830 @intCast(binary_bytes.items.len - header_offset - header_size),
2831 1, // wasm currently only supports 1 linear memory segment
2832 );
2833 section_count += 1;
2834 }
2835
2836 // Global section (used to emit stack pointer)
2837 if (wasm.wasm_globals.items.len > 0) {
2838 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2839
2840 for (wasm.wasm_globals.items) |global| {
2841 try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype));
2842 try binary_writer.writeByte(@intFromBool(global.global_type.mutable));
2843 try emitInit(binary_writer, global.init);
2844 }
2845
2846 try writeVecSectionHeader(
2847 binary_bytes.items,
2848 header_offset,
2849 .global,
2850 @intCast(binary_bytes.items.len - header_offset - header_size),
2851 @intCast(wasm.wasm_globals.items.len),
2852 );
2853 section_count += 1;
2854 }
2855
2856 // Export section
2857 if (wasm.exports.items.len != 0 or export_memory) {
2858 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2859
2860 for (wasm.exports.items) |exp| {
2861 const name = wasm.stringSlice(exp.name);
2862 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
2863 try binary_writer.writeAll(name);
2864 try leb.writeUleb128(binary_writer, @intFromEnum(exp.kind));
2865 try leb.writeUleb128(binary_writer, exp.index);
2866 }
2867
2868 if (export_memory) {
2869 try leb.writeUleb128(binary_writer, @as(u32, @intCast("memory".len)));
2870 try binary_writer.writeAll("memory");
2871 try binary_writer.writeByte(std.wasm.externalKind(.memory));
2872 try leb.writeUleb128(binary_writer, @as(u32, 0));
2873 }
2874
2875 try writeVecSectionHeader(
2876 binary_bytes.items,
2877 header_offset,
2878 .@"export",
2879 @intCast(binary_bytes.items.len - header_offset - header_size),
2880 @intCast(wasm.exports.items.len + @intFromBool(export_memory)),
2881 );
2882 section_count += 1;
2883 }
2884
2885 if (wasm.entry) |entry_index| {
2886 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2887 try writeVecSectionHeader(
2888 binary_bytes.items,
2889 header_offset,
2890 .start,
2891 @intCast(binary_bytes.items.len - header_offset - header_size),
2892 entry_index,
2893 );
2894 }
2895
2896 // element section (function table)
2897 if (wasm.function_table.count() > 0) {
2898 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2899
2900 const table_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
2901 const table_sym = wasm.symbolLocSymbol(table_loc);
2902
2903 const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
2904 try leb.writeUleb128(binary_writer, flags);
2905 if (flags == 0x02) {
2906 try leb.writeUleb128(binary_writer, table_sym.index);
2907 }
2908 try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
2909 if (flags == 0x02) {
2910 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
2911 }
2912 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
2913 var symbol_it = wasm.function_table.keyIterator();
2914 while (symbol_it.next()) |symbol_loc_ptr| {
2915 const sym = wasm.symbolLocSymbol(symbol_loc_ptr.*);
2916 std.debug.assert(sym.isAlive());
2917 std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);
2918 try leb.writeUleb128(binary_writer, sym.index);
2919 }
2920
2921 try writeVecSectionHeader(
2922 binary_bytes.items,
2923 header_offset,
2924 .element,
2925 @intCast(binary_bytes.items.len - header_offset - header_size),
2926 1,
2927 );
2928 section_count += 1;
2929 }
2930
2931 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
2932 const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory);
2933 if (data_segments_count != 0 and shared_memory) {
2934 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2935 try writeVecSectionHeader(
2936 binary_bytes.items,
2937 header_offset,
2938 .data_count,
2939 @intCast(binary_bytes.items.len - header_offset - header_size),
2940 @intCast(data_segments_count),
2941 );
2942 }
2943
2944 // Code section
2945 if (wasm.code_section_index != .none) {
2946 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2947 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
2948
2949 var func_it = wasm.functions.iterator();
2950 while (func_it.next()) |entry| {
2951 const sym_loc: SymbolLoc = .{ .index = entry.value_ptr.sym_index, .file = entry.key_ptr.file };
2952 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2953 const atom = wasm.getAtomPtr(atom_index);
2954
2955 if (!is_obj) {
2956 atom.resolveRelocs(wasm);
2957 }
2958 atom.offset = @intCast(binary_bytes.items.len - start_offset);
2959 try leb.writeUleb128(binary_writer, atom.size);
2960 try binary_writer.writeAll(atom.code.items);
2961 }
2962
2963 try writeVecSectionHeader(
2964 binary_bytes.items,
2965 header_offset,
2966 .code,
2967 @intCast(binary_bytes.items.len - header_offset - header_size),
2968 @intCast(wasm.functions.count()),
2969 );
2970 code_section_index = section_count;
2971 section_count += 1;
2972 }
2973
2974 // Data section
2975 if (data_segments_count != 0) {
2976 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2977
2978 var it = wasm.data_segments.iterator();
2979 var segment_count: u32 = 0;
2980 while (it.next()) |entry| {
2981 // do not output 'bss' section unless we import memory and therefore
2982 // want to guarantee the data is zero initialized
2983 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
2984 const segment_index = entry.value_ptr.*;
2985 const segment = wasm.segmentPtr(segment_index);
2986 if (segment.size == 0) continue; // do not emit empty segments
2987 segment_count += 1;
2988 var atom_index = wasm.atoms.get(segment_index).?;
2989
2990 try leb.writeUleb128(binary_writer, segment.flags);
2991 if (segment.flags & @intFromEnum(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) {
2992 try leb.writeUleb128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry
2993 }
2994 // when a segment is passive, it's initialized during runtime.
2995 if (!segment.isPassive()) {
2996 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment.offset)) });
2997 }
2998 // offset into data section
2999 try leb.writeUleb128(binary_writer, segment.size);
3000
3001 // fill in the offset table and the data segments
3002 var current_offset: u32 = 0;
3003 while (true) {
3004 const atom = wasm.getAtomPtr(atom_index);
3005 if (!is_obj) {
3006 atom.resolveRelocs(wasm);
3007 }
3008
3009 // Pad with zeroes to ensure all segments are aligned
3010 if (current_offset != atom.offset) {
3011 const diff = atom.offset - current_offset;
3012 try binary_writer.writeByteNTimes(0, diff);
3013 current_offset += diff;
3014 }
3015 assert(current_offset == atom.offset);
3016 assert(atom.code.items.len == atom.size);
3017 try binary_writer.writeAll(atom.code.items);
3018
3019 current_offset += atom.size;
3020 if (atom.prev != .null) {
3021 atom_index = atom.prev;
3022 } else {
3023 // also pad with zeroes when last atom to ensure
3024 // segments are aligned.
3025 if (current_offset != segment.size) {
3026 try binary_writer.writeByteNTimes(0, segment.size - current_offset);
3027 current_offset += segment.size - current_offset;
3028 }
3029 break;
3030 }
3031 }
3032 assert(current_offset == segment.size);
3033 }
3034
3035 try writeVecSectionHeader(
3036 binary_bytes.items,
3037 header_offset,
3038 .data,
3039 @intCast(binary_bytes.items.len - header_offset - header_size),
3040 @intCast(segment_count),
3041 );
3042 data_section_index = section_count;
3043 section_count += 1;
3044 }
3045
3046 if (is_obj) {
3047 // relocations need to point to the index of a symbol in the final symbol table. To save memory,
3048 // we never store all symbols in a single table, but store a location reference instead.
3049 // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
3050 var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
3051 try wasm.emitLinkSection(&binary_bytes, &symbol_table);
3052 if (code_section_index) |code_index| {
3053 try wasm.emitCodeRelocations(&binary_bytes, code_index, symbol_table);
3054 }
3055 if (data_section_index) |data_index| {
3056 try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table);
3057 }
3058 } else if (comp.config.debug_format != .strip) {
3059 try wasm.emitNameSection(&binary_bytes, arena);
3060 }
3061
3062 if (comp.config.debug_format != .strip) {
3063 // The build id must be computed on the main sections only,
3064 // so we have to do it now, before the debug sections.
3065 switch (wasm.base.build_id) {
3066 .none => {},
3067 .fast => {
3068 var id: [16]u8 = undefined;
3069 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
3070 var uuid: [36]u8 = undefined;
3071 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
3072 std.fmt.fmtSliceHexLower(id[0..4]),
3073 std.fmt.fmtSliceHexLower(id[4..6]),
3074 std.fmt.fmtSliceHexLower(id[6..8]),
3075 std.fmt.fmtSliceHexLower(id[8..10]),
3076 std.fmt.fmtSliceHexLower(id[10..]),
3077 });
3078 try emitBuildIdSection(&binary_bytes, &uuid);
3079 },
3080 .hexstring => |hs| {
3081 var buffer: [32 * 2]u8 = undefined;
3082 const str = std.fmt.bufPrint(&buffer, "{s}", .{
3083 std.fmt.fmtSliceHexLower(hs.toSlice()),
3084 }) catch unreachable;
3085 try emitBuildIdSection(&binary_bytes, str);
3086 },
3087 else => |mode| {
3088 var err = try diags.addErrorWithNotes(0);
3089 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
3090 },
3091 }
3092
3093 var debug_bytes = std.ArrayList(u8).init(gpa);
3094 defer debug_bytes.deinit();
3095
3096 inline for (@typeInfo(CustomSections).@"struct".fields) |field| {
3097 if (@field(wasm.custom_sections, field.name).index.unwrap()) |index| {
3098 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
3099 while (true) {
3100 atom.resolveRelocs(wasm);
3101 try debug_bytes.appendSlice(atom.code.items);
3102 if (atom.prev == .null) break;
3103 atom = wasm.getAtomPtr(atom.prev);
3104 }
3105 try emitDebugSection(&binary_bytes, debug_bytes.items, field.name);
3106 debug_bytes.clearRetainingCapacity();
3107 }
3108 }
3109
3110 try emitProducerSection(&binary_bytes);
3111 if (feature_count > 0) {
3112 try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count);
3113 }
3114 }
3115
3116 // Only when writing all sections executed properly we write the magic
3117 // bytes. This allows us to easily detect what went wrong while generating
3118 // the final binary.
3119 {
3120 const src = std.wasm.magic ++ std.wasm.version;
3121 binary_bytes.items[0..src.len].* = src;
3122 }
3123
3124 // finally, write the entire binary into the file.
3125 var iovec = [_]std.posix.iovec_const{.{
3126 .base = binary_bytes.items.ptr,
3127 .len = binary_bytes.items.len,
3128 }};
3129 try wasm.base.file.?.writevAll(&iovec);
3130}
3131
3132fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void {
3133 if (data.len == 0) return;
3134 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3135 const writer = binary_bytes.writer();
3136 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
3137 try writer.writeAll(name);
3138
3139 const start = binary_bytes.items.len - header_offset;
3140 log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len });
3141 try writer.writeAll(data);
3142
3143 try writeCustomSectionHeader(
3144 binary_bytes.items,
3145 header_offset,
3146 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3147 );
3148}
3149
3150fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
3151 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3152
3153 const writer = binary_bytes.writer();
3154 const producers = "producers";
3155 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));
3156 try writer.writeAll(producers);
3157
3158 try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by
3159
3160 // used for the Zig version
3161 var version_buf: [100]u8 = undefined;
3162 const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver});
3163
3164 // language field
3165 {
3166 const language = "language";
3167 try leb.writeUleb128(writer, @as(u32, @intCast(language.len)));
3168 try writer.writeAll(language);
3169
3170 // field_value_count (TODO: Parse object files for producer sections to detect their language)
3171 try leb.writeUleb128(writer, @as(u32, 1));
3172
3173 // versioned name
3174 {
3175 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
3176 try writer.writeAll("Zig");
3177
3178 try leb.writeUleb128(writer, @as(u32, @intCast(version.len)));
3179 try writer.writeAll(version);
3180 }
3181 }
3182
3183 // processed-by field
3184 {
3185 const processed_by = "processed-by";
3186 try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len)));
3187 try writer.writeAll(processed_by);
3188
3189 // field_value_count (TODO: Parse object files for producer sections to detect other used tools)
3190 try leb.writeUleb128(writer, @as(u32, 1));
3191
3192 // versioned name
3193 {
3194 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
3195 try writer.writeAll("Zig");
3196
3197 try leb.writeUleb128(writer, @as(u32, @intCast(version.len)));
3198 try writer.writeAll(version);
3199 }
3200 }
3201
3202 try writeCustomSectionHeader(
3203 binary_bytes.items,
3204 header_offset,
3205 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3206 );
3207}
3208
3209fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8), build_id: []const u8) !void {
3210 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3211
3212 const writer = binary_bytes.writer();
3213 const hdr_build_id = "build_id";
3214 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));
3215 try writer.writeAll(hdr_build_id);
3216
3217 try leb.writeUleb128(writer, @as(u32, 1));
3218 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));
3219 try writer.writeAll(build_id);
3220
3221 try writeCustomSectionHeader(
3222 binary_bytes.items,
3223 header_offset,
3224 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3225 );
3226}
3227
3228fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {
3229 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3230
3231 const writer = binary_bytes.writer();
3232 const target_features = "target_features";
3233 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));
3234 try writer.writeAll(target_features);
3235
3236 try leb.writeUleb128(writer, features_count);
3237 for (enabled_features, 0..) |enabled, feature_index| {
3238 if (enabled) {
3239 const feature: Feature = .{ .prefix = .used, .tag = @as(Feature.Tag, @enumFromInt(feature_index)) };
3240 try leb.writeUleb128(writer, @intFromEnum(feature.prefix));
3241 var buf: [100]u8 = undefined;
3242 const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag});
3243 try leb.writeUleb128(writer, @as(u32, @intCast(string.len)));
3244 try writer.writeAll(string);
3245 }
3246 }
3247
3248 try writeCustomSectionHeader(
3249 binary_bytes.items,
3250 header_offset,
3251 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3252 );
3253}
3254
3255fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {
3256 const comp = wasm.base.comp;
3257 const import_memory = comp.config.import_memory;
3258 const Name = struct {
3259 index: u32,
3260 name: []const u8,
3261
3262 fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
3263 _ = context;
3264 return lhs.index < rhs.index;
3265 }
3266 };
3267
3268 // we must de-duplicate symbols that point to the same function
3269 var funcs = std.AutoArrayHashMap(u32, Name).init(arena);
3270 try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count);
3271 var globals = try std.ArrayList(Name).initCapacity(arena, wasm.wasm_globals.items.len + wasm.imported_globals_count);
3272 var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count());
3273
3274 for (wasm.resolved_symbols.keys()) |sym_loc| {
3275 const symbol = wasm.symbolLocSymbol(sym_loc).*;
3276 if (symbol.isDead()) {
3277 continue;
3278 }
3279 const name = wasm.symbolLocName(sym_loc);
3280 switch (symbol.tag) {
3281 .function => {
3282 const gop = funcs.getOrPutAssumeCapacity(symbol.index);
3283 if (!gop.found_existing) {
3284 gop.value_ptr.* = .{ .index = symbol.index, .name = name };
3285 }
3286 },
3287 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }),
3288 else => {},
3289 }
3290 }
3291 // data segments are already 'ordered'
3292 var data_segment_index: u32 = 0;
3293 for (wasm.data_segments.keys()) |key| {
3294 // bss section is not emitted when this condition holds true, so we also
3295 // do not output a name for it.
3296 if (!import_memory and std.mem.eql(u8, key, ".bss")) continue;
3297 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
3298 data_segment_index += 1;
3299 }
3300
3301 mem.sort(Name, funcs.values(), {}, Name.lessThan);
3302 mem.sort(Name, globals.items, {}, Name.lessThan);
3303
3304 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3305 const writer = binary_bytes.writer();
3306 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));
3307 try writer.writeAll("name");
3308
3309 try wasm.emitNameSubsection(.function, funcs.values(), writer);
3310 try wasm.emitNameSubsection(.global, globals.items, writer);
3311 try wasm.emitNameSubsection(.data_segment, segments.items, writer);
3312
3313 try writeCustomSectionHeader(
3314 binary_bytes.items,
3315 header_offset,
3316 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
3317 );
3318}
3319
3320fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
3321 const gpa = wasm.base.comp.gpa;
33221698
3323 // We must emit subsection size, so first write to a temporary list1699 if (wasm.llvm_object) |llvm_object| {
3324 var section_list = std.ArrayList(u8).init(gpa);1700 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
3325 defer section_list.deinit();1701 if (use_lld) return;
3326 const sub_writer = section_list.writer();
3327
3328 try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len)));
3329 for (names) |name| {
3330 log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) });
3331 try leb.writeUleb128(sub_writer, name.index);
3332 try leb.writeUleb128(sub_writer, @as(u32, @intCast(name.name.len)));
3333 try sub_writer.writeAll(name.name);
3334 }1702 }
33351703
3336 // From now, write to the actual writer1704 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
3337 try leb.writeUleb128(writer, @intFromEnum(section_id));
3338 try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len)));
3339 try writer.writeAll(section_list.items);
3340}
33411705
3342fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {1706 if (wasm.base.zcu_object_sub_path) |path| {
3343 try writer.writeByte(limits.flags);1707 const module_obj_path: Path = .{
3344 try leb.writeUleb128(writer, limits.min);1708 .root_dir = wasm.base.emit.root_dir,
3345 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {1709 .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname|
3346 try leb.writeUleb128(writer, limits.max);1710 try fs.path.join(arena, &.{ dirname, path })
1711 else
1712 path,
1713 };
1714 openParseObjectReportingFailure(wasm, module_obj_path);
1715 try prelink(wasm, prog_node);
3347 }1716 }
3348}
33491717
3350fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {1718 const tracy = trace(@src());
3351 switch (init_expr) {1719 defer tracy.end();
3352 .i32_const => |val| {
3353 try writer.writeByte(std.wasm.opcode(.i32_const));
3354 try leb.writeIleb128(writer, val);
3355 },
3356 .i64_const => |val| {
3357 try writer.writeByte(std.wasm.opcode(.i64_const));
3358 try leb.writeIleb128(writer, val);
3359 },
3360 .f32_const => |val| {
3361 try writer.writeByte(std.wasm.opcode(.f32_const));
3362 try writer.writeInt(u32, @bitCast(val), .little);
3363 },
3364 .f64_const => |val| {
3365 try writer.writeByte(std.wasm.opcode(.f64_const));
3366 try writer.writeInt(u64, @bitCast(val), .little);
3367 },
3368 .global_get => |val| {
3369 try writer.writeByte(std.wasm.opcode(.global_get));
3370 try leb.writeUleb128(writer, val);
3371 },
3372 }
3373 try writer.writeByte(std.wasm.opcode(.end));
3374}
33751720
3376fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void {1721 const sub_prog_node = prog_node.start("Wasm Flush", 0);
3377 const module_name = wasm.stringSlice(import.module_name);1722 defer sub_prog_node.end();
3378 try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));1723
3379 try writer.writeAll(module_name);1724 wasm.flush_buffer.clear();
33801725 defer wasm.flush_buffer.subsequent = true;
3381 const name = wasm.stringSlice(import.name);1726 return wasm.flush_buffer.finish(wasm, arena);
3382 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
3383 try writer.writeAll(name);
3384
3385 try writer.writeByte(@intFromEnum(import.kind));
3386 switch (import.kind) {
3387 .function => |type_index| try leb.writeUleb128(writer, type_index),
3388 .global => |global_type| {
3389 try leb.writeUleb128(writer, std.wasm.valtype(global_type.valtype));
3390 try writer.writeByte(@intFromBool(global_type.mutable));
3391 },
3392 .table => |table| {
3393 try leb.writeUleb128(writer, std.wasm.reftype(table.reftype));
3394 try emitLimits(writer, table.limits);
3395 },
3396 .memory => |limits| {
3397 try emitLimits(writer, limits);
3398 },
3399 }
3400}1727}
34011728
3402fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {1729fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
...@@ -3811,281 +2138,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -3811,281 +2138,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
3811 }2138 }
3812}2139}
38132140
3814fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
3815 // section id + fixed leb contents size + fixed leb vector length
3816 const header_size = 1 + 5 + 5;
3817 const offset = @as(u32, @intCast(bytes.items.len));
3818 try bytes.appendSlice(&[_]u8{0} ** header_size);
3819 return offset;
3820}
3821
3822fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
3823 // unlike regular section, we don't emit the count
3824 const header_size = 1 + 5;
3825 const offset = @as(u32, @intCast(bytes.items.len));
3826 try bytes.appendSlice(&[_]u8{0} ** header_size);
3827 return offset;
3828}
3829
3830fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {
3831 var buf: [1 + 5 + 5]u8 = undefined;
3832 buf[0] = @intFromEnum(section);
3833 leb.writeUnsignedFixed(5, buf[1..6], size);
3834 leb.writeUnsignedFixed(5, buf[6..], items);
3835 buffer[offset..][0..buf.len].* = buf;
3836}
3837
3838fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
3839 var buf: [1 + 5]u8 = undefined;
3840 buf[0] = 0; // 0 = 'custom' section
3841 leb.writeUnsignedFixed(5, buf[1..6], size);
3842 buffer[offset..][0..buf.len].* = buf;
3843}
3844
3845fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3846 const offset = try reserveCustomSectionHeader(binary_bytes);
3847 const writer = binary_bytes.writer();
3848 // emit "linking" custom section name
3849 const section_name = "linking";
3850 try leb.writeUleb128(writer, section_name.len);
3851 try writer.writeAll(section_name);
3852
3853 // meta data version, which is currently '2'
3854 try leb.writeUleb128(writer, @as(u32, 2));
3855
3856 // For each subsection type (found in Subsection) we can emit a section.
3857 // Currently, we only support emitting segment info and the symbol table.
3858 try wasm.emitSymbolTable(binary_bytes, symbol_table);
3859 try wasm.emitSegmentInfo(binary_bytes);
3860
3861 const size: u32 = @intCast(binary_bytes.items.len - offset - 6);
3862 try writeCustomSectionHeader(binary_bytes.items, offset, size);
3863}
3864
3865fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
3866 const writer = binary_bytes.writer();
3867
3868 try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE));
3869 const table_offset = binary_bytes.items.len;
3870
3871 var symbol_count: u32 = 0;
3872 for (wasm.resolved_symbols.keys()) |sym_loc| {
3873 const symbol = wasm.symbolLocSymbol(sym_loc).*;
3874 if (symbol.tag == .dead) continue; // Do not emit dead symbols
3875 try symbol_table.putNoClobber(sym_loc, symbol_count);
3876 symbol_count += 1;
3877 log.debug("Emit symbol: {}", .{symbol});
3878 try leb.writeUleb128(writer, @intFromEnum(symbol.tag));
3879 try leb.writeUleb128(writer, symbol.flags);
3880
3881 const sym_name = wasm.symbolLocName(sym_loc);
3882 switch (symbol.tag) {
3883 .data => {
3884 try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
3885 try writer.writeAll(sym_name);
3886
3887 if (symbol.isDefined()) {
3888 try leb.writeUleb128(writer, symbol.index);
3889 const atom_index = wasm.symbol_atom.get(sym_loc).?;
3890 const atom = wasm.getAtom(atom_index);
3891 try leb.writeUleb128(writer, @as(u32, atom.offset));
3892 try leb.writeUleb128(writer, @as(u32, atom.size));
3893 }
3894 },
3895 .section => {
3896 try leb.writeUleb128(writer, symbol.index);
3897 },
3898 else => {
3899 try leb.writeUleb128(writer, symbol.index);
3900 if (symbol.isDefined()) {
3901 try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
3902 try writer.writeAll(sym_name);
3903 }
3904 },
3905 }
3906 }
3907
3908 var buf: [10]u8 = undefined;
3909 leb.writeUnsignedFixed(5, buf[0..5], @intCast(binary_bytes.items.len - table_offset + 5));
3910 leb.writeUnsignedFixed(5, buf[5..], symbol_count);
3911 try binary_bytes.insertSlice(table_offset, &buf);
3912}
3913
3914fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
3915 const writer = binary_bytes.writer();
3916 try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO));
3917 const segment_offset = binary_bytes.items.len;
3918
3919 try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count())));
3920 for (wasm.segment_info.values()) |segment_info| {
3921 log.debug("Emit segment: {s} align({d}) flags({b})", .{
3922 segment_info.name,
3923 segment_info.alignment,
3924 segment_info.flags,
3925 });
3926 try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len)));
3927 try writer.writeAll(segment_info.name);
3928 try leb.writeUleb128(writer, segment_info.alignment.toLog2Units());
3929 try leb.writeUleb128(writer, segment_info.flags);
3930 }
3931
3932 var buf: [5]u8 = undefined;
3933 leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset)));
3934 try binary_bytes.insertSlice(segment_offset, &buf);
3935}
3936
3937pub fn getUleb128Size(uint_value: anytype) u32 {
3938 const T = @TypeOf(uint_value);
3939 const U = if (@typeInfo(T).int.bits < 8) u8 else T;
3940 var value = @as(U, @intCast(uint_value));
3941
3942 var size: u32 = 0;
3943 while (value != 0) : (size += 1) {
3944 value >>= 7;
3945 }
3946 return size;
3947}
3948
3949/// For each relocatable section, emits a custom "relocation.<section_name>" section
3950fn emitCodeRelocations(
3951 wasm: *Wasm,
3952 binary_bytes: *std.ArrayList(u8),
3953 section_index: u32,
3954 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
3955) !void {
3956 const code_index = wasm.code_section_index.unwrap() orelse return;
3957 const writer = binary_bytes.writer();
3958 const header_offset = try reserveCustomSectionHeader(binary_bytes);
3959
3960 // write custom section information
3961 const name = "reloc.CODE";
3962 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
3963 try writer.writeAll(name);
3964 try leb.writeUleb128(writer, section_index);
3965 const reloc_start = binary_bytes.items.len;
3966
3967 var count: u32 = 0;
3968 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?);
3969 // for each atom, we calculate the uleb size and append that
3970 var size_offset: u32 = 5; // account for code section size leb128
3971 while (true) {
3972 size_offset += getUleb128Size(atom.size);
3973 for (atom.relocs.items) |relocation| {
3974 count += 1;
3975 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
3976 const symbol_index = symbol_table.get(sym_loc).?;
3977 try leb.writeUleb128(writer, @intFromEnum(relocation.relocation_type));
3978 const offset = atom.offset + relocation.offset + size_offset;
3979 try leb.writeUleb128(writer, offset);
3980 try leb.writeUleb128(writer, symbol_index);
3981 if (relocation.relocation_type.addendIsPresent()) {
3982 try leb.writeIleb128(writer, relocation.addend);
3983 }
3984 log.debug("Emit relocation: {}", .{relocation});
3985 }
3986 if (atom.prev == .null) break;
3987 atom = wasm.getAtomPtr(atom.prev);
3988 }
3989 if (count == 0) return;
3990 var buf: [5]u8 = undefined;
3991 leb.writeUnsignedFixed(5, &buf, count);
3992 try binary_bytes.insertSlice(reloc_start, &buf);
3993 const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6);
3994 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
3995}
3996
3997fn emitDataRelocations(
3998 wasm: *Wasm,
3999 binary_bytes: *std.ArrayList(u8),
4000 section_index: u32,
4001 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
4002) !void {
4003 if (wasm.data_segments.count() == 0) return;
4004 const writer = binary_bytes.writer();
4005 const header_offset = try reserveCustomSectionHeader(binary_bytes);
4006
4007 // write custom section information
4008 const name = "reloc.DATA";
4009 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
4010 try writer.writeAll(name);
4011 try leb.writeUleb128(writer, section_index);
4012 const reloc_start = binary_bytes.items.len;
4013
4014 var count: u32 = 0;
4015 // for each atom, we calculate the uleb size and append that
4016 var size_offset: u32 = 5; // account for code section size leb128
4017 for (wasm.data_segments.values()) |segment_index| {
4018 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?);
4019 while (true) {
4020 size_offset += getUleb128Size(atom.size);
4021 for (atom.relocs.items) |relocation| {
4022 count += 1;
4023 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
4024 const symbol_index = symbol_table.get(sym_loc).?;
4025 try leb.writeUleb128(writer, @intFromEnum(relocation.relocation_type));
4026 const offset = atom.offset + relocation.offset + size_offset;
4027 try leb.writeUleb128(writer, offset);
4028 try leb.writeUleb128(writer, symbol_index);
4029 if (relocation.relocation_type.addendIsPresent()) {
4030 try leb.writeIleb128(writer, relocation.addend);
4031 }
4032 log.debug("Emit relocation: {}", .{relocation});
4033 }
4034 if (atom.prev == .null) break;
4035 atom = wasm.getAtomPtr(atom.prev);
4036 }
4037 }
4038 if (count == 0) return;
4039
4040 var buf: [5]u8 = undefined;
4041 leb.writeUnsignedFixed(5, &buf, count);
4042 try binary_bytes.insertSlice(reloc_start, &buf);
4043 const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6));
4044 try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
4045}
4046
4047fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
4048 const comp = wasm.base.comp;
4049 const import_memory = comp.config.import_memory;
4050
4051 var it = wasm.data_segments.iterator();
4052 while (it.next()) |entry| {
4053 const segment = wasm.segmentPtr(entry.value_ptr.*);
4054 if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {
4055 return true;
4056 }
4057 }
4058 return false;
4059}
4060
4061/// Searches for a matching function signature. When no matching signature is found,
4062/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
4063pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
4064 if (wasm.getTypeIndex(func_type)) |index| {
4065 return index;
4066 }
4067
4068 // functype does not exist.
4069 const gpa = wasm.base.comp.gpa;
4070 const index: u32 = @intCast(wasm.func_types.items.len);
4071 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
4072 errdefer gpa.free(params);
4073 const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
4074 errdefer gpa.free(returns);
4075 try wasm.func_types.append(gpa, .{
4076 .params = params,
4077 .returns = returns,
4078 });
4079 return index;
4080}
4081
4082/// For the given `nav`, stores the corresponding type representing the function signature.
4083/// Asserts declaration has an associated `Atom`.
4084/// Returns the index into the list of types.
4085pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {
4086 return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type);
4087}
4088
4089/// Returns the symbol index of the error name table.2141/// Returns the symbol index of the error name table.
4090///2142///
4091/// When the symbol does not yet exist, it will create a new one instead.2143/// When the symbol does not yet exist, it will create a new one instead.
...@@ -4094,69 +2146,6 @@ pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {...@@ -4094,69 +2146,6 @@ pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {
4094 return @intFromEnum(sym_index);2146 return @intFromEnum(sym_index);
4095}2147}
40962148
4097/// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
4098/// When the index was not found, a new `Atom` will be created, and its index will be returned.
4099/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
4100pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index {
4101 return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav);
4102}
4103
4104/// Verifies all resolved symbols and checks whether itself needs to be marked alive,
4105/// as well as any of its references.
4106fn markReferences(wasm: *Wasm) !void {
4107 const tracy = trace(@src());
4108 defer tracy.end();
4109
4110 const do_garbage_collect = wasm.base.gc_sections;
4111 const comp = wasm.base.comp;
4112
4113 for (wasm.resolved_symbols.keys()) |sym_loc| {
4114 const sym = wasm.symbolLocSymbol(sym_loc);
4115 if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
4116 try wasm.mark(sym_loc);
4117 continue;
4118 }
4119
4120 // Debug sections may require to be parsed and marked when it contains
4121 // relocations to alive symbols.
4122 if (sym.tag == .section and comp.config.debug_format != .strip) {
4123 const object_id = sym_loc.file.unwrap() orelse continue; // Incremental debug info is done independently
4124 _ = try wasm.parseSymbolIntoAtom(object_id, sym_loc.index);
4125 sym.mark();
4126 }
4127 }
4128}
4129
4130/// Marks a symbol as 'alive' recursively so itself and any references it contains to
4131/// other symbols will not be omit from the binary.
4132fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
4133 const symbol = wasm.symbolLocSymbol(loc);
4134 if (symbol.isAlive()) {
4135 // Symbol is already marked alive, including its references.
4136 // This means we can skip it so we don't end up marking the same symbols
4137 // multiple times.
4138 return;
4139 }
4140 symbol.mark();
4141 gc_log.debug("Marked symbol '{s}'", .{wasm.symbolLocName(loc)});
4142 if (symbol.isUndefined()) {
4143 // undefined symbols do not have an associated `Atom` and therefore also
4144 // do not contain relocations.
4145 return;
4146 }
4147
4148 const atom_index = if (loc.file.unwrap()) |object_id|
4149 try wasm.parseSymbolIntoAtom(object_id, loc.index)
4150 else
4151 wasm.symbol_atom.get(loc) orelse return;
4152
4153 const atom = wasm.getAtom(atom_index);
4154 for (atom.relocs.items) |reloc| {
4155 const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file };
4156 try wasm.mark(wasm.symbolLocFinalLoc(target_loc));
4157 }
4158}
4159
4160fn defaultEntrySymbolName(2149fn defaultEntrySymbolName(
4161 preloaded_strings: *const PreloadedStrings,2150 preloaded_strings: *const PreloadedStrings,
4162 wasi_exec_model: std.builtin.WasiExecModel,2151 wasi_exec_model: std.builtin.WasiExecModel,
...@@ -4167,573 +2156,8 @@ fn defaultEntrySymbolName(...@@ -4167,573 +2156,8 @@ fn defaultEntrySymbolName(
4167 };2156 };
4168}2157}
41692158
4170pub const Atom = struct {
4171 /// Represents the index of the file this atom was generated from.
4172 /// This is `none` when the atom was generated by a synthetic linker symbol.
4173 file: OptionalObjectId,
4174 /// symbol index of the symbol representing this atom
4175 sym_index: Symbol.Index,
4176 /// Size of the atom, used to calculate section sizes in the final binary
4177 size: u32 = 0,
4178 /// List of relocations belonging to this atom
4179 relocs: std.ArrayListUnmanaged(Relocation) = .empty,
4180 /// Contains the binary data of an atom, which can be non-relocated
4181 code: std.ArrayListUnmanaged(u8) = .empty,
4182 /// For code this is 1, for data this is set to the highest value of all segments
4183 alignment: Wasm.Alignment = .@"1",
4184 /// Offset into the section where the atom lives, this already accounts
4185 /// for alignment.
4186 offset: u32 = 0,
4187 /// The original offset within the object file. This value is subtracted from
4188 /// relocation offsets to determine where in the `data` to rewrite the value
4189 original_offset: u32 = 0,
4190 /// Previous atom in relation to this atom.
4191 /// is null when this atom is the first in its order
4192 prev: Atom.Index = .null,
4193 /// Contains atoms local to a decl, all managed by this `Atom`.
4194 /// When the parent atom is being freed, it will also do so for all local atoms.
4195 locals: std.ArrayListUnmanaged(Atom.Index) = .empty,
4196
4197 /// Represents the index of an Atom where `null` is considered
4198 /// an invalid atom.
4199 pub const Index = enum(u32) {
4200 null = std.math.maxInt(u32),
4201 _,
4202 };
4203
4204 /// Frees all resources owned by this `Atom`.
4205 pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
4206 atom.relocs.deinit(gpa);
4207 atom.code.deinit(gpa);
4208 atom.locals.deinit(gpa);
4209 atom.* = undefined;
4210 }
4211
4212 /// Sets the length of relocations and code to '0',
4213 /// effectively resetting them and allowing them to be re-populated.
4214 pub fn clear(atom: *Atom) void {
4215 atom.relocs.clearRetainingCapacity();
4216 atom.code.clearRetainingCapacity();
4217 }
4218
4219 pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
4220 _ = fmt;
4221 _ = options;
4222 try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
4223 @intFromEnum(atom.sym_index),
4224 atom.alignment,
4225 atom.size,
4226 atom.offset,
4227 });
4228 }
4229
4230 /// Returns the location of the symbol that represents this `Atom`
4231 pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
4232 return .{
4233 .file = atom.file,
4234 .index = atom.sym_index,
4235 };
4236 }
4237
4238 /// Resolves the relocations within the atom, writing the new value
4239 /// at the calculated offset.
4240 pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void {
4241 if (atom.relocs.items.len == 0) return;
4242 const symbol_name = wasm.symbolLocName(atom.symbolLoc());
4243 log.debug("Resolving relocs in atom '{s}' count({d})", .{
4244 symbol_name,
4245 atom.relocs.items.len,
4246 });
4247
4248 for (atom.relocs.items) |reloc| {
4249 const value = atom.relocationValue(reloc, wasm);
4250 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
4251 wasm.symbolLocName(.{
4252 .file = atom.file,
4253 .index = @enumFromInt(reloc.index),
4254 }),
4255 symbol_name,
4256 reloc.offset,
4257 value,
4258 });
4259
4260 switch (reloc.relocation_type) {
4261 .R_WASM_TABLE_INDEX_I32,
4262 .R_WASM_FUNCTION_OFFSET_I32,
4263 .R_WASM_GLOBAL_INDEX_I32,
4264 .R_WASM_MEMORY_ADDR_I32,
4265 .R_WASM_SECTION_OFFSET_I32,
4266 => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
4267 .R_WASM_TABLE_INDEX_I64,
4268 .R_WASM_MEMORY_ADDR_I64,
4269 => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little),
4270 .R_WASM_GLOBAL_INDEX_LEB,
4271 .R_WASM_EVENT_INDEX_LEB,
4272 .R_WASM_FUNCTION_INDEX_LEB,
4273 .R_WASM_MEMORY_ADDR_LEB,
4274 .R_WASM_MEMORY_ADDR_SLEB,
4275 .R_WASM_TABLE_INDEX_SLEB,
4276 .R_WASM_TABLE_NUMBER_LEB,
4277 .R_WASM_TYPE_INDEX_LEB,
4278 .R_WASM_MEMORY_ADDR_TLS_SLEB,
4279 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
4280 .R_WASM_MEMORY_ADDR_LEB64,
4281 .R_WASM_MEMORY_ADDR_SLEB64,
4282 .R_WASM_TABLE_INDEX_SLEB64,
4283 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
4284 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value),
4285 }
4286 }
4287 }
4288
4289 /// From a given `relocation` will return the new value to be written.
4290 /// All values will be represented as a `u64` as all values can fit within it.
4291 /// The final value must be casted to the correct size.
4292 fn relocationValue(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 {
4293 const target_loc = wasm.symbolLocFinalLoc(.{
4294 .file = atom.file,
4295 .index = @enumFromInt(relocation.index),
4296 });
4297 const symbol = wasm.symbolLocSymbol(target_loc);
4298 if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
4299 symbol.tag != .section and
4300 symbol.isDead())
4301 {
4302 const val = atom.tombstone(wasm) orelse relocation.addend;
4303 return @bitCast(val);
4304 }
4305 switch (relocation.relocation_type) {
4306 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
4307 .R_WASM_TABLE_NUMBER_LEB => return symbol.index,
4308 .R_WASM_TABLE_INDEX_I32,
4309 .R_WASM_TABLE_INDEX_I64,
4310 .R_WASM_TABLE_INDEX_SLEB,
4311 .R_WASM_TABLE_INDEX_SLEB64,
4312 => return wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
4313 .R_WASM_TYPE_INDEX_LEB => {
4314 const object_id = atom.file.unwrap() orelse return relocation.index;
4315 const original_type = objectFuncTypes(wasm, object_id)[relocation.index];
4316 return wasm.getTypeIndex(original_type).?;
4317 },
4318 .R_WASM_GLOBAL_INDEX_I32,
4319 .R_WASM_GLOBAL_INDEX_LEB,
4320 => return symbol.index,
4321 .R_WASM_MEMORY_ADDR_I32,
4322 .R_WASM_MEMORY_ADDR_I64,
4323 .R_WASM_MEMORY_ADDR_LEB,
4324 .R_WASM_MEMORY_ADDR_LEB64,
4325 .R_WASM_MEMORY_ADDR_SLEB,
4326 .R_WASM_MEMORY_ADDR_SLEB64,
4327 => {
4328 std.debug.assert(symbol.tag == .data);
4329 if (symbol.isUndefined()) {
4330 return 0;
4331 }
4332 const va: i33 = @intCast(symbol.virtual_address);
4333 return @intCast(va + relocation.addend);
4334 },
4335 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
4336 .R_WASM_SECTION_OFFSET_I32 => {
4337 const target_atom_index = wasm.symbol_atom.get(target_loc).?;
4338 const target_atom = wasm.getAtom(target_atom_index);
4339 const rel_value: i33 = @intCast(target_atom.offset);
4340 return @intCast(rel_value + relocation.addend);
4341 },
4342 .R_WASM_FUNCTION_OFFSET_I32 => {
4343 if (symbol.isUndefined()) {
4344 const val = atom.tombstone(wasm) orelse relocation.addend;
4345 return @bitCast(val);
4346 }
4347 const target_atom_index = wasm.symbol_atom.get(target_loc).?;
4348 const target_atom = wasm.getAtom(target_atom_index);
4349 const rel_value: i33 = @intCast(target_atom.offset);
4350 return @intCast(rel_value + relocation.addend);
4351 },
4352 .R_WASM_MEMORY_ADDR_TLS_SLEB,
4353 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
4354 => {
4355 const va: i33 = @intCast(symbol.virtual_address);
4356 return @intCast(va + relocation.addend);
4357 },
4358 }
4359 }
4360
4361 // For a given `Atom` returns whether it has a tombstone value or not.
4362 /// This defines whether we want a specific value when a section is dead.
4363 fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 {
4364 const atom_name = wasm.symbolLocSymbol(atom.symbolLoc()).name;
4365 if (atom_name == wasm.custom_sections.@".debug_ranges".name or
4366 atom_name == wasm.custom_sections.@".debug_loc".name)
4367 {
4368 return -2;
4369 } else if (std.mem.startsWith(u8, wasm.stringSlice(atom_name), ".debug_")) {
4370 return -1;
4371 } else {
4372 return null;
4373 }
4374 }
4375};
4376
4377pub const Relocation = struct {
4378 /// Represents the type of the `Relocation`
4379 relocation_type: RelocationType,
4380 /// Offset of the value to rewrite relative to the relevant section's contents.
4381 /// When `offset` is zero, its position is immediately after the id and size of the section.
4382 offset: u32,
4383 /// The index of the symbol used.
4384 /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type.
4385 index: u32,
4386 /// Addend to add to the address.
4387 /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`.
4388 addend: i32 = 0,
4389
4390 /// All possible relocation types currently existing.
4391 /// This enum is exhaustive as the spec is WIP and new types
4392 /// can be added which means that a generated binary will be invalid,
4393 /// so instead we will show an error in such cases.
4394 pub const RelocationType = enum(u8) {
4395 R_WASM_FUNCTION_INDEX_LEB = 0,
4396 R_WASM_TABLE_INDEX_SLEB = 1,
4397 R_WASM_TABLE_INDEX_I32 = 2,
4398 R_WASM_MEMORY_ADDR_LEB = 3,
4399 R_WASM_MEMORY_ADDR_SLEB = 4,
4400 R_WASM_MEMORY_ADDR_I32 = 5,
4401 R_WASM_TYPE_INDEX_LEB = 6,
4402 R_WASM_GLOBAL_INDEX_LEB = 7,
4403 R_WASM_FUNCTION_OFFSET_I32 = 8,
4404 R_WASM_SECTION_OFFSET_I32 = 9,
4405 R_WASM_EVENT_INDEX_LEB = 10,
4406 R_WASM_GLOBAL_INDEX_I32 = 13,
4407 R_WASM_MEMORY_ADDR_LEB64 = 14,
4408 R_WASM_MEMORY_ADDR_SLEB64 = 15,
4409 R_WASM_MEMORY_ADDR_I64 = 16,
4410 R_WASM_TABLE_INDEX_SLEB64 = 18,
4411 R_WASM_TABLE_INDEX_I64 = 19,
4412 R_WASM_TABLE_NUMBER_LEB = 20,
4413 R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
4414 R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
4415
4416 /// Returns true for relocation types where the `addend` field is present.
4417 pub fn addendIsPresent(self: RelocationType) bool {
4418 return switch (self) {
4419 .R_WASM_MEMORY_ADDR_LEB,
4420 .R_WASM_MEMORY_ADDR_SLEB,
4421 .R_WASM_MEMORY_ADDR_I32,
4422 .R_WASM_MEMORY_ADDR_LEB64,
4423 .R_WASM_MEMORY_ADDR_SLEB64,
4424 .R_WASM_MEMORY_ADDR_I64,
4425 .R_WASM_MEMORY_ADDR_TLS_SLEB,
4426 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
4427 .R_WASM_FUNCTION_OFFSET_I32,
4428 .R_WASM_SECTION_OFFSET_I32,
4429 => true,
4430 else => false,
4431 };
4432 }
4433 };
4434
4435 /// Verifies the relocation type of a given `Relocation` and returns
4436 /// true when the relocation references a function call or address to a function.
4437 pub fn isFunction(self: Relocation) bool {
4438 return switch (self.relocation_type) {
4439 .R_WASM_FUNCTION_INDEX_LEB,
4440 .R_WASM_TABLE_INDEX_SLEB,
4441 => true,
4442 else => false,
4443 };
4444 }
4445
4446 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
4447 _ = fmt;
4448 _ = options;
4449 try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{
4450 @tagName(self.relocation_type),
4451 self.offset,
4452 self.index,
4453 });
4454 }
4455};
4456
4457/// Unlike the `Import` object defined by the wasm spec, and existing
4458/// in the std.wasm namespace, this construct saves the 'module name' and 'name'
4459/// of the import using offsets into a string table, rather than the slices itself.
4460/// This saves us (potentially) 24 bytes per import on 64bit machines.
4461pub const Import = struct {
4462 module_name: String,
4463 name: String,
4464 kind: std.wasm.Import.Kind,
4465};
4466
4467/// Unlike the `Export` object defined by the wasm spec, and existing
4468/// in the std.wasm namespace, this construct saves the 'name'
4469/// of the export using offsets into a string table, rather than the slice itself.
4470/// This saves us (potentially) 12 bytes per export on 64bit machines.
4471pub const Export = struct {
4472 name: String,
4473 index: u32,
4474 kind: std.wasm.ExternalKind,
4475};
4476
4477pub const SubsectionType = enum(u8) {
4478 WASM_SEGMENT_INFO = 5,
4479 WASM_INIT_FUNCS = 6,
4480 WASM_COMDAT_INFO = 7,
4481 WASM_SYMBOL_TABLE = 8,
4482};
4483
4484pub const Alignment = @import("../InternPool.zig").Alignment;
4485
4486pub const NamedSegment = struct {
4487 /// Segment's name, encoded as UTF-8 bytes.
4488 name: []const u8,
4489 /// The required alignment of the segment, encoded as a power of 2
4490 alignment: Alignment,
4491 /// Bitfield containing flags for a segment
4492 flags: u32,
4493
4494 pub fn isTLS(segment: NamedSegment) bool {
4495 return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0;
4496 }
4497
4498 /// Returns the name as how it will be output into the final object
4499 /// file or binary. When `merge_segments` is true, this will return the
4500 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
4501 pub fn outputName(segment: NamedSegment, merge_segments: bool) []const u8 {
4502 if (segment.isTLS()) {
4503 return ".tdata";
4504 } else if (!merge_segments) {
4505 return segment.name;
4506 } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
4507 return ".rodata";
4508 } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
4509 return ".text";
4510 } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
4511 return ".data";
4512 } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
4513 return ".bss";
4514 }
4515 return segment.name;
4516 }
4517
4518 pub const Flags = enum(u32) {
4519 WASM_SEG_FLAG_STRINGS = 0x1,
4520 WASM_SEG_FLAG_TLS = 0x2,
4521 };
4522};
4523
4524pub const InitFunc = struct {
4525 /// Priority of the init function
4526 priority: u32,
4527 /// The symbol index of init function (not the function index).
4528 symbol_index: u32,
4529};
4530
4531pub const Comdat = struct {
4532 name: []const u8,
4533 /// Must be zero, no flags are currently defined by the tool-convention.
4534 flags: u32,
4535 symbols: []const ComdatSym,
4536};
4537
4538pub const ComdatSym = struct {
4539 kind: @This().Type,
4540 /// Index of the data segment/function/global/event/table within a WASM module.
4541 /// The object must not be an import.
4542 index: u32,
4543
4544 pub const Type = enum(u8) {
4545 WASM_COMDAT_DATA = 0,
4546 WASM_COMDAT_FUNCTION = 1,
4547 WASM_COMDAT_GLOBAL = 2,
4548 WASM_COMDAT_EVENT = 3,
4549 WASM_COMDAT_TABLE = 4,
4550 WASM_COMDAT_SECTION = 5,
4551 };
4552};
4553
4554pub const Feature = struct {
4555 /// Provides information about the usage of the feature.
4556 /// - '0x2b' (+): Object uses this feature, and the link fails if feature is not in the allowed set.
4557 /// - '0x2d' (-): Object does not use this feature, and the link fails if this feature is in the allowed set.
4558 /// - '0x3d' (=): Object uses this feature, and the link fails if this feature is not in the allowed set,
4559 /// or if any object does not use this feature.
4560 prefix: Prefix,
4561 /// Type of the feature, must be unique in the sequence of features.
4562 tag: Tag,
4563
4564 /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem
4565 pub const Tag = enum {
4566 atomics,
4567 bulk_memory,
4568 exception_handling,
4569 extended_const,
4570 half_precision,
4571 multimemory,
4572 multivalue,
4573 mutable_globals,
4574 nontrapping_fptoint,
4575 reference_types,
4576 relaxed_simd,
4577 sign_ext,
4578 simd128,
4579 tail_call,
4580 shared_mem,
4581
4582 /// From a given cpu feature, returns its linker feature
4583 pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
4584 return @as(Tag, @enumFromInt(@intFromEnum(feature)));
4585 }
4586
4587 pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
4588 _ = fmt;
4589 _ = opt;
4590 try writer.writeAll(switch (tag) {
4591 .atomics => "atomics",
4592 .bulk_memory => "bulk-memory",
4593 .exception_handling => "exception-handling",
4594 .extended_const => "extended-const",
4595 .half_precision => "half-precision",
4596 .multimemory => "multimemory",
4597 .multivalue => "multivalue",
4598 .mutable_globals => "mutable-globals",
4599 .nontrapping_fptoint => "nontrapping-fptoint",
4600 .reference_types => "reference-types",
4601 .relaxed_simd => "relaxed-simd",
4602 .sign_ext => "sign-ext",
4603 .simd128 => "simd128",
4604 .tail_call => "tail-call",
4605 .shared_mem => "shared-mem",
4606 });
4607 }
4608 };
4609
4610 pub const Prefix = enum(u8) {
4611 used = '+',
4612 disallowed = '-',
4613 required = '=',
4614 };
4615
4616 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
4617 _ = opt;
4618 _ = fmt;
4619 try writer.print("{c} {}", .{ feature.prefix, feature.tag });
4620 }
4621};
4622
4623pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{
4624 .{ "atomics", .atomics },
4625 .{ "bulk-memory", .bulk_memory },
4626 .{ "exception-handling", .exception_handling },
4627 .{ "extended-const", .extended_const },
4628 .{ "half-precision", .half_precision },
4629 .{ "multimemory", .multimemory },
4630 .{ "multivalue", .multivalue },
4631 .{ "mutable-globals", .mutable_globals },
4632 .{ "nontrapping-fptoint", .nontrapping_fptoint },
4633 .{ "reference-types", .reference_types },
4634 .{ "relaxed-simd", .relaxed_simd },
4635 .{ "sign-ext", .sign_ext },
4636 .{ "simd128", .simd128 },
4637 .{ "tail-call", .tail_call },
4638 .{ "shared-mem", .shared_mem },
4639});
4640
4641/// Parses an object file into atoms, for code and data sections
4642fn parseSymbolIntoAtom(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Atom.Index {
4643 const object = wasm.objectById(object_id) orelse
4644 return wasm.zig_object.?.parseSymbolIntoAtom(wasm, symbol_index);
4645 const comp = wasm.base.comp;
4646 const gpa = comp.gpa;
4647 const symbol = &object.symtable[@intFromEnum(symbol_index)];
4648 const relocatable_data: Object.RelocatableData = switch (symbol.tag) {
4649 .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
4650 .data => object.relocatable_data.get(.data).?[symbol.index],
4651 .section => blk: {
4652 const data = object.relocatable_data.get(.custom).?;
4653 for (data) |dat| {
4654 if (dat.section_index == symbol.index) {
4655 break :blk dat;
4656 }
4657 }
4658 unreachable;
4659 },
4660 else => unreachable,
4661 };
4662 const final_index = try wasm.getMatchingSegment(object_id, symbol_index);
4663 const atom_index = try wasm.createAtom(symbol_index, object_id.toOptional());
4664 try wasm.appendAtomAtIndex(final_index, atom_index);
4665
4666 const atom = wasm.getAtomPtr(atom_index);
4667 atom.size = relocatable_data.size;
4668 atom.alignment = relocatable_data.getAlignment(object);
4669 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
4670 atom.original_offset = relocatable_data.offset;
4671
4672 const segment = wasm.segmentPtr(final_index);
4673 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
4674 segment.alignment = segment.alignment.max(atom.alignment);
4675 }
4676
4677 if (object.relocations.get(relocatable_data.section_index)) |relocations| {
4678 const start = searchRelocStart(relocations, relocatable_data.offset);
4679 const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size);
4680 atom.relocs = std.ArrayListUnmanaged(Wasm.Relocation).fromOwnedSlice(relocations[start..][0..len]);
4681 for (atom.relocs.items) |reloc| {
4682 switch (reloc.relocation_type) {
4683 .R_WASM_TABLE_INDEX_I32,
4684 .R_WASM_TABLE_INDEX_I64,
4685 .R_WASM_TABLE_INDEX_SLEB,
4686 .R_WASM_TABLE_INDEX_SLEB64,
4687 => {
4688 try wasm.function_table.put(gpa, .{
4689 .file = object_id.toOptional(),
4690 .index = @enumFromInt(reloc.index),
4691 }, 0);
4692 },
4693 .R_WASM_GLOBAL_INDEX_I32,
4694 .R_WASM_GLOBAL_INDEX_LEB,
4695 => {
4696 const sym = object.symtable[reloc.index];
4697 if (sym.tag != .global) {
4698 try wasm.got_symbols.append(gpa, .{
4699 .file = object_id.toOptional(),
4700 .index = @enumFromInt(reloc.index),
4701 });
4702 }
4703 },
4704 else => {},
4705 }
4706 }
4707 }
4708
4709 return atom_index;
4710}
4711
4712fn searchRelocStart(relocs: []const Wasm.Relocation, address: u32) usize {
4713 var min: usize = 0;
4714 var max: usize = relocs.len;
4715 while (min < max) {
4716 const index = (min + max) / 2;
4717 const curr = relocs[index];
4718 if (curr.offset < address) {
4719 min = index + 1;
4720 } else {
4721 max = index;
4722 }
4723 }
4724 return min;
4725}
4726
4727fn searchRelocEnd(relocs: []const Wasm.Relocation, address: u32) usize {
4728 for (relocs, 0..relocs.len) |reloc, index| {
4729 if (reloc.offset > address) {
4730 return index;
4731 }
4732 }
4733 return relocs.len;
4734}
4735
4736pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String {2159pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String {
2160 assert(mem.indexOfScalar(u8, bytes, 0) == null);
4737 const gpa = wasm.base.comp.gpa;2161 const gpa = wasm.base.comp.gpa;
4738 const gop = try wasm.string_table.getOrPutContextAdapted(2162 const gop = try wasm.string_table.getOrPutContextAdapted(
4739 gpa,2163 gpa,
...@@ -4755,25 +2179,34 @@ pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String {...@@ -4755,25 +2179,34 @@ pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String {
4755}2179}
47562180
4757pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String {2181pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String {
2182 assert(mem.indexOfScalar(u8, bytes, 0) == null);
4758 return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{2183 return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{
4759 .bytes = wasm.string_bytes.items,2184 .bytes = wasm.string_bytes.items,
4760 }));2185 }));
4761}2186}
47622187
4763pub fn stringSlice(wasm: *const Wasm, index: String) [:0]const u8 {2188pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) error{OutOfMemory}!ValtypeList {
4764 const slice = wasm.string_bytes.items[@intFromEnum(index)..];2189 return .fromString(try internString(wasm, @ptrCast(valtype_list)));
4765 return slice[0..mem.indexOfScalar(u8, slice, 0).? :0];
4766}2190}
47672191
4768pub fn optionalStringSlice(wasm: *const Wasm, index: OptionalString) ?[:0]const u8 {2192pub fn addFuncType(wasm: *Wasm, ft: FunctionType) error{OutOfMemory}!FunctionType.Index {
4769 return stringSlice(wasm, index.unwrap() orelse return null);2193 const gpa = wasm.base.comp.gpa;
2194 const gop = try wasm.func_types.getOrPut(gpa, ft);
2195 return @enumFromInt(gop.index);
4770}2196}
47712197
4772pub fn castToString(wasm: *const Wasm, index: u32) String {2198pub fn addExpr(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!Expr {
4773 assert(index == 0 or wasm.string_bytes.items[index - 1] == 0);2199 const gpa = wasm.base.comp.gpa;
4774 return @enumFromInt(index);2200 // We can't use string table deduplication here since these expressions can
2201 // have null bytes in them however it may be interesting to explore since
2202 // it is likely for globals to share initialization values. Then again
2203 // there may not be very many globals in total.
2204 try wasm.string_bytes.appendSlice(gpa, bytes);
2205 return @enumFromInt(wasm.string_bytes.items.len - bytes.len);
4775}2206}
47762207
4777fn segmentPtr(wasm: *const Wasm, index: Segment.Index) *Segment {2208pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!DataSegment.Payload {
4778 return &wasm.segments.items[@intFromEnum(index)];2209 const gpa = wasm.base.comp.gpa;
2210 try wasm.string_bytes.appendSlice(gpa, bytes);
2211 return @enumFromInt(wasm.string_bytes.items.len - bytes.len);
4779}2212}
src/link/Wasm/Archive.zig+12-2
...@@ -142,7 +142,16 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {...@@ -142,7 +142,16 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {
142142
143/// From a given file offset, starts reading for a file header.143/// From a given file offset, starts reading for a file header.
144/// When found, parses the object file into an `Object` and returns it.144/// When found, parses the object file into an `Object` and returns it.
145pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, path: Path) !Object {145pub fn parseObject(
146 archive: Archive,
147 wasm: *Wasm,
148 file_contents: []const u8,
149 path: Path,
150 host_name: Wasm.String,
151 scratch_space: *Object.ScratchSpace,
152 must_link: bool,
153 gc_sections: bool,
154) !Object {
146 const header = mem.bytesAsValue(Header, file_contents[0..@sizeOf(Header)]);155 const header = mem.bytesAsValue(Header, file_contents[0..@sizeOf(Header)]);
147 if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter;156 if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter;
148157
...@@ -157,8 +166,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat...@@ -157,8 +166,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat
157 };166 };
158167
159 const object_file_size = try header.parsedSize();168 const object_file_size = try header.parsedSize();
169 const contents = file_contents[@sizeOf(Header)..][0..object_file_size];
160170
161 return Object.create(wasm, file_contents[@sizeOf(Header)..][0..object_file_size], path, object_name);171 return Object.parse(wasm, contents, path, object_name, host_name, scratch_space, must_link, gc_sections);
162}172}
163173
164const Archive = @This();174const Archive = @This();
src/link/Wasm/Flush.zig created+1448
...@@ -0,0 +1,1448 @@
1//! Temporary, dynamically allocated structures used only during flush.
2//! Could be constructed fresh each time, or kept around between updates to reduce heap allocations.
3
4const Flush = @This();
5const Wasm = @import("../Wasm.zig");
6const Object = @import("Object.zig");
7const Zcu = @import("../../Zcu.zig");
8const Alignment = Wasm.Alignment;
9const String = Wasm.String;
10const Relocation = Wasm.Relocation;
11const InternPool = @import("../../InternPool.zig");
12
13const build_options = @import("build_options");
14
15const std = @import("std");
16const Allocator = std.mem.Allocator;
17const mem = std.mem;
18const leb = std.leb;
19const log = std.log.scoped(.link);
20const assert = std.debug.assert;
21
22/// Ordered list of data segments that will appear in the final binary.
23/// When sorted, to-be-merged segments will be made adjacent.
24/// Values are offset relative to segment start.
25data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Index, u32) = .empty,
26/// Each time a `data_segment` offset equals zero it indicates a new group, and
27/// the next element in this array will contain the total merged segment size.
28data_segment_groups: std.ArrayListUnmanaged(u32) = .empty,
29
30binary_bytes: std.ArrayListUnmanaged(u8) = .empty,
31missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
32
33indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex, u32) = .empty,
34
35/// 0. Index into `data_segments`.
36const DataSegmentIndex = enum(u32) {
37 _,
38};
39
40pub fn clear(f: *Flush) void {
41 f.binary_bytes.clearRetainingCapacity();
42 f.function_imports.clearRetainingCapacity();
43 f.global_imports.clearRetainingCapacity();
44 f.functions.clearRetainingCapacity();
45 f.globals.clearRetainingCapacity();
46 f.data_segments.clearRetainingCapacity();
47 f.data_segment_groups.clearRetainingCapacity();
48 f.indirect_function_table.clearRetainingCapacity();
49 f.function_exports.clearRetainingCapacity();
50 f.global_exports.clearRetainingCapacity();
51}
52
53pub fn deinit(f: *Flush, gpa: Allocator) void {
54 f.binary_bytes.deinit(gpa);
55 f.function_imports.deinit(gpa);
56 f.global_imports.deinit(gpa);
57 f.functions.deinit(gpa);
58 f.globals.deinit(gpa);
59 f.data_segments.deinit(gpa);
60 f.data_segment_groups.deinit(gpa);
61 f.indirect_function_table.deinit(gpa);
62 f.function_exports.deinit(gpa);
63 f.global_exports.deinit(gpa);
64 f.* = undefined;
65}
66
67pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) anyerror!void {
68 const comp = wasm.base.comp;
69 const shared_memory = comp.config.shared_memory;
70 const diags = &comp.link_diags;
71 const gpa = comp.gpa;
72 const import_memory = comp.config.import_memory;
73 const export_memory = comp.config.export_memory;
74 const target = comp.root_mod.resolved_target.result;
75 const is_obj = comp.config.output_mode == .Obj;
76 const allow_undefined = is_obj or wasm.import_symbols;
77 const zcu = wasm.base.comp.zcu.?;
78 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
79
80 if (wasm.any_exports_updated) {
81 wasm.any_exports_updated = false;
82 wasm.function_exports.shrinkRetainingCapacity(wasm.function_exports_len);
83 wasm.global_exports.shrinkRetainingCapacity(wasm.global_exports_len);
84
85 const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;
86
87 try f.missing_exports.reinit(gpa, wasm.missing_exports_init, &.{});
88 for (wasm.nav_exports.keys()) |*nav_export| {
89 if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {
90 try wasm.function_exports.append(gpa, .fromNav(nav_export.nav_index, wasm));
91 if (nav_export.name.toOptional() == entry_name) {
92 wasm.entry_resolution = .pack(wasm, .{ .nav = nav_export.nav_index });
93 } else {
94 f.missing_exports.swapRemove(nav_export.name);
95 }
96 } else {
97 try wasm.global_exports.append(gpa, .fromNav(nav_export.nav_index));
98 f.missing_exports.swapRemove(nav_export.name);
99 }
100 }
101
102 for (f.missing_exports.keys()) |exp_name| {
103 if (exp_name != .none) continue;
104 diags.addError("manually specified export name '{s}' undefined", .{exp_name.slice(wasm)});
105 }
106
107 if (entry_name.unwrap()) |name| {
108 var err = try diags.addErrorWithNotes(1);
109 try err.addMsg("entry symbol '{s}' missing", .{name.slice(wasm)});
110 try err.addNote("'-fno-entry' suppresses this error", .{});
111 }
112 }
113
114 if (!allow_undefined) {
115 for (wasm.function_imports.keys()) |function_import_id| {
116 const name, const src_loc = function_import_id.nameAndLoc(wasm);
117 diags.addSrcError(src_loc, "undefined function: {s}", .{name.slice(wasm)});
118 }
119 for (wasm.global_imports.keys()) |global_import_id| {
120 const name, const src_loc = global_import_id.nameAndLoc(wasm);
121 diags.addSrcError(src_loc, "undefined global: {s}", .{name.slice(wasm)});
122 }
123 for (wasm.table_imports.keys()) |table_import_id| {
124 const name, const src_loc = table_import_id.nameAndLoc(wasm);
125 diags.addSrcError(src_loc, "undefined table: {s}", .{name.slice(wasm)});
126 }
127 }
128
129 if (diags.hasErrors()) return error.LinkFailure;
130
131 // TODO only include init functions for objects with must_link=true or
132 // which have any alive functions inside them.
133 if (wasm.object_init_funcs.items.len > 0) {
134 // Zig has no constructors so these are only for object file inputs.
135 mem.sortUnstable(Wasm.InitFunc, wasm.object_init_funcs.items, {}, Wasm.InitFunc.lessThan);
136 try f.functions.put(gpa, .__wasm_call_ctors, {});
137 }
138
139 var any_passive_inits = false;
140
141 // Merge and order the data segments. Depends on garbage collection so that
142 // unused segments can be omitted.
143 try f.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len);
144 for (wasm.object_data_segments.items, 0..) |*ds, i| {
145 if (!ds.flags.alive) continue;
146 any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !isBss(wasm, ds.name));
147 f.data_segments.putAssumeCapacityNoClobber(@intCast(i), .{
148 .offset = undefined,
149 });
150 }
151
152 try f.functions.ensureUnusedCapacity(gpa, 3);
153
154 // Passive segments are used to avoid memory being reinitialized on each
155 // thread's instantiation. These passive segments are initialized and
156 // dropped in __wasm_init_memory, which is registered as the start function
157 // We also initialize bss segments (using memory.fill) as part of this
158 // function.
159 if (any_passive_inits) {
160 f.functions.putAssumeCapacity(.__wasm_init_memory, {});
161 }
162
163 // When we have TLS GOT entries and shared memory is enabled,
164 // we must perform runtime relocations or else we don't create the function.
165 if (shared_memory) {
166 if (f.need_tls_relocs) f.functions.putAssumeCapacity(.__wasm_apply_global_tls_relocs, {});
167 f.functions.putAssumeCapacity(gpa, .__wasm_init_tls, {});
168 }
169
170 // Sort order:
171 // 0. Whether the segment is TLS
172 // 1. Segment name prefix
173 // 2. Segment alignment
174 // 3. Segment name suffix
175 // 4. Segment index (to break ties, keeping it deterministic)
176 // TLS segments are intended to be merged with each other, and segments
177 // with a common prefix name are intended to be merged with each other.
178 // Sorting ensures the segments intended to be merged will be adjacent.
179 const Sort = struct {
180 wasm: *const Wasm,
181 segments: []const Wasm.DataSegment.Index,
182 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
183 const lhs_segment_index = ctx.segments[lhs];
184 const rhs_segment_index = ctx.segments[rhs];
185 const lhs_segment = lhs_segment_index.ptr(wasm);
186 const rhs_segment = rhs_segment_index.ptr(wasm);
187 const lhs_tls = @intFromBool(lhs_segment.flags.tls);
188 const rhs_tls = @intFromBool(rhs_segment.flags.tls);
189 if (lhs_tls < rhs_tls) return true;
190 if (lhs_tls > rhs_tls) return false;
191 const lhs_prefix, const lhs_suffix = splitSegmentName(lhs_segment.name.unwrap().slice(ctx.wasm));
192 const rhs_prefix, const rhs_suffix = splitSegmentName(rhs_segment.name.unwrap().slice(ctx.wasm));
193 switch (mem.order(u8, lhs_prefix, rhs_prefix)) {
194 .lt => return true,
195 .gt => return false,
196 .eq => {},
197 }
198 switch (lhs_segment.flags.alignment.order(rhs_segment.flags.alignment)) {
199 .lt => return false,
200 .gt => return true,
201 .eq => {},
202 }
203 return switch (mem.order(u8, lhs_suffix, rhs_suffix)) {
204 .lt => true,
205 .gt => false,
206 .eq => @intFromEnum(lhs_segment_index) < @intFromEnum(rhs_segment_index),
207 };
208 }
209 };
210 f.data_segments.sortUnstable(@as(Sort, .{
211 .wasm = wasm,
212 .segments = f.data_segments.keys(),
213 }));
214
215 const page_size = std.wasm.page_size; // 64kb
216 const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention
217 const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention
218 const pointer_alignment: Alignment = .@"4";
219 // Always place the stack at the start by default unless the user specified the global-base flag.
220 const place_stack_first, var memory_ptr: u32 = if (wasm.global_base) |base| .{ false, base } else .{ true, 0 };
221
222 const VirtualAddrs = struct {
223 stack_pointer: u32,
224 heap_base: u32,
225 heap_end: u32,
226 tls_base: ?u32,
227 tls_align: ?u32,
228 tls_size: ?u32,
229 init_memory_flag: ?u32,
230 };
231 var virtual_addrs: VirtualAddrs = .{
232 .stack_pointer = undefined,
233 .heap_base = undefined,
234 .heap_end = undefined,
235 .tls_base = null,
236 .tls_align = null,
237 .tls_size = null,
238 .init_memory_flag = null,
239 };
240
241 if (place_stack_first and !is_obj) {
242 memory_ptr = stack_alignment.forward(memory_ptr);
243 memory_ptr += wasm.base.stack_size;
244 virtual_addrs.stack_pointer = memory_ptr;
245 }
246
247 const segment_indexes = f.data_segments.keys();
248 const segment_offsets = f.data_segments.values();
249 assert(f.data_segment_groups.items.len == 0);
250 {
251 var seen_tls: enum { before, during, after } = .before;
252 var offset: u32 = 0;
253 for (segment_indexes, segment_offsets, 0..) |segment_index, *segment_offset, i| {
254 const segment = segment_index.ptr(f);
255 memory_ptr = segment.alignment.forward(memory_ptr);
256
257 const want_new_segment = b: {
258 if (is_obj) break :b false;
259 switch (seen_tls) {
260 .before => if (segment.flags.tls) {
261 virtual_addrs.tls_base = if (shared_memory) 0 else memory_ptr;
262 virtual_addrs.tls_align = segment.flags.alignment;
263 seen_tls = .during;
264 break :b true;
265 },
266 .during => if (!segment.flags.tls) {
267 virtual_addrs.tls_size = memory_ptr - virtual_addrs.tls_base;
268 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(segment.flags.alignment);
269 seen_tls = .after;
270 break :b true;
271 },
272 .after => {},
273 }
274 break :b i >= 1 and !wasm.wantSegmentMerge(segment_indexes[i - 1], segment_index);
275 };
276 if (want_new_segment) {
277 if (offset > 0) try f.data_segment_groups.append(gpa, offset);
278 offset = 0;
279 }
280
281 segment_offset.* = offset;
282 offset += segment.size;
283 memory_ptr += segment.size;
284 }
285 if (offset > 0) try f.data_segment_groups.append(gpa, offset);
286 }
287
288 if (shared_memory and any_passive_inits) {
289 memory_ptr = pointer_alignment.forward(memory_ptr);
290 virtual_addrs.init_memory_flag = memory_ptr;
291 memory_ptr += 4;
292 }
293
294 if (!place_stack_first and !is_obj) {
295 memory_ptr = stack_alignment.forward(memory_ptr);
296 memory_ptr += wasm.base.stack_size;
297 virtual_addrs.stack_pointer = memory_ptr;
298 }
299
300 memory_ptr = heap_alignment.forward(memory_ptr);
301 virtual_addrs.heap_base = memory_ptr;
302
303 if (wasm.initial_memory) |initial_memory| {
304 if (!mem.isAlignedGeneric(u64, initial_memory, page_size)) {
305 diags.addError("initial memory value {d} is not {d}-byte aligned", .{ initial_memory, page_size });
306 }
307 if (memory_ptr > initial_memory) {
308 diags.addError("initial memory value {d} insufficient; minimum {d}", .{ initial_memory, memory_ptr });
309 }
310 if (initial_memory > std.math.maxInt(u32)) {
311 diags.addError("initial memory value {d} exceeds 32-bit address space", .{initial_memory});
312 }
313 if (diags.hasErrors()) return error.LinkFailure;
314 memory_ptr = initial_memory;
315 } else {
316 memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size);
317 }
318 virtual_addrs.heap_end = memory_ptr;
319
320 // In case we do not import memory, but define it ourselves, set the
321 // minimum amount of pages on the memory section.
322 wasm.memories.limits.min = @intCast(memory_ptr / page_size);
323 log.debug("total memory pages: {d}", .{wasm.memories.limits.min});
324
325 if (wasm.max_memory) |max_memory| {
326 if (!mem.isAlignedGeneric(u64, max_memory, page_size)) {
327 diags.addError("maximum memory value {d} is not {d}-byte aligned", .{ max_memory, page_size });
328 }
329 if (memory_ptr > max_memory) {
330 diags.addError("maximum memory value {d} insufficient; minimum {d}", .{ max_memory, memory_ptr });
331 }
332 if (max_memory > std.math.maxInt(u32)) {
333 diags.addError("maximum memory exceeds 32-bit address space", .{max_memory});
334 }
335 if (diags.hasErrors()) return error.LinkFailure;
336 wasm.memories.limits.max = @intCast(max_memory / page_size);
337 wasm.memories.limits.flags.has_max = true;
338 if (shared_memory) wasm.memories.limits.flags.is_shared = true;
339 log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});
340 }
341
342 // Size of each section header
343 const header_size = 5 + 1;
344 var section_index: u32 = 0;
345 // Index of the code section. Used to tell relocation table where the section lives.
346 var code_section_index: ?u32 = null;
347 // Index of the data section. Used to tell relocation table where the section lives.
348 var data_section_index: ?u32 = null;
349
350 const binary_bytes = &f.binary_bytes;
351 assert(binary_bytes.items.len == 0);
352
353 try binary_bytes.appendSlice(gpa, std.wasm.magic ++ std.wasm.version);
354 assert(binary_bytes.items.len == 8);
355
356 const binary_writer = binary_bytes.writer(gpa);
357
358 // Type section
359 if (wasm.func_types.items.len != 0) {
360 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
361 log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len});
362 for (wasm.func_types.items) |func_type| {
363 try leb.writeUleb128(binary_writer, std.wasm.function_type);
364 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.params.len)));
365 for (func_type.params) |param_ty| {
366 try leb.writeUleb128(binary_writer, std.wasm.valtype(param_ty));
367 }
368 try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.returns.len)));
369 for (func_type.returns) |ret_ty| {
370 try leb.writeUleb128(binary_writer, std.wasm.valtype(ret_ty));
371 }
372 }
373
374 try writeVecSectionHeader(
375 binary_bytes.items,
376 header_offset,
377 .type,
378 @intCast(binary_bytes.items.len - header_offset - header_size),
379 @intCast(wasm.func_types.items.len),
380 );
381 section_index += 1;
382 }
383
384 // Import section
385 const total_imports_len = wasm.function_imports.items.len + wasm.global_imports.items.len +
386 wasm.table_imports.items.len + wasm.memory_imports.items.len + @intFromBool(import_memory);
387
388 if (total_imports_len > 0) {
389 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
390
391 for (wasm.function_imports.items) |*function_import| {
392 const module_name = function_import.module_name.slice(wasm);
393 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
394 try binary_writer.writeAll(module_name);
395
396 const name = function_import.name.slice(wasm);
397 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
398 try binary_writer.writeAll(name);
399
400 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
401 try leb.writeUleb128(binary_writer, function_import.index);
402 }
403
404 for (wasm.table_imports.items) |*table_import| {
405 const module_name = table_import.module_name.slice(wasm);
406 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
407 try binary_writer.writeAll(module_name);
408
409 const name = table_import.name.slice(wasm);
410 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
411 try binary_writer.writeAll(name);
412
413 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
414 try leb.writeUleb128(binary_writer, std.wasm.reftype(table_import.reftype));
415 try emitLimits(binary_writer, table_import.limits);
416 }
417
418 for (wasm.memory_imports.items) |*memory_import| {
419 try emitMemoryImport(wasm, binary_writer, memory_import);
420 } else if (import_memory) {
421 try emitMemoryImport(wasm, binary_writer, &.{
422 .module_name = wasm.host_name,
423 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
424 .limits_min = wasm.memories.limits.min,
425 .limits_max = wasm.memories.limits.max,
426 .limits_has_max = wasm.memories.limits.flags.has_max,
427 .limits_is_shared = wasm.memories.limits.flags.is_shared,
428 });
429 }
430
431 for (wasm.global_imports.items) |*global_import| {
432 const module_name = global_import.module_name.slice(wasm);
433 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
434 try binary_writer.writeAll(module_name);
435
436 const name = global_import.name.slice(wasm);
437 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
438 try binary_writer.writeAll(name);
439
440 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
441 try leb.writeUleb128(binary_writer, @intFromEnum(global_import.valtype));
442 try binary_writer.writeByte(@intFromBool(global_import.mutable));
443 }
444
445 try writeVecSectionHeader(
446 binary_bytes.items,
447 header_offset,
448 .import,
449 @intCast(binary_bytes.items.len - header_offset - header_size),
450 @intCast(total_imports_len),
451 );
452 section_index += 1;
453 }
454
455 // Function section
456 if (wasm.functions.count() != 0) {
457 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
458 for (wasm.functions.values()) |function| {
459 try leb.writeUleb128(binary_writer, function.func.type_index);
460 }
461
462 try writeVecSectionHeader(
463 binary_bytes.items,
464 header_offset,
465 .function,
466 @intCast(binary_bytes.items.len - header_offset - header_size),
467 @intCast(wasm.functions.count()),
468 );
469 section_index += 1;
470 }
471
472 // Table section
473 if (wasm.tables.items.len > 0) {
474 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
475
476 for (wasm.tables.items) |table| {
477 try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));
478 try emitLimits(binary_writer, table.limits);
479 }
480
481 try writeVecSectionHeader(
482 binary_bytes.items,
483 header_offset,
484 .table,
485 @intCast(binary_bytes.items.len - header_offset - header_size),
486 @intCast(wasm.tables.items.len),
487 );
488 section_index += 1;
489 }
490
491 // Memory section
492 if (!import_memory) {
493 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
494
495 try emitLimits(binary_writer, wasm.memories.limits);
496 try writeVecSectionHeader(
497 binary_bytes.items,
498 header_offset,
499 .memory,
500 @intCast(binary_bytes.items.len - header_offset - header_size),
501 1, // wasm currently only supports 1 linear memory segment
502 );
503 section_index += 1;
504 }
505
506 // Global section (used to emit stack pointer)
507 if (wasm.output_globals.items.len > 0) {
508 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
509
510 for (wasm.output_globals.items) |global| {
511 try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype));
512 try binary_writer.writeByte(@intFromBool(global.global_type.mutable));
513 try emitInit(binary_writer, global.init);
514 }
515
516 try writeVecSectionHeader(
517 binary_bytes.items,
518 header_offset,
519 .global,
520 @intCast(binary_bytes.items.len - header_offset - header_size),
521 @intCast(wasm.output_globals.items.len),
522 );
523 section_index += 1;
524 }
525
526 // Export section
527 if (wasm.exports.items.len != 0 or export_memory) {
528 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
529
530 for (wasm.exports.items) |exp| {
531 const name = exp.name.slice(wasm);
532 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
533 try binary_writer.writeAll(name);
534 try leb.writeUleb128(binary_writer, @intFromEnum(exp.kind));
535 try leb.writeUleb128(binary_writer, exp.index);
536 }
537
538 if (export_memory) {
539 try leb.writeUleb128(binary_writer, @as(u32, @intCast("memory".len)));
540 try binary_writer.writeAll("memory");
541 try binary_writer.writeByte(std.wasm.externalKind(.memory));
542 try leb.writeUleb128(binary_writer, @as(u32, 0));
543 }
544
545 try writeVecSectionHeader(
546 binary_bytes.items,
547 header_offset,
548 .@"export",
549 @intCast(binary_bytes.items.len - header_offset - header_size),
550 @intCast(wasm.exports.items.len + @intFromBool(export_memory)),
551 );
552 section_index += 1;
553 }
554
555 if (wasm.entry) |entry_index| {
556 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
557 try writeVecSectionHeader(
558 binary_bytes.items,
559 header_offset,
560 .start,
561 @intCast(binary_bytes.items.len - header_offset - header_size),
562 entry_index,
563 );
564 }
565
566 // element section (function table)
567 if (wasm.function_table.count() > 0) {
568 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
569
570 const table_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?;
571 const table_sym = wasm.finalSymbolByLoc(table_loc);
572
573 const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
574 try leb.writeUleb128(binary_writer, flags);
575 if (flags == 0x02) {
576 try leb.writeUleb128(binary_writer, table_sym.index);
577 }
578 try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
579 if (flags == 0x02) {
580 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
581 }
582 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
583 var symbol_it = wasm.function_table.keyIterator();
584 while (symbol_it.next()) |symbol_loc_ptr| {
585 const sym = wasm.finalSymbolByLoc(symbol_loc_ptr.*);
586 assert(sym.flags.alive);
587 assert(sym.index < wasm.functions.count() + wasm.imported_functions_count);
588 try leb.writeUleb128(binary_writer, sym.index);
589 }
590
591 try writeVecSectionHeader(
592 binary_bytes.items,
593 header_offset,
594 .element,
595 @intCast(binary_bytes.items.len - header_offset - header_size),
596 1,
597 );
598 section_index += 1;
599 }
600
601 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
602 if (f.data_segment_groups.items.len > 0 and shared_memory) {
603 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
604 try writeVecSectionHeader(
605 binary_bytes.items,
606 header_offset,
607 .data_count,
608 @intCast(binary_bytes.items.len - header_offset - header_size),
609 @intCast(f.data_segment_groups.items.len),
610 );
611 }
612
613 // Code section.
614 if (f.functions.count() != 0) {
615 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
616 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
617
618 for (f.functions.keys()) |resolution| switch (resolution.unpack()) {
619 .unresolved => unreachable,
620 .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"),
621 .__wasm_call_ctors => @panic("TODO lower __wasm_call_ctors"),
622 .__wasm_init_memory => @panic("TODO lower __wasm_init_memory "),
623 .__wasm_init_tls => @panic("TODO lower __wasm_init_tls "),
624 .__zig_error_names => @panic("TODO lower __zig_error_names "),
625 .object_function => |i| {
626 _ = i;
627 _ = start_offset;
628 @panic("TODO lower object function code and apply relocations");
629 //try leb.writeUleb128(binary_writer, atom.code.len);
630 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
631 },
632 .nav => |i| {
633 _ = i;
634 _ = start_offset;
635 @panic("TODO lower nav code and apply relocations");
636 //try leb.writeUleb128(binary_writer, atom.code.len);
637 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
638 },
639 };
640
641 try writeVecSectionHeader(
642 binary_bytes.items,
643 header_offset,
644 .code,
645 @intCast(binary_bytes.items.len - header_offset - header_size),
646 @intCast(wasm.functions.count()),
647 );
648 code_section_index = section_index;
649 section_index += 1;
650 }
651
652 // Data section.
653 if (f.data_segment_groups.items.len != 0) {
654 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
655
656 var group_index: u32 = 0;
657 var offset: u32 = undefined;
658 for (segment_indexes, segment_offsets) |segment_index, segment_offset| {
659 const segment = segment_index.ptr(wasm);
660 if (segment.size == 0) continue;
661 if (!import_memory and isBss(wasm, segment.name)) {
662 // It counted for virtual memory but it does not go into the binary.
663 continue;
664 }
665 if (segment_offset == 0) {
666 const group_size = f.data_segment_groups.items[group_index];
667 group_index += 1;
668 offset = 0;
669
670 const flags: Object.DataSegmentFlags = if (segment.flags.is_passive) .passive else .active;
671 try leb.writeUleb128(binary_writer, @intFromEnum(flags));
672 // when a segment is passive, it's initialized during runtime.
673 if (flags != .passive) {
674 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment_offset)) });
675 }
676 try leb.writeUleb128(binary_writer, group_size);
677 }
678
679 try binary_bytes.appendNTimes(gpa, 0, segment_offset - offset);
680 offset = segment_offset;
681 try binary_bytes.appendSlice(gpa, segment.payload.slice(wasm));
682 offset += segment.payload.len;
683 if (true) @panic("TODO apply data segment relocations");
684 }
685 assert(group_index == f.data_segment_groups.items.len);
686
687 try writeVecSectionHeader(
688 binary_bytes.items,
689 header_offset,
690 .data,
691 @intCast(binary_bytes.items.len - header_offset - header_size),
692 group_index,
693 );
694 data_section_index = section_index;
695 section_index += 1;
696 }
697
698 if (is_obj) {
699 @panic("TODO emit link section for object file and apply relocations");
700 //var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
701 //try wasm.emitLinkSection(binary_bytes, &symbol_table);
702 //if (code_section_index) |code_index| {
703 // try wasm.emitCodeRelocations(binary_bytes, code_index, symbol_table);
704 //}
705 //if (data_section_index) |data_index| {
706 // if (wasm.data_segments.count() > 0)
707 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);
708 //}
709 } else if (comp.config.debug_format != .strip) {
710 try wasm.emitNameSection(binary_bytes, arena);
711 }
712
713 if (comp.config.debug_format != .strip) {
714 // The build id must be computed on the main sections only,
715 // so we have to do it now, before the debug sections.
716 switch (wasm.base.build_id) {
717 .none => {},
718 .fast => {
719 var id: [16]u8 = undefined;
720 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
721 var uuid: [36]u8 = undefined;
722 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
723 std.fmt.fmtSliceHexLower(id[0..4]),
724 std.fmt.fmtSliceHexLower(id[4..6]),
725 std.fmt.fmtSliceHexLower(id[6..8]),
726 std.fmt.fmtSliceHexLower(id[8..10]),
727 std.fmt.fmtSliceHexLower(id[10..]),
728 });
729 try emitBuildIdSection(binary_bytes, &uuid);
730 },
731 .hexstring => |hs| {
732 var buffer: [32 * 2]u8 = undefined;
733 const str = std.fmt.bufPrint(&buffer, "{s}", .{
734 std.fmt.fmtSliceHexLower(hs.toSlice()),
735 }) catch unreachable;
736 try emitBuildIdSection(binary_bytes, str);
737 },
738 else => |mode| {
739 var err = try diags.addErrorWithNotes(0);
740 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
741 },
742 }
743
744 var debug_bytes = std.ArrayList(u8).init(gpa);
745 defer debug_bytes.deinit();
746
747 try emitProducerSection(binary_bytes);
748 if (!target.cpu.features.isEmpty())
749 try emitFeaturesSection(binary_bytes, target.cpu.features);
750 }
751
752 // Finally, write the entire binary into the file.
753 const file = wasm.base.file.?;
754 try file.pwriteAll(binary_bytes.items, 0);
755 try file.setEndPos(binary_bytes.items.len);
756}
757
758fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena: Allocator) !void {
759 const comp = wasm.base.comp;
760 const gpa = comp.gpa;
761 const import_memory = comp.config.import_memory;
762
763 // Deduplicate symbols that point to the same function.
764 var funcs: std.AutoArrayHashMapUnmanaged(u32, String) = .empty;
765 try funcs.ensureUnusedCapacityPrecise(arena, wasm.functions.count() + wasm.function_imports.items.len);
766
767 const NamedIndex = struct {
768 index: u32,
769 name: String,
770 };
771
772 var globals: std.MultiArrayList(NamedIndex) = .empty;
773 try globals.ensureTotalCapacityPrecise(arena, wasm.output_globals.items.len + wasm.global_imports.items.len);
774
775 var segments: std.MultiArrayList(NamedIndex) = .empty;
776 try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());
777
778 for (wasm.resolved_symbols.keys()) |sym_loc| {
779 const symbol = wasm.finalSymbolByLoc(sym_loc).*;
780 if (!symbol.flags.alive) continue;
781 const name = wasm.finalSymbolByLoc(sym_loc).name;
782 switch (symbol.tag) {
783 .function => {
784 const index = if (symbol.flags.undefined)
785 @intFromEnum(symbol.pointee.function_import)
786 else
787 wasm.function_imports.items.len + @intFromEnum(symbol.pointee.function);
788 const gop = funcs.getOrPutAssumeCapacity(index);
789 if (gop.found_existing) {
790 assert(gop.value_ptr.* == name);
791 } else {
792 gop.value_ptr.* = name;
793 }
794 },
795 .global => {
796 globals.appendAssumeCapacity(.{
797 .index = if (symbol.flags.undefined)
798 @intFromEnum(symbol.pointee.global_import)
799 else
800 @intFromEnum(symbol.pointee.global),
801 .name = name,
802 });
803 },
804 else => {},
805 }
806 }
807
808 for (wasm.data_segments.keys(), 0..) |key, index| {
809 // bss section is not emitted when this condition holds true, so we also
810 // do not output a name for it.
811 if (!import_memory and mem.eql(u8, key, ".bss")) continue;
812 segments.appendAssumeCapacity(.{ .index = @intCast(index), .name = key });
813 }
814
815 const Sort = struct {
816 indexes: []const u32,
817 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
818 return ctx.indexes[lhs] < ctx.indexes[rhs];
819 }
820 };
821 funcs.entries.sortUnstable(@as(Sort, .{ .indexes = funcs.keys() }));
822 globals.sortUnstable(@as(Sort, .{ .indexes = globals.items(.index) }));
823 // Data segments are already ordered.
824
825 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
826 const writer = binary_bytes.writer();
827 try leb.writeUleb128(writer, @as(u32, @intCast("name".len)));
828 try writer.writeAll("name");
829
830 try emitNameSubsection(wasm, binary_bytes, .function, funcs.keys(), funcs.values());
831 try emitNameSubsection(wasm, binary_bytes, .global, globals.items(.index), globals.items(.name));
832 try emitNameSubsection(wasm, binary_bytes, .data_segment, segments.items(.index), segments.items(.name));
833
834 try writeCustomSectionHeader(
835 binary_bytes.items,
836 header_offset,
837 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
838 );
839}
840
841fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
842 var buf: [1 + 5]u8 = undefined;
843 buf[0] = 0; // 0 = 'custom' section
844 leb.writeUnsignedFixed(5, buf[1..6], size);
845 buffer[offset..][0..buf.len].* = buf;
846}
847
848fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) error{OutOfMemory}!u32 {
849 // unlike regular section, we don't emit the count
850 const header_size = 1 + 5;
851 try bytes.appendNTimes(gpa, 0, header_size);
852 return @intCast(bytes.items.len - header_size);
853}
854
855fn emitNameSubsection(
856 wasm: *const Wasm,
857 binary_bytes: *std.ArrayListUnmanaged(u8),
858 section_id: std.wasm.NameSubsection,
859 indexes: []const u32,
860 names: []const String,
861) !void {
862 assert(indexes.len == names.len);
863 const gpa = wasm.base.comp.gpa;
864 // We must emit subsection size, so first write to a temporary list
865 var section_list: std.ArrayListUnmanaged(u8) = .empty;
866 defer section_list.deinit(gpa);
867 const sub_writer = section_list.writer(gpa);
868
869 try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len)));
870 for (indexes, names) |index, name_index| {
871 const name = name_index.slice(wasm);
872 log.debug("emit symbol '{s}' type({s})", .{ name, @tagName(section_id) });
873 try leb.writeUleb128(sub_writer, index);
874 try leb.writeUleb128(sub_writer, @as(u32, @intCast(name.len)));
875 try sub_writer.writeAll(name);
876 }
877
878 // From now, write to the actual writer
879 const writer = binary_bytes.writer(gpa);
880 try leb.writeUleb128(writer, @intFromEnum(section_id));
881 try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len)));
882 try binary_bytes.appendSlice(gpa, section_list.items);
883}
884
885fn emitFeaturesSection(
886 gpa: Allocator,
887 binary_bytes: *std.ArrayListUnmanaged(u8),
888 features: []const Wasm.Feature,
889) !void {
890 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
891
892 const writer = binary_bytes.writer();
893 const target_features = "target_features";
894 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));
895 try writer.writeAll(target_features);
896
897 try leb.writeUleb128(writer, @as(u32, @intCast(features.len)));
898 for (features) |feature| {
899 assert(feature.prefix != .invalid);
900 try leb.writeUleb128(writer, @tagName(feature.prefix)[0]);
901 const name = @tagName(feature.tag);
902 try leb.writeUleb128(writer, @as(u32, name.len));
903 try writer.writeAll(name);
904 }
905
906 try writeCustomSectionHeader(
907 binary_bytes.items,
908 header_offset,
909 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
910 );
911}
912
913fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void {
914 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
915
916 const writer = binary_bytes.writer();
917 const hdr_build_id = "build_id";
918 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));
919 try writer.writeAll(hdr_build_id);
920
921 try leb.writeUleb128(writer, @as(u32, 1));
922 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));
923 try writer.writeAll(build_id);
924
925 try writeCustomSectionHeader(
926 binary_bytes.items,
927 header_offset,
928 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
929 );
930}
931
932fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void {
933 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
934
935 const writer = binary_bytes.writer();
936 const producers = "producers";
937 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));
938 try writer.writeAll(producers);
939
940 try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by
941
942 // language field
943 {
944 const language = "language";
945 try leb.writeUleb128(writer, @as(u32, @intCast(language.len)));
946 try writer.writeAll(language);
947
948 // field_value_count (TODO: Parse object files for producer sections to detect their language)
949 try leb.writeUleb128(writer, @as(u32, 1));
950
951 // versioned name
952 {
953 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
954 try writer.writeAll("Zig");
955
956 try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len)));
957 try writer.writeAll(build_options.version);
958 }
959 }
960
961 // processed-by field
962 {
963 const processed_by = "processed-by";
964 try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len)));
965 try writer.writeAll(processed_by);
966
967 // field_value_count (TODO: Parse object files for producer sections to detect other used tools)
968 try leb.writeUleb128(writer, @as(u32, 1));
969
970 // versioned name
971 {
972 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"
973 try writer.writeAll("Zig");
974
975 try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len)));
976 try writer.writeAll(build_options.version);
977 }
978 }
979
980 try writeCustomSectionHeader(
981 binary_bytes.items,
982 header_offset,
983 @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
984 );
985}
986
987///// For each relocatable section, emits a custom "relocation.<section_name>" section
988//fn emitCodeRelocations(
989// wasm: *Wasm,
990// binary_bytes: *std.ArrayListUnmanaged(u8),
991// section_index: u32,
992// symbol_table: std.AutoArrayHashMapUnmanaged(SymbolLoc, u32),
993//) !void {
994// const comp = wasm.base.comp;
995// const gpa = comp.gpa;
996// const code_index = wasm.code_section_index.unwrap() orelse return;
997// const writer = binary_bytes.writer();
998// const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
999//
1000// // write custom section information
1001// const name = "reloc.CODE";
1002// try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
1003// try writer.writeAll(name);
1004// try leb.writeUleb128(writer, section_index);
1005// const reloc_start = binary_bytes.items.len;
1006//
1007// var count: u32 = 0;
1008// var atom: *Atom = wasm.atoms.get(code_index).?.ptr(wasm);
1009// // for each atom, we calculate the uleb size and append that
1010// var size_offset: u32 = 5; // account for code section size leb128
1011// while (true) {
1012// size_offset += getUleb128Size(atom.code.len);
1013// for (atom.relocSlice(wasm)) |relocation| {
1014// count += 1;
1015// const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
1016// const symbol_index = symbol_table.get(sym_loc).?;
1017// try leb.writeUleb128(writer, @intFromEnum(relocation.tag));
1018// const offset = atom.offset + relocation.offset + size_offset;
1019// try leb.writeUleb128(writer, offset);
1020// try leb.writeUleb128(writer, symbol_index);
1021// if (relocation.tag.addendIsPresent()) {
1022// try leb.writeIleb128(writer, relocation.addend);
1023// }
1024// log.debug("Emit relocation: {}", .{relocation});
1025// }
1026// if (atom.prev == .none) break;
1027// atom = atom.prev.ptr(wasm);
1028// }
1029// if (count == 0) return;
1030// var buf: [5]u8 = undefined;
1031// leb.writeUnsignedFixed(5, &buf, count);
1032// try binary_bytes.insertSlice(reloc_start, &buf);
1033// const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6);
1034// try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
1035//}
1036
1037//fn emitDataRelocations(
1038// wasm: *Wasm,
1039// binary_bytes: *std.ArrayList(u8),
1040// section_index: u32,
1041// symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
1042//) !void {
1043// const comp = wasm.base.comp;
1044// const gpa = comp.gpa;
1045// const writer = binary_bytes.writer();
1046// const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1047//
1048// // write custom section information
1049// const name = "reloc.DATA";
1050// try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
1051// try writer.writeAll(name);
1052// try leb.writeUleb128(writer, section_index);
1053// const reloc_start = binary_bytes.items.len;
1054//
1055// var count: u32 = 0;
1056// // for each atom, we calculate the uleb size and append that
1057// var size_offset: u32 = 5; // account for code section size leb128
1058// for (wasm.data_segments.values()) |segment_index| {
1059// var atom: *Atom = wasm.atoms.get(segment_index).?.ptr(wasm);
1060// while (true) {
1061// size_offset += getUleb128Size(atom.code.len);
1062// for (atom.relocSlice(wasm)) |relocation| {
1063// count += 1;
1064// const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
1065// const symbol_index = symbol_table.get(sym_loc).?;
1066// try leb.writeUleb128(writer, @intFromEnum(relocation.tag));
1067// const offset = atom.offset + relocation.offset + size_offset;
1068// try leb.writeUleb128(writer, offset);
1069// try leb.writeUleb128(writer, symbol_index);
1070// if (relocation.tag.addendIsPresent()) {
1071// try leb.writeIleb128(writer, relocation.addend);
1072// }
1073// log.debug("Emit relocation: {}", .{relocation});
1074// }
1075// if (atom.prev == .none) break;
1076// atom = atom.prev.ptr(wasm);
1077// }
1078// }
1079// if (count == 0) return;
1080//
1081// var buf: [5]u8 = undefined;
1082// leb.writeUnsignedFixed(5, &buf, count);
1083// try binary_bytes.insertSlice(reloc_start, &buf);
1084// const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6));
1085// try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
1086//}
1087
1088fn isBss(wasm: *Wasm, name: String) bool {
1089 const s = name.slice(wasm);
1090 return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss.");
1091}
1092
1093fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } {
1094 const start = @intFromBool(name.len >= 1 and name[0] == '.');
1095 const pivot = mem.indexOfScalarPos(u8, name, start, '.') orelse 0;
1096 return .{ name[0..pivot], name[pivot..] };
1097}
1098
1099fn wantSegmentMerge(wasm: *const Wasm, a_index: Wasm.DataSegment.Index, b_index: Wasm.DataSegment.Index) bool {
1100 const a = a_index.ptr(wasm);
1101 const b = b_index.ptr(wasm);
1102 if (a.flags.tls and b.flags.tls) return true;
1103 if (a.flags.tls != b.flags.tls) return false;
1104 if (a.flags.is_passive != b.flags.is_passive) return false;
1105 if (a.name == b.name) return true;
1106 const a_prefix, _ = splitSegmentName(a.name.slice(wasm));
1107 const b_prefix, _ = splitSegmentName(b.name.slice(wasm));
1108 return a_prefix.len > 0 and mem.eql(u8, a_prefix, b_prefix);
1109}
1110
1111fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) error{OutOfMemory}!u32 {
1112 // section id + fixed leb contents size + fixed leb vector length
1113 const header_size = 1 + 5 + 5;
1114 try bytes.appendNTimes(gpa, 0, header_size);
1115 return @intCast(bytes.items.len - header_size);
1116}
1117
1118fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {
1119 var buf: [1 + 5 + 5]u8 = undefined;
1120 buf[0] = @intFromEnum(section);
1121 leb.writeUnsignedFixed(5, buf[1..6], size);
1122 leb.writeUnsignedFixed(5, buf[6..], items);
1123 buffer[offset..][0..buf.len].* = buf;
1124}
1125
1126fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {
1127 try writer.writeByte(limits.flags);
1128 try leb.writeUleb128(writer, limits.min);
1129 if (limits.flags.has_max) try leb.writeUleb128(writer, limits.max);
1130}
1131
1132fn emitMemoryImport(wasm: *Wasm, writer: anytype, memory_import: *const Wasm.MemoryImport) error{OutOfMemory}!void {
1133 const module_name = memory_import.module_name.slice(wasm);
1134 try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));
1135 try writer.writeAll(module_name);
1136
1137 const name = memory_import.name.slice(wasm);
1138 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
1139 try writer.writeAll(name);
1140
1141 try writer.writeByte(@intFromEnum(std.wasm.ExternalKind.memory));
1142 try emitLimits(writer, memory_import.limits());
1143}
1144
1145pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
1146 switch (init_expr) {
1147 .i32_const => |val| {
1148 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1149 try leb.writeIleb128(writer, val);
1150 },
1151 .i64_const => |val| {
1152 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));
1153 try leb.writeIleb128(writer, val);
1154 },
1155 .f32_const => |val| {
1156 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const));
1157 try writer.writeInt(u32, @bitCast(val), .little);
1158 },
1159 .f64_const => |val| {
1160 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f64_const));
1161 try writer.writeInt(u64, @bitCast(val), .little);
1162 },
1163 .global_get => |val| {
1164 try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get));
1165 try leb.writeUleb128(writer, val);
1166 },
1167 }
1168 try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));
1169}
1170
1171//fn emitLinkSection(
1172// wasm: *Wasm,
1173// binary_bytes: *std.ArrayListUnmanaged(u8),
1174// symbol_table: *std.AutoArrayHashMapUnmanaged(SymbolLoc, u32),
1175//) !void {
1176// const gpa = wasm.base.comp.gpa;
1177// const offset = try reserveCustomSectionHeader(gpa, binary_bytes);
1178// const writer = binary_bytes.writer();
1179// // emit "linking" custom section name
1180// const section_name = "linking";
1181// try leb.writeUleb128(writer, section_name.len);
1182// try writer.writeAll(section_name);
1183//
1184// // meta data version, which is currently '2'
1185// try leb.writeUleb128(writer, @as(u32, 2));
1186//
1187// // For each subsection type (found in Subsection) we can emit a section.
1188// // Currently, we only support emitting segment info and the symbol table.
1189// try wasm.emitSymbolTable(binary_bytes, symbol_table);
1190// try wasm.emitSegmentInfo(binary_bytes);
1191//
1192// const size: u32 = @intCast(binary_bytes.items.len - offset - 6);
1193// try writeCustomSectionHeader(binary_bytes.items, offset, size);
1194//}
1195
1196fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
1197 const writer = binary_bytes.writer();
1198 try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info));
1199 const segment_offset = binary_bytes.items.len;
1200
1201 try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count())));
1202 for (wasm.segment_info.values()) |segment_info| {
1203 log.debug("Emit segment: {s} align({d}) flags({b})", .{
1204 segment_info.name,
1205 segment_info.alignment,
1206 segment_info.flags,
1207 });
1208 try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len)));
1209 try writer.writeAll(segment_info.name);
1210 try leb.writeUleb128(writer, segment_info.alignment.toLog2Units());
1211 try leb.writeUleb128(writer, segment_info.flags);
1212 }
1213
1214 var buf: [5]u8 = undefined;
1215 leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset)));
1216 try binary_bytes.insertSlice(segment_offset, &buf);
1217}
1218
1219//fn emitSymbolTable(
1220// wasm: *Wasm,
1221// binary_bytes: *std.ArrayListUnmanaged(u8),
1222// symbol_table: *std.AutoArrayHashMapUnmanaged(SymbolLoc, u32),
1223//) !void {
1224// const gpa = wasm.base.comp.gpa;
1225// const writer = binary_bytes.writer(gpa);
1226//
1227// try leb.writeUleb128(writer, @intFromEnum(SubsectionType.symbol_table));
1228// const table_offset = binary_bytes.items.len;
1229//
1230// var symbol_count: u32 = 0;
1231// for (wasm.resolved_symbols.keys()) |sym_loc| {
1232// const symbol = wasm.finalSymbolByLoc(sym_loc).*;
1233// if (symbol.tag == .dead) continue;
1234// try symbol_table.putNoClobber(gpa, sym_loc, symbol_count);
1235// symbol_count += 1;
1236// log.debug("emit symbol: {}", .{symbol});
1237// try leb.writeUleb128(writer, @intFromEnum(symbol.tag));
1238// try leb.writeUleb128(writer, symbol.flags);
1239//
1240// const sym_name = wasm.symbolLocName(sym_loc);
1241// switch (symbol.tag) {
1242// .data => {
1243// try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
1244// try writer.writeAll(sym_name);
1245//
1246// if (!symbol.flags.undefined) {
1247// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.data_out));
1248// const atom_index = wasm.symbol_atom.get(sym_loc).?;
1249// const atom = wasm.getAtom(atom_index);
1250// try leb.writeUleb128(writer, @as(u32, atom.offset));
1251// try leb.writeUleb128(writer, @as(u32, atom.code.len));
1252// }
1253// },
1254// .section => {
1255// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.section));
1256// },
1257// .function => {
1258// if (symbol.flags.undefined) {
1259// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.function_import));
1260// } else {
1261// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.function));
1262// try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
1263// try writer.writeAll(sym_name);
1264// }
1265// },
1266// .global => {
1267// if (symbol.flags.undefined) {
1268// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.global_import));
1269// } else {
1270// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.global));
1271// try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
1272// try writer.writeAll(sym_name);
1273// }
1274// },
1275// .table => {
1276// if (symbol.flags.undefined) {
1277// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.table_import));
1278// } else {
1279// try leb.writeUleb128(writer, @intFromEnum(symbol.pointee.table));
1280// try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len)));
1281// try writer.writeAll(sym_name);
1282// }
1283// },
1284// .event => unreachable,
1285// .dead => unreachable,
1286// .uninitialized => unreachable,
1287// }
1288// }
1289//
1290// var buf: [10]u8 = undefined;
1291// leb.writeUnsignedFixed(5, buf[0..5], @intCast(binary_bytes.items.len - table_offset + 5));
1292// leb.writeUnsignedFixed(5, buf[5..], symbol_count);
1293// try binary_bytes.insertSlice(table_offset, &buf);
1294//}
1295
1296///// Resolves the relocations within the atom, writing the new value
1297///// at the calculated offset.
1298//fn resolveAtomRelocs(wasm: *const Wasm, atom: *Atom) void {
1299// const symbol_name = wasm.symbolLocName(atom.symbolLoc());
1300// log.debug("resolving {d} relocs in atom '{s}'", .{ atom.relocs.len, symbol_name });
1301//
1302// for (atom.relocSlice(wasm)) |reloc| {
1303// const value = atomRelocationValue(wasm, atom, reloc);
1304// log.debug("relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
1305// wasm.symbolLocName(.{
1306// .file = atom.file,
1307// .index = @enumFromInt(reloc.index),
1308// }),
1309// symbol_name,
1310// reloc.offset,
1311// value,
1312// });
1313//
1314// switch (reloc.tag) {
1315// .TABLE_INDEX_I32,
1316// .FUNCTION_OFFSET_I32,
1317// .GLOBAL_INDEX_I32,
1318// .MEMORY_ADDR_I32,
1319// .SECTION_OFFSET_I32,
1320// => mem.writeInt(u32, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
1321//
1322// .TABLE_INDEX_I64,
1323// .MEMORY_ADDR_I64,
1324// => mem.writeInt(u64, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..8], value, .little),
1325//
1326// .GLOBAL_INDEX_LEB,
1327// .EVENT_INDEX_LEB,
1328// .FUNCTION_INDEX_LEB,
1329// .MEMORY_ADDR_LEB,
1330// .MEMORY_ADDR_SLEB,
1331// .TABLE_INDEX_SLEB,
1332// .TABLE_NUMBER_LEB,
1333// .TYPE_INDEX_LEB,
1334// .MEMORY_ADDR_TLS_SLEB,
1335// => leb.writeUnsignedFixed(5, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
1336//
1337// .MEMORY_ADDR_LEB64,
1338// .MEMORY_ADDR_SLEB64,
1339// .TABLE_INDEX_SLEB64,
1340// .MEMORY_ADDR_TLS_SLEB64,
1341// => leb.writeUnsignedFixed(10, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..10], value),
1342// }
1343// }
1344//}
1345
1346///// From a given `relocation` will return the new value to be written.
1347///// All values will be represented as a `u64` as all values can fit within it.
1348///// The final value must be casted to the correct size.
1349//fn atomRelocationValue(wasm: *const Wasm, atom: *const Atom, relocation: *const Relocation) u64 {
1350// if (relocation.tag == .TYPE_INDEX_LEB) {
1351// // Eagerly resolved when parsing the object file.
1352// if (true) @panic("TODO the eager resolve when parsing");
1353// return relocation.index;
1354// }
1355// const target_loc = wasm.symbolLocFinalLoc(.{
1356// .file = atom.file,
1357// .index = @enumFromInt(relocation.index),
1358// });
1359// const symbol = wasm.finalSymbolByLoc(target_loc);
1360// if (symbol.tag != .section and !symbol.flags.alive) {
1361// const val = atom.tombstone(wasm) orelse relocation.addend;
1362// return @bitCast(val);
1363// }
1364// return switch (relocation.tag) {
1365// .FUNCTION_INDEX_LEB => if (symbol.flags.undefined)
1366// @intFromEnum(symbol.pointee.function_import)
1367// else
1368// @intFromEnum(symbol.pointee.function) + wasm.function_imports.items.len,
1369// .TABLE_NUMBER_LEB => if (symbol.flags.undefined)
1370// @intFromEnum(symbol.pointee.table_import)
1371// else
1372// @intFromEnum(symbol.pointee.table) + wasm.table_imports.items.len,
1373// .TABLE_INDEX_I32,
1374// .TABLE_INDEX_I64,
1375// .TABLE_INDEX_SLEB,
1376// .TABLE_INDEX_SLEB64,
1377// => wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
1378//
1379// .TYPE_INDEX_LEB => unreachable, // handled above
1380// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)
1381// @intFromEnum(symbol.pointee.global_import)
1382// else
1383// @intFromEnum(symbol.pointee.global) + wasm.global_imports.items.len,
1384//
1385// .MEMORY_ADDR_I32,
1386// .MEMORY_ADDR_I64,
1387// .MEMORY_ADDR_LEB,
1388// .MEMORY_ADDR_LEB64,
1389// .MEMORY_ADDR_SLEB,
1390// .MEMORY_ADDR_SLEB64,
1391// => {
1392// assert(symbol.tag == .data);
1393// if (symbol.flags.undefined) return 0;
1394// const va: i33 = symbol.virtual_address;
1395// return @intCast(va + relocation.addend);
1396// },
1397// .EVENT_INDEX_LEB => @panic("TODO: expose this as an error, events are unsupported"),
1398// .SECTION_OFFSET_I32 => {
1399// const target_atom_index = wasm.symbol_atom.get(target_loc).?;
1400// const target_atom = wasm.getAtom(target_atom_index);
1401// const rel_value: i33 = target_atom.offset;
1402// return @intCast(rel_value + relocation.addend);
1403// },
1404// .FUNCTION_OFFSET_I32 => {
1405// if (symbol.flags.undefined) {
1406// const val = atom.tombstone(wasm) orelse relocation.addend;
1407// return @bitCast(val);
1408// }
1409// const target_atom_index = wasm.symbol_atom.get(target_loc).?;
1410// const target_atom = wasm.getAtom(target_atom_index);
1411// const rel_value: i33 = target_atom.offset;
1412// return @intCast(rel_value + relocation.addend);
1413// },
1414// .MEMORY_ADDR_TLS_SLEB,
1415// .MEMORY_ADDR_TLS_SLEB64,
1416// => {
1417// const va: i33 = symbol.virtual_address;
1418// return @intCast(va + relocation.addend);
1419// },
1420// };
1421//}
1422
1423///// For a given `Atom` returns whether it has a tombstone value or not.
1424///// This defines whether we want a specific value when a section is dead.
1425//fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 {
1426// const atom_name = wasm.finalSymbolByLoc(atom.symbolLoc()).name;
1427// if (atom_name == wasm.custom_sections.@".debug_ranges".name or
1428// atom_name == wasm.custom_sections.@".debug_loc".name)
1429// {
1430// return -2;
1431// } else if (mem.startsWith(u8, atom_name.slice(wasm), ".debug_")) {
1432// return -1;
1433// } else {
1434// return null;
1435// }
1436//}
1437
1438fn getUleb128Size(uint_value: anytype) u32 {
1439 const T = @TypeOf(uint_value);
1440 const U = if (@typeInfo(T).int.bits < 8) u8 else T;
1441 var value = @as(U, @intCast(uint_value));
1442
1443 var size: u32 = 0;
1444 while (value != 0) : (size += 1) {
1445 value >>= 7;
1446 }
1447 return size;
1448}
src/link/Wasm/Object.zig+897-752
...@@ -1,23 +1,16 @@...@@ -1,23 +1,16 @@
1//! Object represents a wasm object file. When initializing a new
2//! `Object`, it will parse the contents of a given file handler, and verify
3//! the data on correctness. The result can then be used by the linker.
4const Object = @This();1const Object = @This();
52
6const Wasm = @import("../Wasm.zig");3const Wasm = @import("../Wasm.zig");
7const Atom = Wasm.Atom;
8const Alignment = Wasm.Alignment;4const Alignment = Wasm.Alignment;
9const Symbol = @import("Symbol.zig");
105
11const std = @import("std");6const std = @import("std");
12const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
13const leb = std.leb;
14const meta = std.meta;
15const Path = std.Build.Cache.Path;8const Path = std.Build.Cache.Path;
16
17const log = std.log.scoped(.object);9const log = std.log.scoped(.object);
10const assert = std.debug.assert;
1811
19/// Wasm spec version used for this `Object`12/// Wasm spec version used for this `Object`
20version: u32 = 0,13version: u32,
21/// For error reporting purposes only.14/// For error reporting purposes only.
22/// Name (read path) of the object or archive file.15/// Name (read path) of the object or archive file.
23path: Path,16path: Path,
...@@ -25,817 +18,969 @@ path: Path,...@@ -25,817 +18,969 @@ path: Path,
25/// If this represents an object in an archive, it's the basename of the18/// If this represents an object in an archive, it's the basename of the
26/// object, and path refers to the archive.19/// object, and path refers to the archive.
27archive_member_name: ?[]const u8,20archive_member_name: ?[]const u8,
28/// Parsed type section
29func_types: []const std.wasm.Type = &.{},
30/// A list of all imports for this module
31imports: []const Wasm.Import = &.{},
32/// Parsed function section
33functions: []const std.wasm.Func = &.{},
34/// Parsed table section
35tables: []const std.wasm.Table = &.{},
36/// Parsed memory section
37memories: []const std.wasm.Memory = &.{},
38/// Parsed global section
39globals: []const std.wasm.Global = &.{},
40/// Parsed export section
41exports: []const Wasm.Export = &.{},
42/// Parsed element section
43elements: []const std.wasm.Element = &.{},
44/// Represents the function ID that must be called on startup.21/// Represents the function ID that must be called on startup.
45/// This is `null` by default as runtimes may determine the startup22/// This is `null` by default as runtimes may determine the startup
46/// function themselves. This is essentially legacy.23/// function themselves. This is essentially legacy.
47start: ?u32 = null,24start_function: Wasm.OptionalObjectFunctionIndex,
48/// A slice of features that tell the linker what features are mandatory,25/// A slice of features that tell the linker what features are mandatory, used
49/// used (or therefore missing) and must generate an error when another26/// (or therefore missing) and must generate an error when another object uses
50/// object uses features that are not supported by the other.27/// features that are not supported by the other.
51features: []const Wasm.Feature = &.{},28features: Wasm.Feature.Set,
52/// A table that maps the relocations we must perform where the key represents29/// Points into Wasm functions
53/// the section that the list of relocations applies to.30functions: RelativeSlice,
54relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty,31/// Points into Wasm object_globals_imports
55/// Table of symbols belonging to this Object file32globals_imports: RelativeSlice,
56symtable: []Symbol = &.{},33/// Points into Wasm object_tables_imports
57/// Extra metadata about the linking section, such as alignment of segments and their name34tables_imports: RelativeSlice,
58segment_info: []const Wasm.NamedSegment = &.{},35/// Points into Wasm object_custom_segments
59/// A sequence of function initializers that must be called on startup36custom_segments: RelativeSlice,
60init_funcs: []const Wasm.InitFunc = &.{},37/// For calculating local section index from `Wasm.SectionIndex`.
61/// Comdat information38local_section_index_base: u32,
62comdat_info: []const Wasm.Comdat = &.{},39/// Points into Wasm object_init_funcs
63/// Represents non-synthetic sections that can essentially be mem-cpy'd into place40init_funcs: RelativeSlice,
64/// after performing relocations.41/// Points into Wasm object_comdats
65relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty,42comdats: RelativeSlice,
66/// Amount of functions in the `import` sections.43
67imported_functions_count: u32 = 0,44pub const RelativeSlice = struct {
68/// Amount of globals in the `import` section.45 off: u32,
69imported_globals_count: u32 = 0,46 len: u32,
70/// Amount of tables in the `import` section.
71imported_tables_count: u32 = 0,
72
73/// Represents a single item within a section (depending on its `type`)
74pub const RelocatableData = struct {
75 /// The type of the relocatable data
76 type: Tag,
77 /// Pointer to the data of the segment, where its length is written to `size`
78 data: [*]u8,
79 /// The size in bytes of the data representing the segment within the section
80 size: u32,
81 /// The index within the section itself, or in case of a debug section,
82 /// the offset within the `string_table`.
83 index: u32,
84 /// The offset within the section where the data starts
85 offset: u32,
86 /// Represents the index of the section it belongs to
87 section_index: u32,
88 /// Whether the relocatable section is represented by a symbol or not.
89 /// Can only be `true` for custom sections.
90 represented: bool = false,
91
92 const Tag = enum { data, code, custom };
93
94 /// Returns the alignment of the segment, by retrieving it from the segment
95 /// meta data of the given object file.
96 /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's
97 /// alignment to retrieve the natural alignment.
98 pub fn getAlignment(relocatable_data: RelocatableData, object: *const Object) Alignment {
99 if (relocatable_data.type != .data) return .@"1";
100 return object.segment_info[relocatable_data.index].alignment;
101 }
102
103 /// Returns the symbol kind that corresponds to the relocatable section
104 pub fn getSymbolKind(relocatable_data: RelocatableData) Symbol.Tag {
105 return switch (relocatable_data.type) {
106 .data => .data,
107 .code => .function,
108 .custom => .section,
109 };
110 }
111
112 /// Returns the index within a section, or in case of a custom section,
113 /// returns the section index within the object file.
114 pub fn getIndex(relocatable_data: RelocatableData) u32 {
115 if (relocatable_data.type == .custom) return relocatable_data.section_index;
116 return relocatable_data.index;
117 }
118};47};
11948
120/// Initializes a new `Object` from a wasm object file.49pub const SegmentInfo = struct {
121/// This also parses and verifies the object file.50 name: Wasm.String,
122/// When a max size is given, will only parse up to the given size,51 flags: Flags,
123/// else will read until the end of the file.52
124pub fn create(53 const Flags = packed struct(u32) {
125 wasm: *Wasm,54 /// Signals that the segment contains only null terminated strings allowing
126 file_contents: []const u8,55 /// the linker to perform merging.
127 path: Path,56 strings: bool,
128 archive_member_name: ?[]const u8,57 /// The segment contains thread-local data. This means that a unique copy
129) !Object {58 /// of this segment will be created for each thread.
130 const gpa = wasm.base.comp.gpa;59 tls: bool,
131 var object: Object = .{60 /// If the object file is included in the final link, the segment should be
132 .path = path,61 /// retained in the final output regardless of whether it is used by the
133 .archive_member_name = archive_member_name,62 /// program.
63 retain: bool,
64 alignment: Alignment,
65
66 _: u23 = 0,
134 };67 };
68};
13569
136 var parser: Parser = .{70pub const FunctionImport = struct {
137 .object = &object,71 module_name: Wasm.String,
138 .wasm = wasm,72 name: Wasm.String,
139 .reader = std.io.fixedBufferStream(file_contents),73 function_index: ScratchSpace.FuncTypeIndex,
140 };74};
141 try parser.parseObject(gpa);
14275
143 return object;76pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };
144}
14577
146/// Frees all memory of `Object` at once. The given `Allocator` must be78pub const SubsectionType = enum(u8) {
147/// the same allocator that was used when `init` was called.79 segment_info = 5,
148pub fn deinit(object: *Object, gpa: Allocator) void {80 init_funcs = 6,
149 for (object.func_types) |func_ty| {81 comdat_info = 7,
150 gpa.free(func_ty.params);82 symbol_table = 8,
151 gpa.free(func_ty.returns);83};
152 }
153 gpa.free(object.func_types);
154 gpa.free(object.functions);
155 gpa.free(object.imports);
156 gpa.free(object.tables);
157 gpa.free(object.memories);
158 gpa.free(object.globals);
159 gpa.free(object.exports);
160 for (object.elements) |el| {
161 gpa.free(el.func_indexes);
162 }
163 gpa.free(object.elements);
164 gpa.free(object.features);
165 for (object.relocations.values()) |val| {
166 gpa.free(val);
167 }
168 object.relocations.deinit(gpa);
169 gpa.free(object.symtable);
170 gpa.free(object.comdat_info);
171 gpa.free(object.init_funcs);
172 for (object.segment_info) |info| {
173 gpa.free(info.name);
174 }
175 gpa.free(object.segment_info);
176 {
177 var it = object.relocatable_data.valueIterator();
178 while (it.next()) |relocatable_data| {
179 for (relocatable_data.*) |rel_data| {
180 gpa.free(rel_data.data[0..rel_data.size]);
181 }
182 gpa.free(relocatable_data.*);
183 }
184 }
185 object.relocatable_data.deinit(gpa);
186 object.* = undefined;
187}
18884
189/// Finds the import within the list of imports from a given kind and index of that kind.85pub const Symbol = struct {
190/// Asserts the import exists86 flags: Wasm.SymbolFlags,
191pub fn findImport(object: *const Object, sym: Symbol) Wasm.Import {87 name: Wasm.OptionalString,
192 var i: u32 = 0;88 pointee: Pointee,
193 return for (object.imports) |import| {89
194 if (std.meta.activeTag(import.kind) == sym.tag.externalType()) {90 /// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection
195 if (i == sym.index) return import;91 const Tag = enum(u8) {
196 i += 1;92 function,
197 }93 data,
198 } else unreachable; // Only existing imports are allowed to be found94 global,
199}95 section,
96 event,
97 table,
98 };
20099
201/// Checks if the object file is an MVP version.100 const Pointee = union(enum) {
202/// When that's the case, we check if there's an import table definition with its name101 function: Wasm.ObjectFunctionIndex,
203/// set to '__indirect_function_table". When that's also the case,102 function_import: ScratchSpace.FuncImportIndex,
204/// we initialize a new table symbol that corresponds to that import and return that symbol.103 data: struct {
205///104 segment_index: Wasm.DataSegment.Index,
206/// When the object file is *NOT* MVP, we return `null`.105 segment_offset: u32,
207fn checkLegacyIndirectFunctionTable(object: *Object, wasm: *const Wasm) !?Symbol {106 size: u32,
208 const diags = &wasm.base.comp.link_diags;107 },
108 data_import: void,
109 global: Wasm.ObjectGlobalIndex,
110 global_import: Wasm.ObjectGlobalImportIndex,
111 section: Wasm.ObjectSectionIndex,
112 table: Wasm.ObjectTableIndex,
113 table_import: Wasm.ObjectTableImportIndex,
114 };
115};
209116
210 var table_count: usize = 0;117pub const ScratchSpace = struct {
211 for (object.symtable) |sym| {118 func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty,
212 if (sym.tag == .table) table_count += 1;119 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,
213 }120 func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,
121 symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,
122 segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,
214123
215 // For each import table, we also have a symbol so this is not a legacy object file124 /// Index into `func_imports`.
216 if (object.imported_tables_count == table_count) return null;125 const FuncImportIndex = enum(u32) {
126 _,
217127
218 if (table_count != 0) {128 fn ptr(index: FunctionImport, ss: *const ScratchSpace) *FunctionImport {
219 return diags.failParse(object.path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{129 return &ss.func_imports.items[@intFromEnum(index)];
220 object.imported_tables_count,130 }
221 table_count,131 };
222 });
223 }
224
225 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
226 if (object.tables.len > 0) {
227 return diags.failParse(object.path, "unexpected table definition without representing table symbols.", .{});
228 }
229132
230 if (object.imported_tables_count != 1) {133 /// Index into `func_types`.
231 return diags.failParse(object.path, "found more than one table import, but no representing table symbols", .{});134 const FuncTypeIndex = enum(u32) {
232 }135 _,
233136
234 const table_import: Wasm.Import = for (object.imports) |imp| {137 fn ptr(index: FuncTypeIndex, ss: *const ScratchSpace) *Wasm.FunctionType.Index {
235 if (imp.kind == .table) {138 return &ss.func_types.items[@intFromEnum(index)];
236 break imp;
237 }139 }
238 } else unreachable;140 };
239141
240 if (table_import.name != wasm.preloaded_strings.__indirect_function_table) {142 pub fn deinit(ss: *ScratchSpace, gpa: Allocator) void {
241 return diags.failParse(object.path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{143 ss.func_types.deinit(gpa);
242 wasm.stringSlice(table_import.name),144 ss.func_type_indexes.deinit(gpa);
243 });145 ss.func_imports.deinit(gpa);
146 ss.symbol_table.deinit(gpa);
147 ss.segment_info.deinit(gpa);
148 ss.* = undefined;
244 }149 }
245150
246 var table_symbol: Symbol = .{151 fn clear(ss: *ScratchSpace) void {
247 .flags = 0,152 ss.func_types.clearRetainingCapacity();
248 .name = table_import.name,153 ss.func_type_indexes.clearRetainingCapacity();
249 .tag = .table,154 ss.func_imports.clearRetainingCapacity();
250 .index = 0,155 ss.symbol_table.clearRetainingCapacity();
251 .virtual_address = undefined,156 ss.segment_info.clearRetainingCapacity();
252 };157 }
253 table_symbol.setFlag(.WASM_SYM_UNDEFINED);158};
254 table_symbol.setFlag(.WASM_SYM_NO_STRIP);
255 return table_symbol;
256}
257159
258const Parser = struct {160fn parse(
259 reader: std.io.FixedBufferStream([]const u8),
260 /// Object file we're building
261 object: *Object,
262 /// Mutable so that the string table can be modified.
263 wasm: *Wasm,161 wasm: *Wasm,
162 bytes: []const u8,
163 path: Path,
164 archive_member_name: ?[]const u8,
165 host_name: Wasm.String,
166 ss: *ScratchSpace,
167 must_link: bool,
168 gc_sections: bool,
169) anyerror!Object {
170 const gpa = wasm.base.comp.gpa;
171 const diags = &wasm.base.comp.link_diags;
264172
265 fn parseObject(parser: *Parser, gpa: Allocator) anyerror!void {173 var pos: usize = 0;
266 const wasm = parser.wasm;174
267175 if (!std.mem.eql(u8, bytes[0..std.wasm.magic.len], &std.wasm.magic)) return error.BadObjectMagic;
268 {176 pos += std.wasm.magic.len;
269 var magic_bytes: [4]u8 = undefined;177
270 try parser.reader.reader().readNoEof(&magic_bytes);178 const version = std.mem.readInt(u32, bytes[pos..][0..4], .little);
271 if (!std.mem.eql(u8, &magic_bytes, &std.wasm.magic)) return error.BadObjectMagic;179 pos += 4;
272 }180
273181 const data_segment_start: u32 = @intCast(wasm.object_data_segments.items.len);
274 const version = try parser.reader.reader().readInt(u32, .little);182 const custom_segment_start: u32 = @intCast(wasm.object_custom_segments.items.len);
275 parser.object.version = version;183 const imports_start: u32 = @intCast(wasm.object_imports.items.len);
276184 const functions_start: u32 = @intCast(wasm.object_functions.items.len);
277 var saw_linking_section = false;185 const tables_start: u32 = @intCast(wasm.object_tables.items.len);
278186 const memories_start: u32 = @intCast(wasm.object_memories.items.len);
279 var section_index: u32 = 0;187 const globals_start: u32 = @intCast(wasm.object_globals.items.len);
280 while (parser.reader.reader().readByte()) |byte| : (section_index += 1) {188 const init_funcs_start: u32 = @intCast(wasm.object_init_funcs.items.len);
281 const len = try readLeb(u32, parser.reader.reader());189 const comdats_start: u32 = @intCast(wasm.object_comdats.items.len);
282 var limited_reader = std.io.limitedReader(parser.reader.reader(), len);190 const global_imports_start: u32 = @intCast(wasm.object_global_imports.items.len);
283 const reader = limited_reader.reader();191 const table_imports_start: u32 = @intCast(wasm.object_table_imports.items.len);
284 switch (@as(std.wasm.Section, @enumFromInt(byte))) {192 const local_section_index_base = wasm.object_total_sections;
285 .custom => {193 const source_location: Wasm.SourceLocation = .fromObjectIndex(wasm.objects.items.len);
286 const name_len = try readLeb(u32, reader);194
287 const name = try gpa.alloc(u8, name_len);195 ss.clear();
288 defer gpa.free(name);196
289 try reader.readNoEof(name);197 var start_function: Wasm.OptionalObjectFunctionIndex = .none;
290198 var opt_features: ?Wasm.Feature.Set = null;
291 if (std.mem.eql(u8, name, "linking")) {199 var saw_linking_section = false;
292 saw_linking_section = true;200 var has_tls = false;
293 try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left)));201 var local_section_index: u32 = 0;
294 } else if (std.mem.startsWith(u8, name, "reloc")) {202 var table_count: usize = 0;
295 try parser.parseRelocations(gpa);203 while (pos < bytes.len) : (local_section_index += 1) {
296 } else if (std.mem.eql(u8, name, "target_features")) {204 const section_index: Wasm.SectionIndex = @enumFromInt(local_section_index_base + local_section_index);
297 try parser.parseFeatures(gpa);205
298 } else if (std.mem.startsWith(u8, name, ".debug")) {206 const section_tag: std.wasm.Section = @enumFromInt(bytes[pos]);
299 const gop = try parser.object.relocatable_data.getOrPut(gpa, .custom);207 pos += 1;
300 var relocatable_data: std.ArrayListUnmanaged(RelocatableData) = .empty;208
301 defer relocatable_data.deinit(gpa);209 const len, pos = readLeb(u32, bytes, pos);
302 if (!gop.found_existing) {210 const section_end = pos + len;
303 gop.value_ptr.* = &.{};211 switch (section_tag) {
304 } else {212 .custom => {
305 relocatable_data = std.ArrayListUnmanaged(RelocatableData).fromOwnedSlice(gop.value_ptr.*);213 const section_name, pos = readBytes(bytes, pos);
306 }214 if (std.mem.eql(u8, section_name, "linking")) {
307 const debug_size = @as(u32, @intCast(reader.context.bytes_left));215 saw_linking_section = true;
308 const debug_content = try gpa.alloc(u8, debug_size);216 const section_version, pos = readLeb(u32, bytes, pos);
309 errdefer gpa.free(debug_content);217 log.debug("link meta data version: {d}", .{section_version});
310 try reader.readNoEof(debug_content);218 if (section_version != 2) return error.UnsupportedVersion;
311219 while (pos < section_end) {
312 try relocatable_data.append(gpa, .{220 const sub_type, pos = readLeb(u8, bytes, pos);
313 .type = .custom,221 log.debug("found subsection: {s}", .{@tagName(@as(SubsectionType, @enumFromInt(sub_type)))});
314 .data = debug_content.ptr,222 const payload_len, pos = readLeb(u32, bytes, pos);
315 .size = debug_size,223 if (payload_len == 0) break;
316 .index = @intFromEnum(try wasm.internString(name)),224
317 .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset225 const count, pos = readLeb(u32, bytes, pos);
318 .section_index = section_index,226
319 });227 switch (@as(SubsectionType, @enumFromInt(sub_type))) {
320 gop.value_ptr.* = try relocatable_data.toOwnedSlice(gpa);228 .segment_info => {
321 } else {229 for (try ss.segment_info.addManyAsSlice(gpa, count)) |*segment| {
322 try reader.skipBytes(reader.context.bytes_left, .{});230 const name, pos = readBytes(bytes, pos);
323 }231 const alignment, pos = readLeb(u32, bytes, pos);
324 },232 const flags_u32, pos = readLeb(u32, bytes, pos);
325 .type => {233 const flags: SegmentInfo.Flags = @bitCast(flags_u32);
326 for (try readVec(&parser.object.func_types, reader, gpa)) |*type_val| {234 const tls = flags.tls or
327 if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType;235 // Supports legacy object files that specified
328236 // being TLS by the name instead of the TLS flag.
329 for (try readVec(&type_val.params, reader, gpa)) |*param| {237 std.mem.startsWith(u8, name, ".tdata") or
330 param.* = try readEnum(std.wasm.Valtype, reader);238 std.mem.startsWith(u8, name, ".tbss");
331 }239 has_tls = has_tls or tls;
332240 segment.* = .{
333 for (try readVec(&type_val.returns, reader, gpa)) |*result| {241 .name = try wasm.internString(name),
334 result.* = try readEnum(std.wasm.Valtype, reader);242 .flags = .{
335 }243 .strings = flags.strings,
336 }244 .tls = tls,
337 try assertEnd(reader);245 .alignment = @enumFromInt(alignment),
338 },246 .no_strip = flags.retain,
339 .import => {247 },
340 for (try readVec(&parser.object.imports, reader, gpa)) |*import| {248 };
341 const module_len = try readLeb(u32, reader);249 }
342 const module_name = try gpa.alloc(u8, module_len);
343 defer gpa.free(module_name);
344 try reader.readNoEof(module_name);
345
346 const name_len = try readLeb(u32, reader);
347 const name = try gpa.alloc(u8, name_len);
348 defer gpa.free(name);
349 try reader.readNoEof(name);
350
351 const kind = try readEnum(std.wasm.ExternalKind, reader);
352 const kind_value: std.wasm.Import.Kind = switch (kind) {
353 .function => val: {
354 parser.object.imported_functions_count += 1;
355 break :val .{ .function = try readLeb(u32, reader) };
356 },250 },
357 .memory => .{ .memory = try readLimits(reader) },251 .init_funcs => {
358 .global => val: {252 for (try wasm.object_init_funcs.addManyAsSlice(gpa, count)) |*func| {
359 parser.object.imported_globals_count += 1;253 const priority, pos = readLeb(u32, bytes, pos);
360 break :val .{ .global = .{254 const symbol_index, pos = readLeb(u32, bytes, pos);
361 .valtype = try readEnum(std.wasm.Valtype, reader),255 if (symbol_index > ss.symbol_table.items.len)
362 .mutable = (try reader.readByte()) == 0x01,256 return diags.failParse(path, "init_funcs before symbol table", .{});
363 } };257 const sym = &ss.symbol_table.items[symbol_index];
258 if (sym.tag != .function) {
259 return diags.failParse(path, "init_func symbol '{s}' not a function", .{
260 wasm.stringSlice(sym.name),
261 });
262 } else if (sym.flags.undefined) {
263 return diags.failParse(path, "init_func symbol '{s}' is an import", .{
264 wasm.stringSlice(sym.name),
265 });
266 }
267 func.* = .{
268 .priority = priority,
269 .function_index = sym.pointee.function,
270 };
271 }
364 },272 },
365 .table => val: {273 .comdat_info => {
366 parser.object.imported_tables_count += 1;274 for (try wasm.object_comdats.addManyAsSlice(gpa, count)) |*comdat| {
367 break :val .{ .table = .{275 const name, pos = readBytes(bytes, pos);
368 .reftype = try readEnum(std.wasm.RefType, reader),276 const flags, pos = readLeb(u32, bytes, pos);
369 .limits = try readLimits(reader),277 if (flags != 0) return error.UnexpectedComdatFlags;
370 } };278 const symbol_count, pos = readLeb(u32, bytes, pos);
279 const start_off: u32 = @intCast(wasm.object_comdat_symbols.items.len);
280 for (try wasm.object_comdat_symbols.addManyAsSlice(gpa, symbol_count)) |*symbol| {
281 const kind, pos = readEnum(Wasm.Comdat.Symbol.Type, bytes, pos);
282 const index, pos = readLeb(u32, bytes, pos);
283 if (true) @panic("TODO rebase index depending on kind");
284 symbol.* = .{
285 .kind = kind,
286 .index = index,
287 };
288 }
289 comdat.* = .{
290 .name = try wasm.internString(name),
291 .flags = flags,
292 .symbols = .{
293 .off = start_off,
294 .len = @intCast(wasm.object_comdat_symbols.items.len - start_off),
295 },
296 };
297 }
371 },298 },
372 };299 .symbol_table => {
373300 for (try ss.symbol_table.addManyAsSlice(gpa, count)) |*symbol| {
374 import.* = .{301 const tag, pos = readEnum(Symbol.Tag, bytes, pos);
375 .module_name = try wasm.internString(module_name),302 const flags, pos = readLeb(u32, bytes, pos);
376 .name = try wasm.internString(name),303 symbol.* = .{
377 .kind = kind_value,304 .flags = @bitCast(flags),
378 };305 .name = .none,
379 }306 .pointee = undefined,
380 try assertEnd(reader);307 };
381 },308 symbol.flags.initZigSpecific(must_link, gc_sections);
382 .function => {309
383 for (try readVec(&parser.object.functions, reader, gpa)) |*func| {310 switch (tag) {
384 func.* = .{ .type_index = try readLeb(u32, reader) };311 .data => {
385 }312 const name, pos = readBytes(bytes, pos);
386 try assertEnd(reader);313 symbol.name = (try wasm.internString(name)).toOptional();
387 },314 if (symbol.flags.undefined) {
388 .table => {315 symbol.pointee = .data_import;
389 for (try readVec(&parser.object.tables, reader, gpa)) |*table| {316 } else {
390 table.* = .{317 const segment_index, pos = readLeb(u32, bytes, pos);
391 .reftype = try readEnum(std.wasm.RefType, reader),318 const segment_offset, pos = readLeb(u32, bytes, pos);
392 .limits = try readLimits(reader),319 const size, pos = readLeb(u32, bytes, pos);
393 };320
394 }321 symbol.pointee = .{ .data = .{
395 try assertEnd(reader);322 .index = @enumFromInt(data_segment_start + segment_index),
396 },323 .segment_offset = segment_offset,
397 .memory => {324 .size = size,
398 for (try readVec(&parser.object.memories, reader, gpa)) |*memory| {325 } };
399 memory.* = .{ .limits = try readLimits(reader) };326 }
400 }327 },
401 try assertEnd(reader);328 .section => {
402 },329 const local_section, pos = readLeb(u32, bytes, pos);
403 .global => {330 const section: Wasm.SectionIndex = @enumFromInt(local_section_index_base + local_section);
404 for (try readVec(&parser.object.globals, reader, gpa)) |*global| {331 symbol.pointee = .{ .section = section };
405 global.* = .{332 },
406 .global_type = .{333
407 .valtype = try readEnum(std.wasm.Valtype, reader),334 .function => {
408 .mutable = (try reader.readByte()) == 0x01,335 const local_index, pos = readLeb(u32, bytes, pos);
336 if (symbol.flags.undefined) {
337 symbol.pointee = .{ .function_import = @enumFromInt(local_index) };
338 if (flags.explicit_name) {
339 const name, pos = readBytes(bytes, pos);
340 symbol.name = (try wasm.internString(name)).toOptional();
341 }
342 } else {
343 symbol.pointee = .{ .function = @enumFromInt(functions_start + local_index) };
344 const name, pos = readBytes(bytes, pos);
345 symbol.name = (try wasm.internString(name)).toOptional();
346 }
347 },
348 .global => {
349 const local_index, pos = readLeb(u32, bytes, pos);
350 if (symbol.flags.undefined) {
351 symbol.pointee = .{ .global_import = @enumFromInt(global_imports_start + local_index) };
352 if (flags.explicit_name) {
353 const name, pos = readBytes(bytes, pos);
354 symbol.name = (try wasm.internString(name)).toOptional();
355 }
356 } else {
357 symbol.pointee = .{ .global = @enumFromInt(globals_start + local_index) };
358 const name, pos = readBytes(bytes, pos);
359 symbol.name = (try wasm.internString(name)).toOptional();
360 }
361 },
362 .table => {
363 table_count += 1;
364 const local_index, pos = readLeb(u32, bytes, pos);
365 if (symbol.flags.undefined) {
366 symbol.pointee = .{ .table_import = @enumFromInt(table_imports_start + local_index) };
367 if (flags.explicit_name) {
368 const name, pos = readBytes(bytes, pos);
369 symbol.name = (try wasm.internString(name)).toOptional();
370 }
371 } else {
372 symbol.pointee = .{ .table = @enumFromInt(tables_start + local_index) };
373 const name, pos = readBytes(bytes, pos);
374 symbol.name = (try wasm.internString(name)).toOptional();
375 }
376 },
377 else => {
378 log.debug("unrecognized symbol type tag: {x}", .{tag});
379 return error.UnrecognizedSymbolType;
380 },
381 }
382 log.debug("found symbol: {}", .{symbol});
383 }
409 },384 },
410 .init = try readInit(reader),
411 };
412 }
413 try assertEnd(reader);
414 },
415 .@"export" => {
416 for (try readVec(&parser.object.exports, reader, gpa)) |*exp| {
417 const name_len = try readLeb(u32, reader);
418 const name = try gpa.alloc(u8, name_len);
419 defer gpa.free(name);
420 try reader.readNoEof(name);
421 exp.* = .{
422 .name = try wasm.internString(name),
423 .kind = try readEnum(std.wasm.ExternalKind, reader),
424 .index = try readLeb(u32, reader),
425 };
426 }
427 try assertEnd(reader);
428 },
429 .start => {
430 parser.object.start = try readLeb(u32, reader);
431 try assertEnd(reader);
432 },
433 .element => {
434 for (try readVec(&parser.object.elements, reader, gpa)) |*elem| {
435 elem.table_index = try readLeb(u32, reader);
436 elem.offset = try readInit(reader);
437
438 for (try readVec(&elem.func_indexes, reader, gpa)) |*idx| {
439 idx.* = try readLeb(u32, reader);
440 }385 }
441 }386 }
442 try assertEnd(reader);387 } else if (std.mem.startsWith(u8, section_name, "reloc.")) {
443 },388 // 'The "reloc." custom sections must come after the "linking" custom section'
444 .code => {389 if (!saw_linking_section) return error.RelocBeforeLinkingSection;
445 const start = reader.context.bytes_left;390
446 var index: u32 = 0;391 // "Relocation sections start with an identifier specifying
447 const count = try readLeb(u32, reader);392 // which section they apply to, and must be sequenced in
448 const imported_function_count = parser.object.imported_functions_count;393 // the module after that section."
449 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);394 // "Relocation sections can only target code, data and custom sections."
450 defer relocatable_data.deinit();395 const local_section, pos = readLeb(u32, bytes, pos);
451 while (index < count) : (index += 1) {396 const count, pos = readLeb(u32, bytes, pos);
452 const code_len = try readLeb(u32, reader);397 const section: Wasm.SectionIndex = @enumFromInt(local_section_index_base + local_section);
453 const offset = @as(u32, @intCast(start - reader.context.bytes_left));398
454 const data = try gpa.alloc(u8, code_len);399 log.debug("found {d} relocations for section={d}", .{ count, section });
455 errdefer gpa.free(data);400
456 try reader.readNoEof(data);401 var prev_offset: u32 = 0;
457 relocatable_data.appendAssumeCapacity(.{402 try wasm.relocations.ensureUnusedCapacity(gpa, count);
458 .type = .code,403 for (0..count) |_| {
459 .data = data.ptr,404 const tag: Wasm.Relocation.Tag = @enumFromInt(bytes[pos]);
460 .size = code_len,405 pos += 1;
461 .index = imported_function_count + index,406 const offset, pos = readLeb(u32, bytes, pos);
462 .offset = offset,407 const index, pos = readLeb(u32, bytes, pos);
463 .section_index = section_index,408
464 });409 if (offset < prev_offset)
465 }410 return diags.failParse(path, "relocation entries not sorted by offset", .{});
466 try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice());411 prev_offset = offset;
467 },412
468 .data => {413 switch (tag) {
469 const start = reader.context.bytes_left;414 .MEMORY_ADDR_LEB,
470 var index: u32 = 0;415 .MEMORY_ADDR_SLEB,
471 const count = try readLeb(u32, reader);416 .MEMORY_ADDR_I32,
472 var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);417 .MEMORY_ADDR_REL_SLEB,
473 defer relocatable_data.deinit();418 .MEMORY_ADDR_LEB64,
474 while (index < count) : (index += 1) {419 .MEMORY_ADDR_SLEB64,
475 const flags = try readLeb(u32, reader);420 .MEMORY_ADDR_I64,
476 const data_offset = try readInit(reader);421 .MEMORY_ADDR_REL_SLEB64,
477 _ = flags; // TODO: Do we need to check flags to detect passive/active memory?422 .MEMORY_ADDR_TLS_SLEB,
478 _ = data_offset;423 .MEMORY_ADDR_LOCREL_I32,
479 const data_len = try readLeb(u32, reader);424 .MEMORY_ADDR_TLS_SLEB64,
480 const offset = @as(u32, @intCast(start - reader.context.bytes_left));425 .FUNCTION_OFFSET_I32,
481 const data = try gpa.alloc(u8, data_len);426 .SECTION_OFFSET_I32,
482 errdefer gpa.free(data);427 => {
483 try reader.readNoEof(data);428 const addend: i32, pos = readLeb(i32, bytes, pos);
484 relocatable_data.appendAssumeCapacity(.{429 wasm.relocations.appendAssumeCapacity(.{
485 .type = .data,430 .tag = tag,
486 .data = data.ptr,431 .offset = offset,
487 .size = data_len,432 .pointee = .{ .section = ss.symbol_table.items[index].pointee.section },
488 .index = index,433 .addend = addend,
489 .offset = offset,434 });
490 .section_index = section_index,435 },
491 });436 .TYPE_INDEX_LEB => {
437 wasm.relocations.appendAssumeCapacity(.{
438 .tag = tag,
439 .offset = offset,
440 .pointee = .{ .type_index = ss.func_types.items[index] },
441 .addend = undefined,
442 });
443 },
444 .FUNCTION_INDEX_LEB,
445 .GLOBAL_INDEX_LEB,
446 => {
447 wasm.relocations.appendAssumeCapacity(.{
448 .tag = tag,
449 .offset = offset,
450 .pointee = .{ .symbol_name = ss.symbol_table.items[index].name.unwrap().? },
451 .addend = undefined,
452 });
453 },
454 }
492 }455 }
493 try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice());
494 },
495 else => try parser.reader.reader().skipBytes(len, .{}),
496 }
497 } else |err| switch (err) {
498 error.EndOfStream => {}, // finished parsing the file
499 else => |e| return e,
500 }
501 if (!saw_linking_section) return error.MissingLinkingSection;
502 }
503
504 /// Based on the "features" custom section, parses it into a list of
505 /// features that tell the linker what features were enabled and may be mandatory
506 /// to be able to link.
507 /// Logs an info message when an undefined feature is detected.
508 fn parseFeatures(parser: *Parser, gpa: Allocator) !void {
509 const diags = &parser.wasm.base.comp.link_diags;
510 const reader = parser.reader.reader();
511 for (try readVec(&parser.object.features, reader, gpa)) |*feature| {
512 const prefix = try readEnum(Wasm.Feature.Prefix, reader);
513 const name_len = try leb.readUleb128(u32, reader);
514 const name = try gpa.alloc(u8, name_len);
515 defer gpa.free(name);
516 try reader.readNoEof(name);
517
518 const tag = Wasm.known_features.get(name) orelse {
519 return diags.failParse(parser.object.path, "object file contains unknown feature: {s}", .{name});
520 };
521 feature.* = .{
522 .prefix = prefix,
523 .tag = tag,
524 };
525 }
526 }
527
528 /// Parses a "reloc" custom section into a list of relocations.
529 /// The relocations are mapped into `Object` where the key is the section
530 /// they apply to.
531 fn parseRelocations(parser: *Parser, gpa: Allocator) !void {
532 const reader = parser.reader.reader();
533 const section = try leb.readUleb128(u32, reader);
534 const count = try leb.readUleb128(u32, reader);
535 const relocations = try gpa.alloc(Wasm.Relocation, count);
536 errdefer gpa.free(relocations);
537
538 log.debug("Found {d} relocations for section ({d})", .{
539 count,
540 section,
541 });
542456
543 for (relocations) |*relocation| {457 try wasm.object_relocations_table.putNoClobber(gpa, section, .{
544 const rel_type = try reader.readByte();458 .off = @intCast(wasm.relocations.items.len - count),
545 const rel_type_enum = std.meta.intToEnum(Wasm.Relocation.RelocationType, rel_type) catch return error.MalformedSection;459 .len = count,
546 relocation.* = .{
547 .relocation_type = rel_type_enum,
548 .offset = try leb.readUleb128(u32, reader),
549 .index = try leb.readUleb128(u32, reader),
550 .addend = if (rel_type_enum.addendIsPresent()) try leb.readIleb128(i32, reader) else 0,
551 };
552 log.debug("Found relocation: type({s}) offset({d}) index({d}) addend({?d})", .{
553 @tagName(relocation.relocation_type),
554 relocation.offset,
555 relocation.index,
556 relocation.addend,
557 });
558 }
559
560 try parser.object.relocations.putNoClobber(gpa, section, relocations);
561 }
562
563 /// Parses the "linking" custom section. Versions that are not
564 /// supported will be an error. `payload_size` is required to be able
565 /// to calculate the subsections we need to parse, as that data is not
566 /// available within the section itparser.
567 fn parseMetadata(parser: *Parser, gpa: Allocator, payload_size: usize) !void {
568 var limited = std.io.limitedReader(parser.reader.reader(), payload_size);
569 const limited_reader = limited.reader();
570
571 const version = try leb.readUleb128(u32, limited_reader);
572 log.debug("Link meta data version: {d}", .{version});
573 if (version != 2) return error.UnsupportedVersion;
574
575 while (limited.bytes_left > 0) {
576 try parser.parseSubsection(gpa, limited_reader);
577 }
578 }
579
580 /// Parses a `spec.Subsection`.
581 /// The `reader` param for this is to provide a `LimitedReader`, which allows
582 /// us to only read until a max length.
583 ///
584 /// `parser` is used to provide access to other sections that may be needed,
585 /// such as access to the `import` section to find the name of a symbol.
586 fn parseSubsection(parser: *Parser, gpa: Allocator, reader: anytype) !void {
587 const wasm = parser.wasm;
588 const sub_type = try leb.readUleb128(u8, reader);
589 log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))});
590 const payload_len = try leb.readUleb128(u32, reader);
591 if (payload_len == 0) return;
592
593 var limited = std.io.limitedReader(reader, payload_len);
594 const limited_reader = limited.reader();
595
596 // every subsection contains a 'count' field
597 const count = try leb.readUleb128(u32, limited_reader);
598
599 switch (@as(Wasm.SubsectionType, @enumFromInt(sub_type))) {
600 .WASM_SEGMENT_INFO => {
601 const segments = try gpa.alloc(Wasm.NamedSegment, count);
602 errdefer gpa.free(segments);
603 for (segments) |*segment| {
604 const name_len = try leb.readUleb128(u32, reader);
605 const name = try gpa.alloc(u8, name_len);
606 errdefer gpa.free(name);
607 try reader.readNoEof(name);
608 segment.* = .{
609 .name = name,
610 .alignment = @enumFromInt(try leb.readUleb128(u32, reader)),
611 .flags = try leb.readUleb128(u32, reader),
612 };
613 log.debug("Found segment: {s} align({d}) flags({b})", .{
614 segment.name,
615 segment.alignment,
616 segment.flags,
617 });460 });
618461 } else if (std.mem.eql(u8, section_name, "target_features")) {
619 // support legacy object files that specified being TLS by the name instead of the TLS flag.462 opt_features, pos = try parseFeatures(wasm, bytes, pos, path);
620 if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) {463 } else if (std.mem.startsWith(u8, section_name, ".debug")) {
621 // set the flag so we can simply check for the flag in the rest of the linker.464 const debug_content = bytes[pos..section_end];
622 segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS);465 pos = section_end;
466
467 const data_off: u32 = @enumFromInt(wasm.string_bytes.items.len);
468 try wasm.string_bytes.appendSlice(gpa, debug_content);
469
470 try wasm.object_custom_segments.put(gpa, section_index, .{
471 .data_off = data_off,
472 .flags = .{
473 .data_len = @intCast(debug_content.len),
474 .represented = false, // set when scanning symbol table
475 },
476 .section_name = try wasm.internString(section_name),
477 });
478 } else {
479 pos = section_end;
480 }
481 },
482 .type => {
483 const func_types_len, pos = readLeb(u32, bytes, pos);
484 for (ss.func_types.addManyAsSlice(gpa, func_types_len)) |*func_type| {
485 if (bytes[pos] != std.wasm.function_type) return error.ExpectedFuncType;
486 pos += 1;
487
488 const params, pos = readBytes(bytes, pos);
489 const returns, pos = readBytes(bytes, pos);
490 func_type.* = try wasm.addFuncType(.{
491 .params = .fromString(try wasm.internString(params)),
492 .returns = .fromString(try wasm.internString(returns)),
493 });
494 }
495 },
496 .import => {
497 const imports_len, pos = readLeb(u32, bytes, pos);
498 for (0..imports_len) |_| {
499 const module_name, pos = readBytes(bytes, pos);
500 const name, pos = readBytes(bytes, pos);
501 const kind, pos = readEnum(std.wasm.ExternalKind, bytes, pos);
502 const interned_module_name = try wasm.internString(module_name);
503 const interned_name = try wasm.internString(name);
504 switch (kind) {
505 .function => {
506 const function, pos = readLeb(u32, bytes, pos);
507 try ss.function_imports.append(gpa, .{
508 .module_name = interned_module_name,
509 .name = interned_name,
510 .index = function,
511 });
512 },
513 .memory => {
514 const limits, pos = readLimits(bytes, pos);
515 try wasm.object_memory_imports.append(gpa, .{
516 .module_name = interned_module_name,
517 .name = interned_name,
518 .limits_min = limits.min,
519 .limits_max = limits.max,
520 .limits_has_max = limits.flags.has_max,
521 .limits_is_shared = limits.flags.is_shared,
522 });
523 },
524 .global => {
525 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
526 const mutable = bytes[pos] == 0x01;
527 pos += 1;
528 try wasm.object_global_imports.append(gpa, .{
529 .module_name = interned_module_name,
530 .name = interned_name,
531 .mutable = mutable,
532 .valtype = valtype,
533 });
534 },
535 .table => {
536 const reftype, pos = readEnum(std.wasm.RefType, bytes, pos);
537 const limits, pos = readLimits(bytes, pos);
538 try wasm.object_table_imports.append(gpa, .{
539 .module_name = interned_module_name,
540 .name = interned_name,
541 .limits_min = limits.min,
542 .limits_max = limits.max,
543 .limits_has_max = limits.flags.has_max,
544 .limits_is_shared = limits.flags.is_shared,
545 .reftype = reftype,
546 });
547 },
623 }548 }
624 }549 }
625 parser.object.segment_info = segments;
626 },550 },
627 .WASM_INIT_FUNCS => {551 .function => {
628 const funcs = try gpa.alloc(Wasm.InitFunc, count);552 const functions_len, pos = readLeb(u32, bytes, pos);
629 errdefer gpa.free(funcs);553 for (try ss.func_type_indexes.addManyAsSlice(gpa, functions_len)) |*func_type_index| {
630 for (funcs) |*func| {554 func_type_index.*, pos = readLeb(u32, bytes, pos);
631 func.* = .{555 }
632 .priority = try leb.readUleb128(u32, reader),556 },
633 .symbol_index = try leb.readUleb128(u32, reader),557 .table => {
558 const tables_len, pos = readLeb(u32, bytes, pos);
559 for (try wasm.object_tables.addManyAsSlice(gpa, tables_len)) |*table| {
560 const reftype, pos = readEnum(std.wasm.RefType, bytes, pos);
561 const limits, pos = readLimits(bytes, pos);
562 table.* = .{
563 .reftype = reftype,
564 .limits = limits,
634 };565 };
635 log.debug("Found function - prio: {d}, index: {d}", .{ func.priority, func.symbol_index });
636 }566 }
637 parser.object.init_funcs = funcs;
638 },567 },
639 .WASM_COMDAT_INFO => {568 .memory => {
640 const comdats = try gpa.alloc(Wasm.Comdat, count);569 const memories_len, pos = readLeb(u32, bytes, pos);
641 errdefer gpa.free(comdats);570 for (try wasm.object_memories.addManyAsSlice(gpa, memories_len)) |*memory| {
642 for (comdats) |*comdat| {571 const limits, pos = readLimits(bytes, pos);
643 const name_len = try leb.readUleb128(u32, reader);572 memory.* = .{ .limits = limits };
644 const name = try gpa.alloc(u8, name_len);573 }
645 errdefer gpa.free(name);574 },
646 try reader.readNoEof(name);575 .global => {
647576 const globals_len, pos = readLeb(u32, bytes, pos);
648 const flags = try leb.readUleb128(u32, reader);577 for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| {
649 if (flags != 0) {578 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
650 return error.UnexpectedValue;579 const mutable = bytes[pos] == 0x01;
651 }580 pos += 1;
652581 const expr, pos = try readInit(wasm, bytes, pos);
653 const symbol_count = try leb.readUleb128(u32, reader);582 global.* = .{
654 const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count);583 .valtype = valtype,
655 errdefer gpa.free(symbols);584 .mutable = mutable,
656 for (symbols) |*symbol| {585 .expr = expr,
657 symbol.* = .{
658 .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))),
659 .index = try leb.readUleb128(u32, reader),
660 };
661 }
662
663 comdat.* = .{
664 .name = name,
665 .flags = flags,
666 .symbols = symbols,
667 };586 };
668 }587 }
669
670 parser.object.comdat_info = comdats;
671 },588 },
672 .WASM_SYMBOL_TABLE => {589 .@"export" => {
673 var symbols = try std.ArrayList(Symbol).initCapacity(gpa, count);590 const exports_len, pos = readLeb(u32, bytes, pos);
674591 // TODO: instead, read into scratch space, and then later
675 var i: usize = 0;592 // add this data as if it were extra symbol table entries,
676 while (i < count) : (i += 1) {593 // but allow merging with existing symbol table data if the name matches.
677 const symbol = symbols.addOneAssumeCapacity();594 for (try wasm.object_exports.addManyAsSlice(gpa, exports_len)) |*exp| {
678 symbol.* = try parser.parseSymbol(gpa, reader);595 const name, pos = readBytes(bytes, pos);
679 log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{596 const kind: std.wasm.ExternalKind = @enumFromInt(bytes[pos]);
680 @tagName(symbol.tag),597 pos += 1;
681 wasm.stringSlice(symbol.name),598 const index, pos = readLeb(u32, bytes, pos);
682 symbol.flags,599 const rebased_index = index + switch (kind) {
683 });600 .function => functions_start,
601 .table => tables_start,
602 .memory => memories_start,
603 .global => globals_start,
604 };
605 exp.* = .{
606 .name = try wasm.internString(name),
607 .kind = kind,
608 .index = rebased_index,
609 };
684 }610 }
685611 },
686 // we found all symbols, check for indirect function table612 .start => {
687 // in case of an MVP object file613 const index, pos = readLeb(u32, bytes, pos);
688 if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm)) |symbol| {614 start_function = @enumFromInt(functions_start + index);
689 try symbols.append(symbol);615 },
690 log.debug("Found legacy indirect function table. Created symbol", .{});616 .element => {
617 log.warn("unimplemented: element section in {}", .{path});
618 pos = section_end;
619 },
620 .code => {
621 const start = pos;
622 const count, pos = readLeb(u32, bytes, pos);
623 for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| {
624 const code_len, pos = readLeb(u32, bytes, pos);
625 const offset: u32 = @intCast(pos - start);
626 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..code_len]);
627 pos += code_len;
628 elem.* = .{
629 .flags = .{}, // populated from symbol table
630 .name = .none, // populated from symbol table
631 .type_index = undefined, // populated from func_types
632 .code = payload,
633 .offset = offset,
634 .section_index = section_index,
635 .source_location = source_location,
636 };
691 }637 }
692638 },
693 // Not all debug sections may be represented by a symbol, for those sections639 .data => {
694 // we manually create a symbol.640 const start = pos;
695 if (parser.object.relocatable_data.get(.custom)) |custom_sections| {641 const count, pos = readLeb(u32, bytes, pos);
696 for (custom_sections) |*data| {642 for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| {
697 if (!data.represented) {643 const flags, pos = readEnum(DataSegmentFlags, bytes, pos);
698 const name = wasm.castToString(data.index);644 if (flags == .active_memidx) {
699 try symbols.append(.{645 const memidx, pos = readLeb(u32, bytes, pos);
700 .name = name,646 if (memidx != 0) return diags.failParse(path, "data section uses mem index {d}", .{memidx});
701 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
702 .tag = .section,
703 .virtual_address = 0,
704 .index = data.section_index,
705 });
706 data.represented = true;
707 log.debug("Created synthetic custom section symbol for '{s}'", .{
708 wasm.stringSlice(name),
709 });
710 }
711 }647 }
648 //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos };
649 if (flags != .passive) pos = try skipInit(bytes, pos);
650 const data_len, pos = readLeb(u32, bytes, pos);
651 const segment_offset: u32 = @intCast(pos - start);
652 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]);
653 pos += data_len;
654 elem.* = .{
655 .payload = payload,
656 .segment_offset = segment_offset,
657 .section_index = section_index,
658 .name = .none, // Populated from symbol table
659 .flags = .{}, // Populated from symbol table and segment_info
660 };
712 }661 }
713
714 parser.object.symtable = try symbols.toOwnedSlice();
715 },662 },
663 else => pos = section_end,
716 }664 }
665 if (pos != section_end) return error.MalformedSection;
717 }666 }
667 if (!saw_linking_section) return error.MissingLinkingSection;
718668
719 /// Parses the symbol information based on its kind,669 wasm.object_total_sections = local_section_index_base + local_section_index;
720 /// requires access to `Object` to find the name of a symbol when it's
721 /// an import and flag `WASM_SYM_EXPLICIT_NAME` is not set.
722 fn parseSymbol(parser: *Parser, gpa: Allocator, reader: anytype) !Symbol {
723 const wasm = parser.wasm;
724 const tag: Symbol.Tag = @enumFromInt(try leb.readUleb128(u8, reader));
725 const flags = try leb.readUleb128(u32, reader);
726 var symbol: Symbol = .{
727 .flags = flags,
728 .tag = tag,
729 .name = undefined,
730 .index = undefined,
731 .virtual_address = undefined,
732 };
733670
734 switch (tag) {671 if (has_tls) {
735 .data => {672 const cpu_features = wasm.base.comp.root_mod.resolved_target.result.cpu.features;
736 const name_len = try leb.readUleb128(u32, reader);673 if (!std.Target.wasm.featureSetHas(cpu_features, .atomics))
737 const name = try gpa.alloc(u8, name_len);674 return diags.failParse(path, "object has TLS segment but target CPU feature atomics is disabled", .{});
738 defer gpa.free(name);675 if (!std.Target.wasm.featureSetHas(cpu_features, .bulk_memory))
739 try reader.readNoEof(name);676 return diags.failParse(path, "object has TLS segment but target CPU feature bulk_memory is disabled", .{});
740 symbol.name = try wasm.internString(name);677 }
741678
742 // Data symbols only have the following fields if the symbol is defined679 const features = opt_features orelse return error.MissingFeatures;
743 if (symbol.isDefined()) {680 if (true) @panic("iterate features, match against target features");
744 symbol.index = try leb.readUleb128(u32, reader);681
745 // @TODO: We should verify those values682 // Apply function type information.
746 _ = try leb.readUleb128(u32, reader);683 for (ss.func_types.items, wasm.object_functions.items[functions_start..]) |func_type, *func| {
747 _ = try leb.readUleb128(u32, reader);684 func.type_index = func_type;
685 }
686
687 // Apply symbol table information.
688 for (ss.symbol_table.items) |symbol| switch (symbol.pointee) {
689 .function_import => |index| {
690 const ptr = index.ptr(ss);
691 const name = symbol.name.unwrap().?;
692 if (symbol.flags.binding == .local) {
693 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
694 continue;
695 }
696 const gop = try wasm.object_function_imports.getOrPut(gpa, name);
697 const fn_ty_index = ptr.function_index.ptr(ss).*;
698 if (gop.found_existing) {
699 if (gop.value_ptr.type != fn_ty_index) {
700 var err = try diags.addErrorWithNotes(2);
701 try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)});
702 try err.addSrcNote(gop.value_ptr.source_location, "imported as {} here", .{gop.value_ptr.type.fmt(wasm)});
703 try err.addSrcNote(source_location, "imported as {} here", .{fn_ty_index.fmt(wasm)});
704 continue;
748 }705 }
749 },706 if (gop.value_ptr.module_name != ptr.module_name) {
750 .section => {707 var err = try diags.addErrorWithNotes(2);
751 symbol.index = try leb.readUleb128(u32, reader);708 try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
752 const section_data = parser.object.relocatable_data.get(.custom).?;709 try err.addSrcNote(gop.value_ptr.source_location, "module '{s}' here", .{gop.value_ptr.module_name.slice(wasm)});
753 for (section_data) |*data| {710 try err.addSrcNote(source_location, "module '{s}' here", .{ptr.module_name.slice(wasm)});
754 if (data.section_index == symbol.index) {711 continue;
755 symbol.name = wasm.castToString(data.index);
756 data.represented = true;
757 break;
758 }
759 }712 }
760 },713 if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
761 else => {714 if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false;
762 symbol.index = try leb.readUleb128(u32, reader);715 if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true;
763 const is_undefined = symbol.isUndefined();716 } else {
764 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);717 gop.value_ptr.* = .{
765 symbol.name = if (!is_undefined or (is_undefined and explicit_name)) name: {718 .flags = symbol.flags,
766 const name_len = try leb.readUleb128(u32, reader);719 .module_name = ptr.module_name,
767 const name = try gpa.alloc(u8, name_len);720 .source_location = source_location,
768 defer gpa.free(name);721 .resolution = .unresolved,
769 try reader.readNoEof(name);722 .type = fn_ty_index,
770 break :name try wasm.internString(name);723 };
771 } else parser.object.findImport(symbol).name;724 }
772 },725 },
726 .function => |index| {
727 assert(!symbol.flags.undefined);
728 const ptr = index.ptr();
729 ptr.name = symbol.name;
730 ptr.flags = symbol.flags;
731 if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
732 const name = symbol.name.unwrap().?;
733 const gop = try wasm.object_function_imports.getOrPut(gpa, name);
734 if (gop.found_existing) {
735 if (gop.value_ptr.type != ptr.type_index) {
736 var err = try diags.addErrorWithNotes(2);
737 try err.addMsg("function signature mismatch: {s}", .{name.slice(wasm)});
738 try err.addSrcNote(gop.value_ptr.source_location, "exported as {} here", .{ptr.type_index.fmt(wasm)});
739 const word = if (gop.value_ptr.resolution == .none) "imported" else "exported";
740 try err.addSrcNote(source_location, "{s} as {} here", .{ word, gop.value_ptr.type.fmt(wasm) });
741 continue;
742 }
743 if (gop.value_ptr.resolution == .none or gop.value_ptr.flags.binding == .weak) {
744 // Intentional: if they're both weak, take the last one.
745 gop.value_ptr.source_location = source_location;
746 gop.value_ptr.module_name = host_name;
747 gop.value_ptr.resolution = .fromObjectFunction(index);
748 continue;
749 }
750 var err = try diags.addErrorWithNotes(2);
751 try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
752 try err.addSrcNote(gop.value_ptr.source_location, "exported as {} here", .{ptr.type_index.fmt(wasm)});
753 try err.addSrcNote(source_location, "exported as {} here", .{gop.value_ptr.type.fmt(wasm)});
754 continue;
755 } else {
756 gop.value_ptr.* = .{
757 .flags = symbol.flags,
758 .module_name = host_name,
759 .source_location = source_location,
760 .resolution = .fromObjectFunction(index),
761 .type = ptr.type_index,
762 };
763 }
764 },
765
766 inline .global, .global_import, .table, .table_import => |i| {
767 const ptr = i.ptr(wasm);
768 ptr.name = symbol.name;
769 ptr.flags = symbol.flags;
770 if (symbol.flags.undefined and symbol.flags.binding == .local) {
771 const name = wasm.stringSlice(ptr.name.unwrap().?);
772 diags.addParseError(path, "local symbol '{s}' references import", .{name});
773 }
774 },
775 .section => |i| {
776 // Name is provided by the section directly; symbol table does not have it.
777 const ptr = i.ptr(wasm);
778 ptr.flags = symbol.flags;
779 if (symbol.flags.undefined and symbol.flags.binding == .local) {
780 const name = wasm.stringSlice(ptr.name);
781 diags.addParseError(path, "local symbol '{s}' references import", .{name});
782 }
783 },
784 .data_import => {
785 const name = symbol.name.unwrap().?;
786 log.warn("TODO data import '{s}'", .{name.slice(wasm)});
787 },
788 .data => |data| {
789 const ptr = data.ptr(wasm);
790 const is_passive = ptr.flags.is_passive;
791 ptr.name = symbol.name;
792 ptr.flags = symbol.flags;
793 ptr.flags.is_passive = is_passive;
794 ptr.offset = data.segment_offset;
795 ptr.size = data.size;
796 },
797 };
798
799 // Apply segment_info.
800 for (wasm.object_data_segments.items[data_segment_start..], ss.segment_info.items) |*data, info| {
801 data.name = info.name.toOptional();
802 data.flags.strings = info.flags.strings;
803 data.flags.tls = data.flags.tls or info.flags.tls;
804 data.flags.no_strip = info.flags.retain;
805 data.flags.alignment = info.flags.alignment;
806 if (data.flags.undefined and data.flags.binding == .local) {
807 const name = wasm.stringSlice(info.name);
808 diags.addParseError(path, "local symbol '{s}' references import", .{name});
773 }809 }
774 return symbol;
775 }810 }
776};
777811
778/// First reads the count from the reader and then allocate812 // Check for indirect function table in case of an MVP object file.
779/// a slice of ptr child's element type.813 legacy_indirect_function_table: {
780fn readVec(ptr: anytype, reader: anytype, gpa: Allocator) ![]ElementType(@TypeOf(ptr)) {814 const table_imports = wasm.object_table_imports.items[table_imports_start..];
781 const len = try readLeb(u32, reader);815 // If there is a symbol for each import table, this is not a legacy object file.
782 const slice = try gpa.alloc(ElementType(@TypeOf(ptr)), len);816 if (table_imports.len == table_count) break :legacy_indirect_function_table;
783 ptr.* = slice;817 if (table_count != 0) {
784 return slice;818 return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
785}819 table_imports.len, table_count,
820 });
821 }
822 // MVP object files cannot have any table definitions, only
823 // imports (for the indirect function table).
824 const tables = wasm.object_tables.items[tables_start..];
825 if (tables.len > 0) {
826 return diags.failParse(path, "table definition without representing table symbols", .{});
827 }
828 if (table_imports.len != 1) {
829 return diags.failParse(path, "found more than one table import, but no representing table symbols", .{});
830 }
831 const table_import_name = table_imports[0].name;
832 if (table_import_name != wasm.preloaded_strings.__indirect_function_table) {
833 return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{
834 wasm.stringSlice(table_import_name),
835 });
836 }
837 table_imports[0].flags = .{
838 .undefined = true,
839 .no_strip = true,
840 };
841 }
786842
787fn ElementType(comptime ptr: type) type {843 for (wasm.object_init_funcs.items[init_funcs_start..]) |init_func| {
788 return meta.Elem(meta.Child(ptr));844 const func = init_func.function_index.ptr(wasm);
789}845 const params = func.type_index.ptr(wasm).params.slice(wasm);
846 if (params.len != 0) diags.addError("constructor function '{s}' has non-empty parameter list", .{
847 func.name.slice(wasm).?,
848 });
849 }
790850
791/// Uses either `readIleb128` or `readUleb128` depending on the851 return .{
792/// signedness of the given type `T`.852 .version = version,
793/// Asserts `T` is an integer.853 .path = path,
794fn readLeb(comptime T: type, reader: anytype) !T {854 .archive_member_name = archive_member_name,
795 return switch (@typeInfo(T).int.signedness) {855 .start_function = start_function,
796 .signed => try leb.readIleb128(T, reader),856 .features = features,
797 .unsigned => try leb.readUleb128(T, reader),857 .imports = .{
858 .off = imports_start,
859 .len = @intCast(wasm.object_imports.items.len - imports_start),
860 },
861 .functions = .{
862 .off = functions_start,
863 .len = @intCast(wasm.functions.items.len - functions_start),
864 },
865 .tables = .{
866 .off = tables_start,
867 .len = @intCast(wasm.object_tables.items.len - tables_start),
868 },
869 .memories = .{
870 .off = memories_start,
871 .len = @intCast(wasm.object_memories.items.len - memories_start),
872 },
873 .globals = .{
874 .off = globals_start,
875 .len = @intCast(wasm.object_globals.items.len - globals_start),
876 },
877 .init_funcs = .{
878 .off = init_funcs_start,
879 .len = @intCast(wasm.object_init_funcs.items.len - init_funcs_start),
880 },
881 .comdats = .{
882 .off = comdats_start,
883 .len = @intCast(wasm.object_comdats.items.len - comdats_start),
884 },
885 .custom_segments = .{
886 .off = custom_segment_start,
887 .len = @intCast(wasm.object_custom_segments.items.len - custom_segment_start),
888 },
889 .local_section_index_base = local_section_index_base,
798 };890 };
799}891}
800892
801/// Reads an enum type from the given reader.893/// Based on the "features" custom section, parses it into a list of
802/// Asserts `T` is an enum894/// features that tell the linker what features were enabled and may be mandatory
803fn readEnum(comptime T: type, reader: anytype) !T {895/// to be able to link.
804 switch (@typeInfo(T)) {896fn parseFeatures(
805 .@"enum" => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))),897 wasm: *Wasm,
806 else => @compileError("T must be an enum. Instead was given type " ++ @typeName(T)),898 bytes: []const u8,
899 start_pos: usize,
900 path: Path,
901) error{ OutOfMemory, LinkFailure }!struct { Wasm.Feature.Set, usize } {
902 const gpa = wasm.base.comp.gpa;
903 const diags = &wasm.base.comp.link_diags;
904 const features_len, var pos = readLeb(u32, bytes, start_pos);
905 // This temporary allocation could be avoided by using the string_bytes buffer as a scratch space.
906 const feature_buffer = try gpa.alloc(Wasm.Feature, features_len);
907 defer gpa.free(feature_buffer);
908 for (feature_buffer) |*feature| {
909 const prefix: Wasm.Feature.Prefix = switch (bytes[pos]) {
910 '-' => .@"-",
911 '+' => .@"+",
912 '=' => .@"=",
913 else => return error.InvalidFeaturePrefix,
914 };
915 pos += 1;
916 const name, pos = readBytes(bytes, pos);
917 const tag = std.meta.stringToEnum(Wasm.Feature.Tag, name) orelse {
918 return diags.failParse(path, "unrecognized wasm feature in object: {s}", .{name});
919 };
920 feature.* = .{
921 .prefix = prefix,
922 .tag = tag,
923 };
807 }924 }
925 std.mem.sortUnstable(Wasm.Feature, feature_buffer, {}, Wasm.Feature.lessThan);
926
927 return .{
928 .fromString(try wasm.internString(@bitCast(feature_buffer))),
929 pos,
930 };
808}931}
809932
810fn readLimits(reader: anytype) !std.wasm.Limits {933fn readLeb(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
811 const flags = try reader.readByte();934 var fbr = std.io.fixedBufferStream(bytes[pos..]);
812 const min = try readLeb(u32, reader);935 return .{
813 var limits: std.wasm.Limits = .{936 switch (@typeInfo(T).int.signedness) {
814 .flags = flags,937 .signed => std.leb.readIleb128(T, fbr.reader()) catch unreachable,
815 .min = min,938 .unsigned => std.leb.readUleb128(T, fbr.reader()) catch unreachable,
816 .max = undefined,939 },
940 pos + fbr.pos,
817 };941 };
818 if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
819 limits.max = try readLeb(u32, reader);
820 }
821 return limits;
822}942}
823943
824fn readInit(reader: anytype) !std.wasm.InitExpression {944fn readBytes(bytes: []const u8, start_pos: usize) struct { []const u8, usize } {
825 const opcode = try reader.readByte();945 const len, const pos = readLeb(u32, bytes, start_pos);
826 const init_expr: std.wasm.InitExpression = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {946 return .{
827 .i32_const => .{ .i32_const = try readLeb(i32, reader) },947 bytes[pos..][0..len],
828 .global_get => .{ .global_get = try readLeb(u32, reader) },948 pos + len,
829 else => @panic("TODO: initexpression for other opcodes"),
830 };949 };
950}
831951
832 if ((try readEnum(std.wasm.Opcode, reader)) != .end) return error.MissingEndForExpression;952fn readEnum(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
833 return init_expr;953 const Tag = @typeInfo(T).@"enum".tag_type;
954 const int, const new_pos = readLeb(Tag, bytes, pos);
955 return .{ @enumFromInt(int), new_pos };
834}956}
835957
836fn assertEnd(reader: anytype) !void {958fn readLimits(bytes: []const u8, start_pos: usize) struct { std.wasm.Limits, usize } {
837 var buf: [1]u8 = undefined;959 const flags = bytes[start_pos];
838 const len = try reader.read(&buf);960 const min, const max_pos = readLeb(u32, bytes, start_pos + 1);
839 if (len != 0) return error.MalformedSection;961 const max, const end_pos = if (flags.has_max) readLeb(u32, bytes, max_pos) else .{ undefined, max_pos };
840 if (reader.context.bytes_left != 0) return error.MalformedSection;962 return .{ .{
963 .flags = flags,
964 .min = min,
965 .max = max,
966 }, end_pos };
967}
968
969fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usize } {
970 const end_pos = skipInit(bytes, pos); // one after the end opcode
971 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };
972}
973
974fn skipInit(bytes: []const u8, pos: usize) !usize {
975 const opcode = bytes[pos];
976 const end_pos = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
977 .i32_const => readLeb(i32, bytes, pos + 1)[1],
978 .i64_const => readLeb(i64, bytes, pos + 1)[1],
979 .f32_const => pos + 5,
980 .f64_const => pos + 9,
981 .global_get => readLeb(u32, bytes, pos + 1)[1],
982 else => return error.InvalidInitOpcode,
983 };
984 if (readEnum(std.wasm.Opcode, bytes, end_pos) != .end) return error.InitExprMissingEnd;
985 return end_pos + 1;
841}986}
src/link/Wasm/Symbol.zig deleted-210
...@@ -1,210 +0,0 @@
1//! Represents a WebAssembly symbol. Containing all of its properties,
2//! as well as providing helper methods to determine its functionality
3//! and how it will/must be linked.
4//! The name of the symbol can be found by providing the offset, found
5//! on the `name` field, to a string table in the wasm binary or object file.
6
7/// Bitfield containings flags for a symbol
8/// Can contain any of the flags defined in `Flag`
9flags: u32,
10/// Symbol name, when the symbol is undefined the name will be taken from the import.
11/// Note: This is an index into the wasm string table.
12name: wasm.String,
13/// Index into the list of objects based on set `tag`
14/// NOTE: This will be set to `undefined` when `tag` is `data`
15/// and the symbol is undefined.
16index: u32,
17/// Represents the kind of the symbol, such as a function or global.
18tag: Tag,
19/// Contains the virtual address of the symbol, relative to the start of its section.
20/// This differs from the offset of an `Atom` which is relative to the start of a segment.
21virtual_address: u32,
22
23/// Represents a symbol index where `null` represents an invalid index.
24pub const Index = enum(u32) {
25 null,
26 _,
27};
28
29pub const Tag = enum {
30 function,
31 data,
32 global,
33 section,
34 event,
35 table,
36 /// synthetic kind used by the wasm linker during incremental compilation
37 /// to notate a symbol has been freed, but still lives in the symbol list.
38 dead,
39 undefined,
40
41 /// From a given symbol tag, returns the `ExternalType`
42 /// Asserts the given tag can be represented as an external type.
43 pub fn externalType(tag: Tag) std.wasm.ExternalKind {
44 return switch (tag) {
45 .function => .function,
46 .global => .global,
47 .data => unreachable, // Data symbols will generate a global
48 .section => unreachable, // Not an external type
49 .event => unreachable, // Not an external type
50 .dead => unreachable, // Dead symbols should not be referenced
51 .undefined => unreachable,
52 .table => .table,
53 };
54 }
55};
56
57pub const Flag = enum(u32) {
58 /// Indicates a weak symbol.
59 /// When linking multiple modules defining the same symbol, all weak definitions are discarded
60 /// in favourite of the strong definition. When no strong definition exists, all weak but one definition is discarded.
61 /// If multiple definitions remain, we get an error: symbol collision.
62 WASM_SYM_BINDING_WEAK = 0x1,
63 /// Indicates a local, non-exported, non-module-linked symbol.
64 /// The names of local symbols are not required to be unique, unlike non-local symbols.
65 WASM_SYM_BINDING_LOCAL = 0x2,
66 /// Represents the binding of a symbol, indicating if it's local or not, and weak or not.
67 WASM_SYM_BINDING_MASK = 0x3,
68 /// Indicates a hidden symbol. Hidden symbols will not be exported to the link result, but may
69 /// link to other modules.
70 WASM_SYM_VISIBILITY_HIDDEN = 0x4,
71 /// Indicates an undefined symbol. For non-data symbols, this must match whether the symbol is
72 /// an import or is defined. For data symbols however, determines whether a segment is specified.
73 WASM_SYM_UNDEFINED = 0x10,
74 /// Indicates a symbol of which its intention is to be exported from the wasm module to the host environment.
75 /// This differs from the visibility flag as this flag affects the static linker.
76 WASM_SYM_EXPORTED = 0x20,
77 /// Indicates the symbol uses an explicit symbol name, rather than reusing the name from a wasm import.
78 /// Allows remapping imports from foreign WASM modules into local symbols with a different name.
79 WASM_SYM_EXPLICIT_NAME = 0x40,
80 /// Indicates the symbol is to be included in the linker output, regardless of whether it is used or has any references to it.
81 WASM_SYM_NO_STRIP = 0x80,
82 /// Indicates a symbol is TLS
83 WASM_SYM_TLS = 0x100,
84 /// Zig specific flag. Uses the most significant bit of the flag to annotate whether a symbol is
85 /// alive or not. Dead symbols are allowed to be garbage collected.
86 alive = 0x80000000,
87};
88
89/// Verifies if the given symbol should be imported from the
90/// host environment or not
91pub fn requiresImport(symbol: Symbol) bool {
92 if (symbol.tag == .data) return false;
93 if (!symbol.isUndefined()) return false;
94 if (symbol.isWeak()) return false;
95 // if (symbol.isDefined() and symbol.isWeak()) return true; //TODO: Only when building shared lib
96
97 return true;
98}
99
100/// Marks a symbol as 'alive', ensuring the garbage collector will not collect the trash.
101pub fn mark(symbol: *Symbol) void {
102 symbol.flags |= @intFromEnum(Flag.alive);
103}
104
105pub fn unmark(symbol: *Symbol) void {
106 symbol.flags &= ~@intFromEnum(Flag.alive);
107}
108
109pub fn isAlive(symbol: Symbol) bool {
110 return symbol.flags & @intFromEnum(Flag.alive) != 0;
111}
112
113pub fn isDead(symbol: Symbol) bool {
114 return symbol.flags & @intFromEnum(Flag.alive) == 0;
115}
116
117pub fn isTLS(symbol: Symbol) bool {
118 return symbol.flags & @intFromEnum(Flag.WASM_SYM_TLS) != 0;
119}
120
121pub fn hasFlag(symbol: Symbol, flag: Flag) bool {
122 return symbol.flags & @intFromEnum(flag) != 0;
123}
124
125pub fn setFlag(symbol: *Symbol, flag: Flag) void {
126 symbol.flags |= @intFromEnum(flag);
127}
128
129pub fn isUndefined(symbol: Symbol) bool {
130 return symbol.flags & @intFromEnum(Flag.WASM_SYM_UNDEFINED) != 0;
131}
132
133pub fn setUndefined(symbol: *Symbol, is_undefined: bool) void {
134 if (is_undefined) {
135 symbol.setFlag(.WASM_SYM_UNDEFINED);
136 } else {
137 symbol.flags &= ~@intFromEnum(Flag.WASM_SYM_UNDEFINED);
138 }
139}
140
141pub fn setGlobal(symbol: *Symbol, is_global: bool) void {
142 if (is_global) {
143 symbol.flags &= ~@intFromEnum(Flag.WASM_SYM_BINDING_LOCAL);
144 } else {
145 symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
146 }
147}
148
149pub fn isDefined(symbol: Symbol) bool {
150 return !symbol.isUndefined();
151}
152
153pub fn isVisible(symbol: Symbol) bool {
154 return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) == 0;
155}
156
157pub fn isLocal(symbol: Symbol) bool {
158 return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) != 0;
159}
160
161pub fn isGlobal(symbol: Symbol) bool {
162 return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) == 0;
163}
164
165pub fn isHidden(symbol: Symbol) bool {
166 return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) != 0;
167}
168
169pub fn isNoStrip(symbol: Symbol) bool {
170 return symbol.flags & @intFromEnum(Flag.WASM_SYM_NO_STRIP) != 0;
171}
172
173pub fn isExported(symbol: Symbol, is_dynamic: bool) bool {
174 if (symbol.isUndefined() or symbol.isLocal()) return false;
175 if (is_dynamic and symbol.isVisible()) return true;
176 return symbol.hasFlag(.WASM_SYM_EXPORTED);
177}
178
179pub fn isWeak(symbol: Symbol) bool {
180 return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_WEAK) != 0;
181}
182
183/// Formats the symbol into human-readable text
184pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
185 _ = fmt;
186 _ = options;
187
188 const kind_fmt: u8 = switch (symbol.tag) {
189 .function => 'F',
190 .data => 'D',
191 .global => 'G',
192 .section => 'S',
193 .event => 'E',
194 .table => 'T',
195 .dead => '-',
196 .undefined => unreachable,
197 };
198 const visible: []const u8 = if (symbol.isVisible()) "yes" else "no";
199 const binding: []const u8 = if (symbol.isLocal()) "local" else "global";
200 const undef: []const u8 = if (symbol.isUndefined()) "undefined" else "";
201
202 try writer.print(
203 "{c} binding={s} visible={s} id={d} name_offset={d} {s}",
204 .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
205 );
206}
207
208const std = @import("std");
209const Symbol = @This();
210const wasm = @import("../Wasm.zig");
src/link/Wasm/ZigObject.zig deleted-1229
...@@ -1,1229 +0,0 @@
1//! ZigObject encapsulates the state of the incrementally compiled Zig module.
2//! It stores the associated input local and global symbols, allocated atoms,
3//! and any relocations that may have been emitted.
4
5/// For error reporting purposes only.
6path: Path,
7/// Map of all `Nav` that are currently alive.
8/// Each index maps to the corresponding `NavInfo`.
9navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavInfo) = .empty,
10/// List of function type signatures for this Zig module.
11func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty,
12/// List of `std.wasm.Func`. Each entry contains the function signature,
13/// rather than the actual body.
14functions: std.ArrayListUnmanaged(std.wasm.Func) = .empty,
15/// List of indexes pointing to an entry within the `functions` list which has been removed.
16functions_free_list: std.ArrayListUnmanaged(u32) = .empty,
17/// Map of symbol locations, represented by its `Wasm.Import`.
18imports: std.AutoHashMapUnmanaged(Symbol.Index, Wasm.Import) = .empty,
19/// List of WebAssembly globals.
20globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty,
21/// Mapping between an `Atom` and its type index representing the Wasm
22/// type of the function signature.
23atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .empty,
24/// List of all symbols generated by Zig code.
25symbols: std.ArrayListUnmanaged(Symbol) = .empty,
26/// Map from symbol name to their index into the `symbols` list.
27global_syms: std.AutoHashMapUnmanaged(Wasm.String, Symbol.Index) = .empty,
28/// List of symbol indexes which are free to be used.
29symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .empty,
30/// Extra metadata about the linking section, such as alignment of segments and their name.
31segment_info: std.ArrayListUnmanaged(Wasm.NamedSegment) = .empty,
32/// List of indexes which contain a free slot in the `segment_info` list.
33segment_free_list: std.ArrayListUnmanaged(u32) = .empty,
34/// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
35uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .empty,
36/// List of atom indexes of functions that are generated by the backend.
37synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .empty,
38/// Represents the symbol index of the error name table
39/// When this is `null`, no code references an error using runtime `@errorName`.
40/// During initializion, a symbol with corresponding atom will be created that is
41/// used to perform relocations to the pointer of this table.
42/// The actual table is populated during `flush`.
43error_table_symbol: Symbol.Index = .null,
44/// Atom index of the table of symbol names. This is stored so we can clean up the atom.
45error_names_atom: Atom.Index = .null,
46/// Amount of functions in the `import` sections.
47imported_functions_count: u32 = 0,
48/// Amount of globals in the `import` section.
49imported_globals_count: u32 = 0,
50/// Symbol index representing the stack pointer. This will be set upon initializion
51/// of a new `ZigObject`. Codegen will make calls into this to create relocations for
52/// this symbol each time the stack pointer is moved.
53stack_pointer_sym: Symbol.Index,
54/// Debug information for the Zig module.
55dwarf: ?Dwarf = null,
56// Debug section atoms. These are only set when the current compilation
57// unit contains Zig code. The lifetime of these atoms are extended
58// until the end of the compiler's lifetime. Meaning they're not freed
59// during `flush()` in incremental-mode.
60debug_info_atom: ?Atom.Index = null,
61debug_line_atom: ?Atom.Index = null,
62debug_loc_atom: ?Atom.Index = null,
63debug_ranges_atom: ?Atom.Index = null,
64debug_abbrev_atom: ?Atom.Index = null,
65debug_str_atom: ?Atom.Index = null,
66debug_pubnames_atom: ?Atom.Index = null,
67debug_pubtypes_atom: ?Atom.Index = null,
68/// The index of the segment representing the custom '.debug_info' section.
69debug_info_index: ?u32 = null,
70/// The index of the segment representing the custom '.debug_line' section.
71debug_line_index: ?u32 = null,
72/// The index of the segment representing the custom '.debug_loc' section.
73debug_loc_index: ?u32 = null,
74/// The index of the segment representing the custom '.debug_ranges' section.
75debug_ranges_index: ?u32 = null,
76/// The index of the segment representing the custom '.debug_pubnames' section.
77debug_pubnames_index: ?u32 = null,
78/// The index of the segment representing the custom '.debug_pubtypes' section.
79debug_pubtypes_index: ?u32 = null,
80/// The index of the segment representing the custom '.debug_pubtypes' section.
81debug_str_index: ?u32 = null,
82/// The index of the segment representing the custom '.debug_pubtypes' section.
83debug_abbrev_index: ?u32 = null,
84
85const NavInfo = struct {
86 atom: Atom.Index = .null,
87 exports: std.ArrayListUnmanaged(Symbol.Index) = .empty,
88
89 fn @"export"(ni: NavInfo, zo: *const ZigObject, name: Wasm.String) ?Symbol.Index {
90 for (ni.exports.items) |sym_index| {
91 if (zo.symbol(sym_index).name == name) return sym_index;
92 }
93 return null;
94 }
95
96 fn appendExport(ni: *NavInfo, gpa: std.mem.Allocator, sym_index: Symbol.Index) !void {
97 return ni.exports.append(gpa, sym_index);
98 }
99
100 fn deleteExport(ni: *NavInfo, sym_index: Symbol.Index) void {
101 for (ni.exports.items, 0..) |idx, index| {
102 if (idx == sym_index) {
103 _ = ni.exports.swapRemove(index);
104 return;
105 }
106 }
107 unreachable; // invalid sym_index
108 }
109};
110
111/// Initializes the `ZigObject` with initial symbols.
112pub fn init(zig_object: *ZigObject, wasm: *Wasm) !void {
113 // Initialize an undefined global with the name __stack_pointer. Codegen will use
114 // this to generate relocations when moving the stack pointer. This symbol will be
115 // resolved automatically by the final linking stage.
116 try zig_object.createStackPointer(wasm);
117
118 // TODO: Initialize debug information when we reimplement Dwarf support.
119}
120
121fn createStackPointer(zig_object: *ZigObject, wasm: *Wasm) !void {
122 const gpa = wasm.base.comp.gpa;
123 const sym_index = try zig_object.getGlobalSymbol(gpa, wasm.preloaded_strings.__stack_pointer);
124 const sym = zig_object.symbol(sym_index);
125 sym.index = zig_object.imported_globals_count;
126 sym.tag = .global;
127 const is_wasm32 = wasm.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32;
128 try zig_object.imports.putNoClobber(gpa, sym_index, .{
129 .name = sym.name,
130 .module_name = wasm.host_name,
131 .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } },
132 });
133 zig_object.imported_globals_count += 1;
134 zig_object.stack_pointer_sym = sym_index;
135}
136
137pub fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol {
138 return &zig_object.symbols.items[@intFromEnum(index)];
139}
140
141/// Frees and invalidates all memory of the incrementally compiled Zig module.
142/// It is illegal behavior to access the `ZigObject` after calling `deinit`.
143pub fn deinit(zig_object: *ZigObject, wasm: *Wasm) void {
144 const gpa = wasm.base.comp.gpa;
145 for (zig_object.segment_info.items) |segment_info| {
146 gpa.free(segment_info.name);
147 }
148
149 {
150 var it = zig_object.navs.valueIterator();
151 while (it.next()) |nav_info| {
152 const atom = wasm.getAtomPtr(nav_info.atom);
153 for (atom.locals.items) |local_index| {
154 const local_atom = wasm.getAtomPtr(local_index);
155 local_atom.deinit(gpa);
156 }
157 atom.deinit(gpa);
158 nav_info.exports.deinit(gpa);
159 }
160 }
161 {
162 for (zig_object.uavs.values()) |atom_index| {
163 const atom = wasm.getAtomPtr(atom_index);
164 for (atom.locals.items) |local_index| {
165 const local_atom = wasm.getAtomPtr(local_index);
166 local_atom.deinit(gpa);
167 }
168 atom.deinit(gpa);
169 }
170 }
171 if (zig_object.global_syms.get(wasm.preloaded_strings.__zig_errors_len)) |sym_index| {
172 const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index }).?;
173 wasm.getAtomPtr(atom_index).deinit(gpa);
174 }
175 if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol })) |atom_index| {
176 const atom = wasm.getAtomPtr(atom_index);
177 atom.deinit(gpa);
178 }
179 for (zig_object.synthetic_functions.items) |atom_index| {
180 const atom = wasm.getAtomPtr(atom_index);
181 atom.deinit(gpa);
182 }
183 zig_object.synthetic_functions.deinit(gpa);
184 for (zig_object.func_types.items) |*ty| {
185 ty.deinit(gpa);
186 }
187 if (zig_object.error_names_atom != .null) {
188 const atom = wasm.getAtomPtr(zig_object.error_names_atom);
189 atom.deinit(gpa);
190 }
191 zig_object.global_syms.deinit(gpa);
192 zig_object.func_types.deinit(gpa);
193 zig_object.atom_types.deinit(gpa);
194 zig_object.functions.deinit(gpa);
195 zig_object.imports.deinit(gpa);
196 zig_object.navs.deinit(gpa);
197 zig_object.uavs.deinit(gpa);
198 zig_object.symbols.deinit(gpa);
199 zig_object.symbols_free_list.deinit(gpa);
200 zig_object.segment_info.deinit(gpa);
201 zig_object.segment_free_list.deinit(gpa);
202
203 if (zig_object.dwarf) |*dwarf| {
204 dwarf.deinit();
205 }
206 gpa.free(zig_object.path.sub_path);
207 zig_object.* = undefined;
208}
209
210/// Allocates a new symbol and returns its index.
211/// Will re-use slots when a symbol was freed at an earlier stage.
212pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.Index {
213 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
214 const sym: Symbol = .{
215 .name = undefined, // will be set after updateDecl as well as during atom creation for decls
216 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
217 .tag = .undefined, // will be set after updateDecl
218 .index = std.math.maxInt(u32), // will be set during atom parsing
219 .virtual_address = std.math.maxInt(u32), // will be set during atom allocation
220 };
221 if (zig_object.symbols_free_list.popOrNull()) |index| {
222 zig_object.symbols.items[@intFromEnum(index)] = sym;
223 return index;
224 }
225 const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
226 zig_object.symbols.appendAssumeCapacity(sym);
227 return index;
228}
229
230// Generate code for the `Nav`, storing it in memory to be later written to
231// the file on flush().
232pub fn updateNav(
233 zig_object: *ZigObject,
234 wasm: *Wasm,
235 pt: Zcu.PerThread,
236 nav_index: InternPool.Nav.Index,
237) !void {
238 const zcu = pt.zcu;
239 const ip = &zcu.intern_pool;
240 const nav = ip.getNav(nav_index);
241
242 const nav_val = zcu.navValue(nav_index);
243 const is_extern, const lib_name, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
244 .variable => |variable| .{ false, .none, Value.fromInterned(variable.init) },
245 .func => return,
246 .@"extern" => |@"extern"| if (ip.isFunctionType(nav.typeOf(ip)))
247 return
248 else
249 .{ true, @"extern".lib_name, nav_val },
250 else => .{ false, .none, nav_val },
251 };
252
253 if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
254 const gpa = wasm.base.comp.gpa;
255 const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
256 const atom = wasm.getAtomPtr(atom_index);
257 atom.clear();
258
259 if (is_extern)
260 return zig_object.addOrUpdateImport(wasm, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null);
261
262 var code_writer = std.ArrayList(u8).init(gpa);
263 defer code_writer.deinit();
264
265 const res = try codegen.generateSymbol(
266 &wasm.base,
267 pt,
268 zcu.navSrcLoc(nav_index),
269 nav_init,
270 &code_writer,
271 .{ .atom_index = @intFromEnum(atom.sym_index) },
272 );
273
274 const code = switch (res) {
275 .ok => code_writer.items,
276 .fail => |em| {
277 try zcu.failed_codegen.put(zcu.gpa, nav_index, em);
278 return;
279 },
280 };
281
282 try zig_object.finishUpdateNav(wasm, pt, nav_index, code);
283 }
284}
285
286pub fn updateFunc(
287 zig_object: *ZigObject,
288 wasm: *Wasm,
289 pt: Zcu.PerThread,
290 func_index: InternPool.Index,
291 air: Air,
292 liveness: Liveness,
293) !void {
294 const zcu = pt.zcu;
295 const gpa = zcu.gpa;
296 const func = pt.zcu.funcInfo(func_index);
297 const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, func.owner_nav);
298 const atom = wasm.getAtomPtr(atom_index);
299 atom.clear();
300
301 var code_writer = std.ArrayList(u8).init(gpa);
302 defer code_writer.deinit();
303 const result = try codegen.generateFunction(
304 &wasm.base,
305 pt,
306 zcu.navSrcLoc(func.owner_nav),
307 func_index,
308 air,
309 liveness,
310 &code_writer,
311 .none,
312 );
313
314 const code = switch (result) {
315 .ok => code_writer.items,
316 .fail => |em| {
317 try pt.zcu.failed_codegen.put(gpa, func.owner_nav, em);
318 return;
319 },
320 };
321
322 return zig_object.finishUpdateNav(wasm, pt, func.owner_nav, code);
323}
324
325fn finishUpdateNav(
326 zig_object: *ZigObject,
327 wasm: *Wasm,
328 pt: Zcu.PerThread,
329 nav_index: InternPool.Nav.Index,
330 code: []const u8,
331) !void {
332 const zcu = pt.zcu;
333 const ip = &zcu.intern_pool;
334 const gpa = zcu.gpa;
335 const nav = ip.getNav(nav_index);
336 const nav_val = zcu.navValue(nav_index);
337 const nav_info = zig_object.navs.get(nav_index).?;
338 const atom_index = nav_info.atom;
339 const atom = wasm.getAtomPtr(atom_index);
340 const sym = zig_object.symbol(atom.sym_index);
341 sym.name = try wasm.internString(nav.fqn.toSlice(ip));
342 try atom.code.appendSlice(gpa, code);
343 atom.size = @intCast(code.len);
344
345 if (ip.isFunctionType(nav.typeOf(ip))) {
346 sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? });
347 sym.tag = .function;
348 } else {
349 const is_const, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
350 .variable => |variable| .{ false, variable.init },
351 .@"extern" => |@"extern"| .{ @"extern".is_const, .none },
352 else => .{ true, nav_val.toIntern() },
353 };
354 const segment_name = name: {
355 if (is_const) break :name ".rodata.";
356
357 if (nav_init != .none and Value.fromInterned(nav_init).isUndefDeep(zcu)) {
358 break :name switch (zcu.navFileScope(nav_index).mod.optimize_mode) {
359 .Debug, .ReleaseSafe => ".data.",
360 .ReleaseFast, .ReleaseSmall => ".bss.",
361 };
362 }
363 // when the decl is all zeroes, we store the atom in the bss segment,
364 // in all other cases it will be in the data segment.
365 for (atom.code.items) |byte| {
366 if (byte != 0) break :name ".data.";
367 }
368 break :name ".bss.";
369 };
370 if ((wasm.base.isObject() or wasm.base.comp.config.import_memory) and
371 std.mem.startsWith(u8, segment_name, ".bss"))
372 {
373 @memset(atom.code.items, 0);
374 }
375 // Will be freed upon freeing of decl or after cleanup of Wasm binary.
376 const full_segment_name = try std.mem.concat(gpa, u8, &.{
377 segment_name,
378 nav.fqn.toSlice(ip),
379 });
380 errdefer gpa.free(full_segment_name);
381 sym.tag = .data;
382 sym.index = try zig_object.createDataSegment(gpa, full_segment_name, pt.navAlignment(nav_index));
383 }
384 if (code.len == 0) return;
385 atom.alignment = pt.navAlignment(nav_index);
386}
387
388/// Creates and initializes a new segment in the 'Data' section.
389/// Reuses free slots in the list of segments and returns the index.
390fn createDataSegment(
391 zig_object: *ZigObject,
392 gpa: std.mem.Allocator,
393 name: []const u8,
394 alignment: InternPool.Alignment,
395) !u32 {
396 const segment_index: u32 = if (zig_object.segment_free_list.popOrNull()) |index|
397 index
398 else index: {
399 const idx: u32 = @intCast(zig_object.segment_info.items.len);
400 _ = try zig_object.segment_info.addOne(gpa);
401 break :index idx;
402 };
403 zig_object.segment_info.items[segment_index] = .{
404 .alignment = alignment,
405 .flags = 0,
406 .name = name,
407 };
408 return segment_index;
409}
410
411/// For a given `InternPool.Nav.Index` returns its corresponding `Atom.Index`.
412/// When the index was not found, a new `Atom` will be created, and its index will be returned.
413/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
414pub fn getOrCreateAtomForNav(
415 zig_object: *ZigObject,
416 wasm: *Wasm,
417 pt: Zcu.PerThread,
418 nav_index: InternPool.Nav.Index,
419) !Atom.Index {
420 const ip = &pt.zcu.intern_pool;
421 const gpa = pt.zcu.gpa;
422 const gop = try zig_object.navs.getOrPut(gpa, nav_index);
423 if (!gop.found_existing) {
424 const sym_index = try zig_object.allocateSymbol(gpa);
425 gop.value_ptr.* = .{ .atom = try wasm.createAtom(sym_index, .zig_object) };
426 const nav = ip.getNav(nav_index);
427 const sym = zig_object.symbol(sym_index);
428 sym.name = try wasm.internString(nav.fqn.toSlice(ip));
429 }
430 return gop.value_ptr.atom;
431}
432
433pub fn lowerUav(
434 zig_object: *ZigObject,
435 wasm: *Wasm,
436 pt: Zcu.PerThread,
437 uav: InternPool.Index,
438 explicit_alignment: InternPool.Alignment,
439 src_loc: Zcu.LazySrcLoc,
440) !codegen.GenResult {
441 const gpa = wasm.base.comp.gpa;
442 const gop = try zig_object.uavs.getOrPut(gpa, uav);
443 if (!gop.found_existing) {
444 var name_buf: [32]u8 = undefined;
445 const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
446 @intFromEnum(uav),
447 }) catch unreachable;
448
449 switch (try zig_object.lowerConst(wasm, pt, name, Value.fromInterned(uav), src_loc)) {
450 .ok => |atom_index| zig_object.uavs.values()[gop.index] = atom_index,
451 .fail => |em| return .{ .fail = em },
452 }
453 }
454
455 const atom = wasm.getAtomPtr(zig_object.uavs.values()[gop.index]);
456 atom.alignment = switch (atom.alignment) {
457 .none => explicit_alignment,
458 else => switch (explicit_alignment) {
459 .none => atom.alignment,
460 else => atom.alignment.maxStrict(explicit_alignment),
461 },
462 };
463 return .{ .mcv = .{ .load_symbol = @intFromEnum(atom.sym_index) } };
464}
465
466const LowerConstResult = union(enum) {
467 ok: Atom.Index,
468 fail: *Zcu.ErrorMsg,
469};
470
471fn lowerConst(
472 zig_object: *ZigObject,
473 wasm: *Wasm,
474 pt: Zcu.PerThread,
475 name: []const u8,
476 val: Value,
477 src_loc: Zcu.LazySrcLoc,
478) !LowerConstResult {
479 const gpa = wasm.base.comp.gpa;
480 const zcu = wasm.base.comp.zcu.?;
481
482 const ty = val.typeOf(zcu);
483
484 // Create and initialize a new local symbol and atom
485 const sym_index = try zig_object.allocateSymbol(gpa);
486 const atom_index = try wasm.createAtom(sym_index, .zig_object);
487 var value_bytes = std.ArrayList(u8).init(gpa);
488 defer value_bytes.deinit();
489
490 const code = code: {
491 const atom = wasm.getAtomPtr(atom_index);
492 atom.alignment = ty.abiAlignment(zcu);
493 const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name });
494 errdefer gpa.free(segment_name);
495 zig_object.symbol(sym_index).* = .{
496 .name = try wasm.internString(name),
497 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
498 .tag = .data,
499 .index = try zig_object.createDataSegment(
500 gpa,
501 segment_name,
502 ty.abiAlignment(zcu),
503 ),
504 .virtual_address = undefined,
505 };
506
507 const result = try codegen.generateSymbol(
508 &wasm.base,
509 pt,
510 src_loc,
511 val,
512 &value_bytes,
513 .{ .atom_index = @intFromEnum(atom.sym_index) },
514 );
515 break :code switch (result) {
516 .ok => value_bytes.items,
517 .fail => |em| {
518 return .{ .fail = em };
519 },
520 };
521 };
522
523 const atom = wasm.getAtomPtr(atom_index);
524 atom.size = @intCast(code.len);
525 try atom.code.appendSlice(gpa, code);
526 return .{ .ok = atom_index };
527}
528
529/// Returns the symbol index of the error name table.
530///
531/// When the symbol does not yet exist, it will create a new one instead.
532pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm: *Wasm, pt: Zcu.PerThread) !Symbol.Index {
533 if (zig_object.error_table_symbol != .null) {
534 return zig_object.error_table_symbol;
535 }
536
537 // no error was referenced yet, so create a new symbol and atom for it
538 // and then return said symbol's index. The final table will be populated
539 // during `flush` when we know all possible error names.
540 const gpa = wasm.base.comp.gpa;
541 const sym_index = try zig_object.allocateSymbol(gpa);
542 const atom_index = try wasm.createAtom(sym_index, .zig_object);
543 const atom = wasm.getAtomPtr(atom_index);
544 const slice_ty = Type.slice_const_u8_sentinel_0;
545 atom.alignment = slice_ty.abiAlignment(pt.zcu);
546
547 const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_name_table");
548 const sym = zig_object.symbol(sym_index);
549 sym.* = .{
550 .name = wasm.preloaded_strings.__zig_err_name_table,
551 .tag = .data,
552 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
553 .index = try zig_object.createDataSegment(gpa, segment_name, atom.alignment),
554 .virtual_address = undefined,
555 };
556
557 log.debug("Error name table was created with symbol index: ({d})", .{@intFromEnum(sym_index)});
558 zig_object.error_table_symbol = sym_index;
559 return sym_index;
560}
561
562/// Populates the error name table, when `error_table_symbol` is not null.
563///
564/// This creates a table that consists of pointers and length to each error name.
565/// The table is what is being pointed to within the runtime bodies that are generated.
566fn populateErrorNameTable(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void {
567 if (zig_object.error_table_symbol == .null) return;
568 const gpa = wasm.base.comp.gpa;
569 const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol }).?;
570
571 // Rather than creating a symbol for each individual error name,
572 // we create a symbol for the entire region of error names. We then calculate
573 // the pointers into the list using addends which are appended to the relocation.
574 const names_sym_index = try zig_object.allocateSymbol(gpa);
575 const names_atom_index = try wasm.createAtom(names_sym_index, .zig_object);
576 const names_atom = wasm.getAtomPtr(names_atom_index);
577 names_atom.alignment = .@"1";
578 const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names");
579 const names_symbol = zig_object.symbol(names_sym_index);
580 names_symbol.* = .{
581 .name = wasm.preloaded_strings.__zig_err_names,
582 .tag = .data,
583 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
584 .index = try zig_object.createDataSegment(gpa, segment_name, names_atom.alignment),
585 .virtual_address = undefined,
586 };
587
588 log.debug("Populating error names", .{});
589
590 // Addend for each relocation to the table
591 var addend: u32 = 0;
592 const pt: Zcu.PerThread = .activate(wasm.base.comp.zcu.?, tid);
593 defer pt.deactivate();
594 const slice_ty = Type.slice_const_u8_sentinel_0;
595 const atom = wasm.getAtomPtr(atom_index);
596 {
597 // TODO: remove this unreachable entry
598 try atom.code.appendNTimes(gpa, 0, 4);
599 try atom.code.writer(gpa).writeInt(u32, 0, .little);
600 atom.size += @intCast(slice_ty.abiSize(pt.zcu));
601 addend += 1;
602
603 try names_atom.code.append(gpa, 0);
604 }
605 const ip = &pt.zcu.intern_pool;
606 for (ip.global_error_set.getNamesFromMainThread()) |error_name| {
607 const error_name_slice = error_name.toSlice(ip);
608 const len: u32 = @intCast(error_name_slice.len + 1); // names are 0-terminated
609
610 const offset = @as(u32, @intCast(atom.code.items.len));
611 // first we create the data for the slice of the name
612 try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated
613 try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
614 // create relocation to the error name
615 try atom.relocs.append(gpa, .{
616 .index = @intFromEnum(names_atom.sym_index),
617 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
618 .offset = offset,
619 .addend = @intCast(addend),
620 });
621 atom.size += @intCast(slice_ty.abiSize(pt.zcu));
622 addend += len;
623
624 // as we updated the error name table, we now store the actual name within the names atom
625 try names_atom.code.ensureUnusedCapacity(gpa, len);
626 names_atom.code.appendSliceAssumeCapacity(error_name_slice[0..len]);
627
628 log.debug("Populated error name: '{}'", .{error_name.fmt(ip)});
629 }
630 names_atom.size = addend;
631 zig_object.error_names_atom = names_atom_index;
632}
633
634/// Either creates a new import, or updates one if existing.
635/// When `type_index` is non-null, we assume an external function.
636/// In all other cases, a data-symbol will be created instead.
637pub fn addOrUpdateImport(
638 zig_object: *ZigObject,
639 wasm: *Wasm,
640 /// Name of the import
641 name: []const u8,
642 /// Symbol index that is external
643 symbol_index: Symbol.Index,
644 /// Optional library name (i.e. `extern "c" fn foo() void`
645 lib_name: ?[:0]const u8,
646 /// The index of the type that represents the function signature
647 /// when the extern is a function. When this is null, a data-symbol
648 /// is asserted instead.
649 type_index: ?u32,
650) !void {
651 const gpa = wasm.base.comp.gpa;
652 std.debug.assert(symbol_index != .null);
653 // For the import name, we use the decl's name, rather than the fully qualified name
654 // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
655 // name but different module can be resolved correctly.
656 const mangle_name = if (lib_name) |n| !std.mem.eql(u8, n, "c") else false;
657 const full_name = if (mangle_name)
658 try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? })
659 else
660 name;
661 defer if (mangle_name) gpa.free(full_name);
662
663 const decl_name_index = try wasm.internString(full_name);
664 const sym: *Symbol = &zig_object.symbols.items[@intFromEnum(symbol_index)];
665 sym.setUndefined(true);
666 sym.setGlobal(true);
667 sym.name = decl_name_index;
668 if (mangle_name) {
669 // we specified a specific name for the symbol that does not match the import name
670 sym.setFlag(.WASM_SYM_EXPLICIT_NAME);
671 }
672
673 if (type_index) |ty_index| {
674 const gop = try zig_object.imports.getOrPut(gpa, symbol_index);
675 const module_name = if (lib_name) |n| try wasm.internString(n) else wasm.host_name;
676 if (!gop.found_existing) zig_object.imported_functions_count += 1;
677 gop.value_ptr.* = .{
678 .module_name = module_name,
679 .name = try wasm.internString(name),
680 .kind = .{ .function = ty_index },
681 };
682 sym.tag = .function;
683 } else {
684 sym.tag = .data;
685 }
686}
687
688/// Returns the symbol index from a symbol of which its flag is set global,
689/// such as an exported or imported symbol.
690/// If the symbol does not yet exist, creates a new one symbol instead
691/// and then returns the index to it.
692pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name_index: Wasm.String) !Symbol.Index {
693 const gop = try zig_object.global_syms.getOrPut(gpa, name_index);
694 if (gop.found_existing) {
695 return gop.value_ptr.*;
696 }
697
698 var sym: Symbol = .{
699 .name = name_index,
700 .flags = 0,
701 .index = undefined, // index to type will be set after merging symbols
702 .tag = .function,
703 .virtual_address = std.math.maxInt(u32),
704 };
705 sym.setGlobal(true);
706 sym.setUndefined(true);
707
708 const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: {
709 const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
710 try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
711 zig_object.symbols.items.len += 1;
712 break :blk index;
713 };
714 zig_object.symbol(sym_index).* = sym;
715 gop.value_ptr.* = sym_index;
716 return sym_index;
717}
718
719/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
720/// Returns the given pointer address
721pub fn getNavVAddr(
722 zig_object: *ZigObject,
723 wasm: *Wasm,
724 pt: Zcu.PerThread,
725 nav_index: InternPool.Nav.Index,
726 reloc_info: link.File.RelocInfo,
727) !u64 {
728 const zcu = pt.zcu;
729 const ip = &zcu.intern_pool;
730 const gpa = zcu.gpa;
731 const nav = ip.getNav(nav_index);
732 const target = &zcu.navFileScope(nav_index).mod.resolved_target.result;
733
734 const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
735 const target_atom = wasm.getAtom(target_atom_index);
736 const target_symbol_index = @intFromEnum(target_atom.sym_index);
737 if (nav.getExtern(ip)) |@"extern"| {
738 try zig_object.addOrUpdateImport(
739 wasm,
740 nav.name.toSlice(ip),
741 target_atom.sym_index,
742 @"extern".lib_name.toSlice(ip),
743 null,
744 );
745 }
746
747 std.debug.assert(reloc_info.parent.atom_index != 0);
748 const atom_index = wasm.symbol_atom.get(.{
749 .file = .zig_object,
750 .index = @enumFromInt(reloc_info.parent.atom_index),
751 }).?;
752 const atom = wasm.getAtomPtr(atom_index);
753 const is_wasm32 = target.cpu.arch == .wasm32;
754 if (ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) {
755 std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
756 try atom.relocs.append(gpa, .{
757 .index = target_symbol_index,
758 .offset = @intCast(reloc_info.offset),
759 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
760 });
761 } else {
762 try atom.relocs.append(gpa, .{
763 .index = target_symbol_index,
764 .offset = @intCast(reloc_info.offset),
765 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
766 .addend = @intCast(reloc_info.addend),
767 });
768 }
769
770 // we do not know the final address at this point,
771 // as atom allocation will determine the address and relocations
772 // will calculate and rewrite this. Therefore, we simply return the symbol index
773 // that was targeted.
774 return target_symbol_index;
775}
776
777pub fn getUavVAddr(
778 zig_object: *ZigObject,
779 wasm: *Wasm,
780 uav: InternPool.Index,
781 reloc_info: link.File.RelocInfo,
782) !u64 {
783 const gpa = wasm.base.comp.gpa;
784 const target = wasm.base.comp.root_mod.resolved_target.result;
785 const atom_index = zig_object.uavs.get(uav).?;
786 const target_symbol_index = @intFromEnum(wasm.getAtom(atom_index).sym_index);
787
788 const parent_atom_index = wasm.symbol_atom.get(.{
789 .file = .zig_object,
790 .index = @enumFromInt(reloc_info.parent.atom_index),
791 }).?;
792 const parent_atom = wasm.getAtomPtr(parent_atom_index);
793 const is_wasm32 = target.cpu.arch == .wasm32;
794 const zcu = wasm.base.comp.zcu.?;
795 const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav));
796 if (ty.zigTypeTag(zcu) == .@"fn") {
797 std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations
798 try parent_atom.relocs.append(gpa, .{
799 .index = target_symbol_index,
800 .offset = @intCast(reloc_info.offset),
801 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
802 });
803 } else {
804 try parent_atom.relocs.append(gpa, .{
805 .index = target_symbol_index,
806 .offset = @intCast(reloc_info.offset),
807 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
808 .addend = @intCast(reloc_info.addend),
809 });
810 }
811
812 // we do not know the final address at this point,
813 // as atom allocation will determine the address and relocations
814 // will calculate and rewrite this. Therefore, we simply return the symbol index
815 // that was targeted.
816 return target_symbol_index;
817}
818
819pub fn deleteExport(
820 zig_object: *ZigObject,
821 wasm: *Wasm,
822 exported: Zcu.Exported,
823 name: InternPool.NullTerminatedString,
824) void {
825 const zcu = wasm.base.comp.zcu.?;
826 const nav_index = switch (exported) {
827 .nav => |nav_index| nav_index,
828 .uav => @panic("TODO: implement Wasm linker code for exporting a constant value"),
829 };
830 const nav_info = zig_object.navs.getPtr(nav_index) orelse return;
831 const name_interned = wasm.getExistingString(name.toSlice(&zcu.intern_pool)).?;
832 if (nav_info.@"export"(zig_object, name_interned)) |sym_index| {
833 const sym = zig_object.symbol(sym_index);
834 nav_info.deleteExport(sym_index);
835 std.debug.assert(zig_object.global_syms.remove(sym.name));
836 std.debug.assert(wasm.symbol_atom.remove(.{ .file = .zig_object, .index = sym_index }));
837 zig_object.symbols_free_list.append(wasm.base.comp.gpa, sym_index) catch {};
838 sym.tag = .dead;
839 }
840}
841
842pub fn updateExports(
843 zig_object: *ZigObject,
844 wasm: *Wasm,
845 pt: Zcu.PerThread,
846 exported: Zcu.Exported,
847 export_indices: []const u32,
848) !void {
849 const zcu = pt.zcu;
850 const ip = &zcu.intern_pool;
851 const nav_index = switch (exported) {
852 .nav => |nav| nav,
853 .uav => |uav| {
854 _ = uav;
855 @panic("TODO: implement Wasm linker code for exporting a constant value");
856 },
857 };
858 const nav = ip.getNav(nav_index);
859 const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
860 const nav_info = zig_object.navs.getPtr(nav_index).?;
861 const atom = wasm.getAtom(atom_index);
862 const atom_sym = wasm.symbolLocSymbol(atom.symbolLoc()).*;
863 const gpa = zcu.gpa;
864 log.debug("Updating exports for decl '{}'", .{nav.name.fmt(ip)});
865
866 for (export_indices) |export_idx| {
867 const exp = zcu.all_exports.items[export_idx];
868 if (exp.opts.section.toSlice(ip)) |section| {
869 try zcu.failed_exports.putNoClobber(gpa, export_idx, try Zcu.ErrorMsg.create(
870 gpa,
871 zcu.navSrcLoc(nav_index),
872 "Unimplemented: ExportOptions.section '{s}'",
873 .{section},
874 ));
875 continue;
876 }
877
878 const export_name = try wasm.internString(exp.opts.name.toSlice(ip));
879 const sym_index = if (nav_info.@"export"(zig_object, export_name)) |idx| idx else index: {
880 const sym_index = try zig_object.allocateSymbol(gpa);
881 try nav_info.appendExport(gpa, sym_index);
882 break :index sym_index;
883 };
884
885 const sym = zig_object.symbol(sym_index);
886 sym.setGlobal(true);
887 sym.setUndefined(false);
888 sym.index = atom_sym.index;
889 sym.tag = atom_sym.tag;
890 sym.name = export_name;
891
892 switch (exp.opts.linkage) {
893 .internal => {
894 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
895 },
896 .weak => {
897 sym.setFlag(.WASM_SYM_BINDING_WEAK);
898 },
899 .strong => {}, // symbols are strong by default
900 .link_once => {
901 try zcu.failed_exports.putNoClobber(gpa, export_idx, try Zcu.ErrorMsg.create(
902 gpa,
903 zcu.navSrcLoc(nav_index),
904 "Unimplemented: LinkOnce",
905 .{},
906 ));
907 continue;
908 },
909 }
910 if (exp.opts.visibility == .hidden) {
911 sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
912 }
913 log.debug(" with name '{s}' - {}", .{ wasm.stringSlice(export_name), sym });
914 try zig_object.global_syms.put(gpa, export_name, sym_index);
915 try wasm.symbol_atom.put(gpa, .{ .file = .zig_object, .index = sym_index }, atom_index);
916 }
917}
918
919pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.Index) void {
920 const gpa = wasm.base.comp.gpa;
921 const zcu = wasm.base.comp.zcu.?;
922 const ip = &zcu.intern_pool;
923 const nav_info = zig_object.navs.getPtr(nav_index).?;
924 const atom_index = nav_info.atom;
925 const atom = wasm.getAtomPtr(atom_index);
926 zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {};
927 for (nav_info.exports.items) |exp_sym_index| {
928 const exp_sym = zig_object.symbol(exp_sym_index);
929 exp_sym.tag = .dead;
930 zig_object.symbols_free_list.append(exp_sym_index) catch {};
931 }
932 nav_info.exports.deinit(gpa);
933 std.debug.assert(zig_object.navs.remove(nav_index));
934 const sym = &zig_object.symbols.items[atom.sym_index];
935 for (atom.locals.items) |local_atom_index| {
936 const local_atom = wasm.getAtom(local_atom_index);
937 const local_symbol = &zig_object.symbols.items[local_atom.sym_index];
938 std.debug.assert(local_symbol.tag == .data);
939 zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
940 std.debug.assert(wasm.symbol_atom.remove(local_atom.symbolLoc()));
941 local_symbol.tag = .dead; // also for any local symbol
942 const segment = &zig_object.segment_info.items[local_atom.sym_index];
943 gpa.free(segment.name);
944 segment.name = &.{}; // Ensure no accidental double free
945 }
946
947 const nav = ip.getNav(nav_index);
948 if (nav.getExtern(ip) != null) {
949 std.debug.assert(zig_object.imports.remove(atom.sym_index));
950 }
951 std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc()));
952
953 // if (wasm.dwarf) |*dwarf| {
954 // dwarf.freeDecl(decl_index);
955 // }
956
957 atom.prev = null;
958 sym.tag = .dead;
959 if (sym.isGlobal()) {
960 std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
961 }
962 if (ip.isFunctionType(nav.typeOf(ip))) {
963 zig_object.functions_free_list.append(gpa, sym.index) catch {};
964 std.debug.assert(zig_object.atom_types.remove(atom_index));
965 } else {
966 zig_object.segment_free_list.append(gpa, sym.index) catch {};
967 const segment = &zig_object.segment_info.items[sym.index];
968 gpa.free(segment.name);
969 segment.name = &.{}; // Prevent accidental double free
970 }
971}
972
973fn getTypeIndex(zig_object: *const ZigObject, func_type: std.wasm.Type) ?u32 {
974 var index: u32 = 0;
975 while (index < zig_object.func_types.items.len) : (index += 1) {
976 if (zig_object.func_types.items[index].eql(func_type)) return index;
977 }
978 return null;
979}
980
981/// Searches for a matching function signature. When no matching signature is found,
982/// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
983pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_type: std.wasm.Type) !u32 {
984 if (zig_object.getTypeIndex(func_type)) |index| {
985 return index;
986 }
987
988 // functype does not exist.
989 const index: u32 = @intCast(zig_object.func_types.items.len);
990 const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
991 errdefer gpa.free(params);
992 const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
993 errdefer gpa.free(returns);
994 try zig_object.func_types.append(gpa, .{
995 .params = params,
996 .returns = returns,
997 });
998 return index;
999}
1000
1001/// Generates an atom containing the global error set' size.
1002/// This will only be generated if the symbol exists.
1003fn setupErrorsLen(zig_object: *ZigObject, wasm: *Wasm) !void {
1004 const gpa = wasm.base.comp.gpa;
1005 const sym_index = zig_object.global_syms.get(wasm.preloaded_strings.__zig_errors_len) orelse return;
1006
1007 const errors_len = 1 + wasm.base.comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;
1008 // overwrite existing atom if it already exists (maybe the error set has increased)
1009 // if not, allocate a new atom.
1010 const atom_index = if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index })) |index| blk: {
1011 const atom = wasm.getAtomPtr(index);
1012 atom.prev = .null;
1013 atom.deinit(gpa);
1014 break :blk index;
1015 } else idx: {
1016 // We found a call to __zig_errors_len so make the symbol a local symbol
1017 // and define it, so the final binary or resulting object file will not attempt
1018 // to resolve it.
1019 const sym = zig_object.symbol(sym_index);
1020 sym.setGlobal(false);
1021 sym.setUndefined(false);
1022 sym.tag = .data;
1023 const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len");
1024 sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2");
1025 break :idx try wasm.createAtom(sym_index, .zig_object);
1026 };
1027
1028 const atom = wasm.getAtomPtr(atom_index);
1029 atom.code.clearRetainingCapacity();
1030 atom.sym_index = sym_index;
1031 atom.size = 2;
1032 atom.alignment = .@"2";
1033 try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
1034}
1035
1036/// Initializes symbols and atoms for the debug sections
1037/// Initialization is only done when compiling Zig code.
1038/// When Zig is invoked as a linker instead, the atoms
1039/// and symbols come from the object files instead.
1040pub fn initDebugSections(zig_object: *ZigObject) !void {
1041 if (zig_object.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
1042 std.debug.assert(zig_object.debug_info_index == null);
1043 // this will create an Atom and set the index for us.
1044 zig_object.debug_info_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_info_index, ".debug_info");
1045 zig_object.debug_line_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_line_index, ".debug_line");
1046 zig_object.debug_loc_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_loc_index, ".debug_loc");
1047 zig_object.debug_abbrev_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_abbrev_index, ".debug_abbrev");
1048 zig_object.debug_ranges_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_ranges_index, ".debug_ranges");
1049 zig_object.debug_str_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_str_index, ".debug_str");
1050 zig_object.debug_pubnames_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubnames_index, ".debug_pubnames");
1051 zig_object.debug_pubtypes_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubtypes_index, ".debug_pubtypes");
1052}
1053
1054/// From a given index variable, creates a new debug section.
1055/// This initializes the index, appends a new segment,
1056/// and finally, creates a managed `Atom`.
1057pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
1058 const gpa = wasm.base.comp.gpa;
1059 const new_index: u32 = @intCast(zig_object.segments.items.len);
1060 index.* = new_index;
1061 try zig_object.appendDummySegment();
1062
1063 const sym_index = try zig_object.allocateSymbol(gpa);
1064 const atom_index = try wasm.createAtom(sym_index, .zig_object);
1065 const atom = wasm.getAtomPtr(atom_index);
1066 zig_object.symbols.items[sym_index] = .{
1067 .tag = .section,
1068 .name = try wasm.internString(name),
1069 .index = 0,
1070 .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1071 };
1072
1073 atom.alignment = .@"1"; // debug sections are always 1-byte-aligned
1074 return atom_index;
1075}
1076
1077pub fn updateLineNumber(zig_object: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1078 if (zig_object.dwarf) |*dw| {
1079 try dw.updateLineNumber(pt.zcu, ti_id);
1080 }
1081}
1082
1083/// Allocates debug atoms into their respective debug sections
1084/// to merge them with maybe-existing debug atoms from object files.
1085fn allocateDebugAtoms(zig_object: *ZigObject) !void {
1086 if (zig_object.dwarf == null) return;
1087
1088 const allocAtom = struct {
1089 fn f(ctx: *ZigObject, maybe_index: *?u32, atom_index: Atom.Index) !void {
1090 const index = maybe_index.* orelse idx: {
1091 const index = @as(u32, @intCast(ctx.segments.items.len));
1092 try ctx.appendDummySegment();
1093 maybe_index.* = index;
1094 break :idx index;
1095 };
1096 const atom = ctx.getAtomPtr(atom_index);
1097 atom.size = @as(u32, @intCast(atom.code.items.len));
1098 ctx.symbols.items[atom.sym_index].index = index;
1099 try ctx.appendAtomAtIndex(index, atom_index);
1100 }
1101 }.f;
1102
1103 try allocAtom(zig_object, &zig_object.debug_info_index, zig_object.debug_info_atom.?);
1104 try allocAtom(zig_object, &zig_object.debug_line_index, zig_object.debug_line_atom.?);
1105 try allocAtom(zig_object, &zig_object.debug_loc_index, zig_object.debug_loc_atom.?);
1106 try allocAtom(zig_object, &zig_object.debug_str_index, zig_object.debug_str_atom.?);
1107 try allocAtom(zig_object, &zig_object.debug_ranges_index, zig_object.debug_ranges_atom.?);
1108 try allocAtom(zig_object, &zig_object.debug_abbrev_index, zig_object.debug_abbrev_atom.?);
1109 try allocAtom(zig_object, &zig_object.debug_pubnames_index, zig_object.debug_pubnames_atom.?);
1110 try allocAtom(zig_object, &zig_object.debug_pubtypes_index, zig_object.debug_pubtypes_atom.?);
1111}
1112
1113/// For the given `decl_index`, stores the corresponding type representing the function signature.
1114/// Asserts declaration has an associated `Atom`.
1115/// Returns the index into the list of types.
1116pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, nav_index: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 {
1117 const nav_info = zig_object.navs.get(nav_index).?;
1118 const index = try zig_object.putOrGetFuncType(gpa, func_type);
1119 try zig_object.atom_types.put(gpa, nav_info.atom, index);
1120 return index;
1121}
1122
1123/// The symbols in ZigObject are already represented by an atom as we need to store its data.
1124/// So rather than creating a new Atom and returning its index, we use this opportunity to scan
1125/// its relocations and create any GOT symbols or function table indexes it may require.
1126pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm: *Wasm, index: Symbol.Index) !Atom.Index {
1127 const gpa = wasm.base.comp.gpa;
1128 const loc: Wasm.SymbolLoc = .{ .file = .zig_object, .index = index };
1129 const atom_index = wasm.symbol_atom.get(loc).?;
1130 const final_index = try wasm.getMatchingSegment(.zig_object, index);
1131 try wasm.appendAtomAtIndex(final_index, atom_index);
1132 const atom = wasm.getAtom(atom_index);
1133 for (atom.relocs.items) |reloc| {
1134 const reloc_index: Symbol.Index = @enumFromInt(reloc.index);
1135 switch (reloc.relocation_type) {
1136 .R_WASM_TABLE_INDEX_I32,
1137 .R_WASM_TABLE_INDEX_I64,
1138 .R_WASM_TABLE_INDEX_SLEB,
1139 .R_WASM_TABLE_INDEX_SLEB64,
1140 => {
1141 try wasm.function_table.put(gpa, .{
1142 .file = .zig_object,
1143 .index = reloc_index,
1144 }, 0);
1145 },
1146 .R_WASM_GLOBAL_INDEX_I32,
1147 .R_WASM_GLOBAL_INDEX_LEB,
1148 => {
1149 const sym = zig_object.symbol(reloc_index);
1150 if (sym.tag != .global) {
1151 try wasm.got_symbols.append(gpa, .{
1152 .file = .zig_object,
1153 .index = reloc_index,
1154 });
1155 }
1156 },
1157 else => {},
1158 }
1159 }
1160 return atom_index;
1161}
1162
1163/// Creates a new Wasm function with a given symbol name and body.
1164/// Returns the symbol index of the new function.
1165pub fn createFunction(
1166 zig_object: *ZigObject,
1167 wasm: *Wasm,
1168 symbol_name: []const u8,
1169 func_ty: std.wasm.Type,
1170 function_body: *std.ArrayList(u8),
1171 relocations: *std.ArrayList(Wasm.Relocation),
1172) !Symbol.Index {
1173 const gpa = wasm.base.comp.gpa;
1174 const sym_index = try zig_object.allocateSymbol(gpa);
1175 const sym = zig_object.symbol(sym_index);
1176 sym.tag = .function;
1177 sym.name = try wasm.internString(symbol_name);
1178 const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
1179 sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index });
1180
1181 const atom_index = try wasm.createAtom(sym_index, .zig_object);
1182 const atom = wasm.getAtomPtr(atom_index);
1183 atom.size = @intCast(function_body.items.len);
1184 atom.code = function_body.moveToUnmanaged();
1185 atom.relocs = relocations.moveToUnmanaged();
1186
1187 try zig_object.synthetic_functions.append(gpa, atom_index);
1188 return sym_index;
1189}
1190
1191/// Appends a new `std.wasm.Func` to the list of functions and returns its index.
1192fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 {
1193 const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx|
1194 idx
1195 else idx: {
1196 const len: u32 = @intCast(zig_object.functions.items.len);
1197 _ = try zig_object.functions.addOne(gpa);
1198 break :idx len;
1199 };
1200 zig_object.functions.items[index] = func;
1201
1202 return index;
1203}
1204
1205pub fn flushModule(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void {
1206 try zig_object.populateErrorNameTable(wasm, tid);
1207 try zig_object.setupErrorsLen(wasm);
1208}
1209
1210const build_options = @import("build_options");
1211const builtin = @import("builtin");
1212const codegen = @import("../../codegen.zig");
1213const link = @import("../../link.zig");
1214const log = std.log.scoped(.zig_object);
1215const std = @import("std");
1216const Path = std.Build.Cache.Path;
1217
1218const Air = @import("../../Air.zig");
1219const Atom = Wasm.Atom;
1220const Dwarf = @import("../Dwarf.zig");
1221const InternPool = @import("../../InternPool.zig");
1222const Liveness = @import("../../Liveness.zig");
1223const Zcu = @import("../../Zcu.zig");
1224const Symbol = @import("Symbol.zig");
1225const Type = @import("../../Type.zig");
1226const Value = @import("../../Value.zig");
1227const Wasm = @import("../Wasm.zig");
1228const AnalUnit = InternPool.AnalUnit;
1229const ZigObject = @This();
src/main.zig+4
...@@ -75,6 +75,10 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {...@@ -75,6 +75,10 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
75 process.exit(1);75 process.exit(1);
76}76}
7777
78/// Shaming all the locations that inappropriately use an O(N) search algorithm.
79/// Please delete this and fix the compilation errors!
80pub const @"bad O(N)" = void;
81
78const normal_usage =82const normal_usage =
79 \\Usage: zig [command] [options]83 \\Usage: zig [command] [options]
80 \\84 \\
src/register_manager.zig+9-14
...@@ -14,19 +14,14 @@ const link = @import("link.zig");...@@ -14,19 +14,14 @@ const link = @import("link.zig");
1414
15const log = std.log.scoped(.register_manager);15const log = std.log.scoped(.register_manager);
1616
17pub const AllocateRegistersError = error{17pub const AllocationError = error{
18 /// No registers are available anymore
19 OutOfRegisters,18 OutOfRegisters,
20 /// Can happen when spilling an instruction in codegen runs out of
21 /// memory, so we propagate that error
22 OutOfMemory,19 OutOfMemory,
23 /// Can happen when spilling an instruction in codegen triggers integer20 /// Compiler was asked to operate on a number larger than supported.
24 /// overflow, so we propagate that error
25 Overflow,21 Overflow,
26 /// Can happen when spilling an instruction triggers a codegen22 /// Indicates the error is already stored in `failed_codegen` on the Zcu.
27 /// error, so we propagate that error
28 CodegenFail,23 CodegenFail,
29} || link.File.UpdateDebugInfoError;24};
3025
31pub fn RegisterManager(26pub fn RegisterManager(
32 comptime Function: type,27 comptime Function: type,
...@@ -281,7 +276,7 @@ pub fn RegisterManager(...@@ -281,7 +276,7 @@ pub fn RegisterManager(
281 comptime count: comptime_int,276 comptime count: comptime_int,
282 insts: [count]?Air.Inst.Index,277 insts: [count]?Air.Inst.Index,
283 register_class: RegisterBitSet,278 register_class: RegisterBitSet,
284 ) AllocateRegistersError![count]Register {279 ) AllocationError![count]Register {
285 comptime assert(count > 0 and count <= tracked_registers.len);280 comptime assert(count > 0 and count <= tracked_registers.len);
286281
287 var locked_registers = self.locked_registers;282 var locked_registers = self.locked_registers;
...@@ -338,7 +333,7 @@ pub fn RegisterManager(...@@ -338,7 +333,7 @@ pub fn RegisterManager(
338 self: *Self,333 self: *Self,
339 inst: ?Air.Inst.Index,334 inst: ?Air.Inst.Index,
340 register_class: RegisterBitSet,335 register_class: RegisterBitSet,
341 ) AllocateRegistersError!Register {336 ) AllocationError!Register {
342 return (try self.allocRegs(1, .{inst}, register_class))[0];337 return (try self.allocRegs(1, .{inst}, register_class))[0];
343 }338 }
344339
...@@ -349,7 +344,7 @@ pub fn RegisterManager(...@@ -349,7 +344,7 @@ pub fn RegisterManager(
349 self: *Self,344 self: *Self,
350 tracked_index: TrackedIndex,345 tracked_index: TrackedIndex,
351 inst: ?Air.Inst.Index,346 inst: ?Air.Inst.Index,
352 ) AllocateRegistersError!void {347 ) AllocationError!void {
353 log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst });348 log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst });
354 if (!self.isRegIndexFree(tracked_index)) {349 if (!self.isRegIndexFree(tracked_index)) {
355 // Move the instruction that was previously there to a350 // Move the instruction that was previously there to a
...@@ -362,7 +357,7 @@ pub fn RegisterManager(...@@ -362,7 +357,7 @@ pub fn RegisterManager(
362 }357 }
363 self.getRegIndexAssumeFree(tracked_index, inst);358 self.getRegIndexAssumeFree(tracked_index, inst);
364 }359 }
365 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {360 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocationError!void {
366 log.debug("getting reg: {}", .{reg});361 log.debug("getting reg: {}", .{reg});
367 return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst);362 return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst);
368 }363 }
...@@ -370,7 +365,7 @@ pub fn RegisterManager(...@@ -370,7 +365,7 @@ pub fn RegisterManager(
370 self: *Self,365 self: *Self,
371 comptime reg: Register,366 comptime reg: Register,
372 inst: ?Air.Inst.Index,367 inst: ?Air.Inst.Index,
373 ) AllocateRegistersError!void {368 ) AllocationError!void {
374 return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst);369 return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst);
375 }370 }
376371