| author | |
| committer | |
| log | d4fe4698d9ff865ed1dc7e0163f2d5fcbe2b45a6 |
| tree | 160d596e8ab0ab9568dac3f026c2ce42ad1c935e |
| parent | 77273103a8f9895ceab28287dffcf4d4c6fcb91b |
| parent | eda8b6e137a10f398cd292b533e924960f7fc409 |
| signature |
wasm linker: aggressive rewrite towards Data-Oriented Design104 files changed, 13672 insertions(+), 12204 deletions(-)
CMakeLists.txt+1-2| ... | @@ -643,9 +643,8 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -643,9 +643,8 @@ set(ZIG_STAGE2_SOURCES |
| 643 | src/link/StringTable.zig | 643 | src/link/StringTable.zig |
| 644 | src/link/Wasm.zig | 644 | src/link/Wasm.zig |
| 645 | src/link/Wasm/Archive.zig | 645 | src/link/Wasm/Archive.zig |
| 646 | src/link/Wasm/Flush.zig | ||
| 646 | src/link/Wasm/Object.zig | 647 | src/link/Wasm/Object.zig |
| 647 | src/link/Wasm/Symbol.zig | ||
| 648 | src/link/Wasm/ZigObject.zig | ||
| 649 | src/link/aarch64.zig | 648 | src/link/aarch64.zig |
| 650 | src/link/riscv.zig | 649 | src/link/riscv.zig |
| 651 | src/link/table_section.zig | 650 | src/link/table_section.zig |
build.zig+5| ... | @@ -447,6 +447,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -447,6 +447,7 @@ pub fn build(b: *std.Build) !void { |
| 447 | .skip_single_threaded = skip_single_threaded, | 447 | .skip_single_threaded = skip_single_threaded, |
| 448 | .skip_non_native = skip_non_native, | 448 | .skip_non_native = skip_non_native, |
| 449 | .skip_libc = skip_libc, | 449 | .skip_libc = skip_libc, |
| 450 | .use_llvm = use_llvm, | ||
| 450 | .max_rss = 1 * 1024 * 1024 * 1024, | 451 | .max_rss = 1 * 1024 * 1024 * 1024, |
| 451 | })); | 452 | })); |
| 452 | 453 | ||
| ... | @@ -462,6 +463,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -462,6 +463,7 @@ pub fn build(b: *std.Build) !void { |
| 462 | .skip_single_threaded = true, | 463 | .skip_single_threaded = true, |
| 463 | .skip_non_native = skip_non_native, | 464 | .skip_non_native = skip_non_native, |
| 464 | .skip_libc = skip_libc, | 465 | .skip_libc = skip_libc, |
| 466 | .use_llvm = use_llvm, | ||
| 465 | })); | 467 | })); |
| 466 | 468 | ||
| 467 | test_modules_step.dependOn(tests.addModuleTests(b, .{ | 469 | test_modules_step.dependOn(tests.addModuleTests(b, .{ |
| ... | @@ -476,6 +478,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -476,6 +478,7 @@ pub fn build(b: *std.Build) !void { |
| 476 | .skip_single_threaded = true, | 478 | .skip_single_threaded = true, |
| 477 | .skip_non_native = skip_non_native, | 479 | .skip_non_native = skip_non_native, |
| 478 | .skip_libc = true, | 480 | .skip_libc = true, |
| 481 | .use_llvm = use_llvm, | ||
| 479 | .no_builtin = true, | 482 | .no_builtin = true, |
| 480 | })); | 483 | })); |
| 481 | 484 | ||
| ... | @@ -491,6 +494,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -491,6 +494,7 @@ pub fn build(b: *std.Build) !void { |
| 491 | .skip_single_threaded = true, | 494 | .skip_single_threaded = true, |
| 492 | .skip_non_native = skip_non_native, | 495 | .skip_non_native = skip_non_native, |
| 493 | .skip_libc = true, | 496 | .skip_libc = true, |
| 497 | .use_llvm = use_llvm, | ||
| 494 | .no_builtin = true, | 498 | .no_builtin = true, |
| 495 | })); | 499 | })); |
| 496 | 500 | ||
| ... | @@ -506,6 +510,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -506,6 +510,7 @@ pub fn build(b: *std.Build) !void { |
| 506 | .skip_single_threaded = skip_single_threaded, | 510 | .skip_single_threaded = skip_single_threaded, |
| 507 | .skip_non_native = skip_non_native, | 511 | .skip_non_native = skip_non_native, |
| 508 | .skip_libc = skip_libc, | 512 | .skip_libc = skip_libc, |
| 513 | .use_llvm = use_llvm, | ||
| 509 | // I observed a value of 4572626944 on the M2 CI. | 514 | // I observed a value of 4572626944 on the M2 CI. |
| 510 | .max_rss = 5029889638, | 515 | .max_rss = 5029889638, |
| 511 | })); | 516 | })); |
lib/std/Build/Step/CheckObject.zig+17-4| ... | @@ -2424,7 +2424,22 @@ const WasmDumper = struct { | ... | @@ -2424,7 +2424,22 @@ const WasmDumper = struct { |
| 2424 | } | 2424 | } |
| 2425 | 2425 | ||
| 2426 | var output = std.ArrayList(u8).init(gpa); | 2426 | var output = std.ArrayList(u8).init(gpa); |
| 2427 | errdefer output.deinit(); | 2427 | defer output.deinit(); |
| 2428 | parseAndDumpInner(step, check, bytes, &fbs, &output) catch |err| switch (err) { | ||
| 2429 | error.EndOfStream => try output.appendSlice("\n<UnexpectedEndOfStream>"), | ||
| 2430 | else => |e| return e, | ||
| 2431 | }; | ||
| 2432 | return output.toOwnedSlice(); | ||
| 2433 | } | ||
| 2434 | |||
| 2435 | fn parseAndDumpInner( | ||
| 2436 | step: *Step, | ||
| 2437 | check: Check, | ||
| 2438 | bytes: []const u8, | ||
| 2439 | fbs: *std.io.FixedBufferStream([]const u8), | ||
| 2440 | output: *std.ArrayList(u8), | ||
| 2441 | ) !void { | ||
| 2442 | const reader = fbs.reader(); | ||
| 2428 | const writer = output.writer(); | 2443 | const writer = output.writer(); |
| 2429 | 2444 | ||
| 2430 | switch (check.kind) { | 2445 | switch (check.kind) { |
| ... | @@ -2442,8 +2457,6 @@ const WasmDumper = struct { | ... | @@ -2442,8 +2457,6 @@ const WasmDumper = struct { |
| 2442 | 2457 | ||
| 2443 | else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), | 2458 | else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), |
| 2444 | } | 2459 | } |
| 2445 | |||
| 2446 | return output.toOwnedSlice(); | ||
| 2447 | } | 2460 | } |
| 2448 | 2461 | ||
| 2449 | fn parseAndDumpSection( | 2462 | fn parseAndDumpSection( |
| ... | @@ -2682,7 +2695,7 @@ const WasmDumper = struct { | ... | @@ -2682,7 +2695,7 @@ const WasmDumper = struct { |
| 2682 | else => unreachable, | 2695 | else => unreachable, |
| 2683 | } | 2696 | } |
| 2684 | const end_opcode = try std.leb.readUleb128(u8, reader); | 2697 | const end_opcode = try std.leb.readUleb128(u8, reader); |
| 2685 | if (end_opcode != std.wasm.opcode(.end)) { | 2698 | if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) { |
| 2686 | return step.fail("expected 'end' opcode in init expression", .{}); | 2699 | return step.fail("expected 'end' opcode in init expression", .{}); |
| 2687 | } | 2700 | } |
| 2688 | } | 2701 | } |
lib/std/Target.zig+6| ... | @@ -1219,6 +1219,12 @@ pub const Cpu = struct { | ... | @@ -1219,6 +1219,12 @@ pub const Cpu = struct { |
| 1219 | } else true; | 1219 | } else true; |
| 1220 | } | 1220 | } |
| 1221 | 1221 | ||
| 1222 | pub fn count(set: Set) std.math.IntFittingRange(0, needed_bit_count) { | ||
| 1223 | var sum: usize = 0; | ||
| 1224 | for (set.ints) |x| sum += @popCount(x); | ||
| 1225 | return @intCast(sum); | ||
| 1226 | } | ||
| 1227 | |||
| 1222 | pub fn isEnabled(set: Set, arch_feature_index: Index) bool { | 1228 | pub fn isEnabled(set: Set, arch_feature_index: Index) bool { |
| 1223 | const usize_index = arch_feature_index / @bitSizeOf(usize); | 1229 | const usize_index = arch_feature_index / @bitSizeOf(usize); |
| 1224 | const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize)); | 1230 | const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize)); |
lib/std/Thread.zig+8-5| ... | @@ -1018,12 +1018,15 @@ const WasiThreadImpl = struct { | ... | @@ -1018,12 +1018,15 @@ const WasiThreadImpl = struct { |
| 1018 | return .{ .thread = &instance.thread }; | 1018 | return .{ .thread = &instance.thread }; |
| 1019 | } | 1019 | } |
| 1020 | 1020 | ||
| 1021 | /// Bootstrap procedure, called by the host environment after thread creation. | 1021 | comptime { |
| 1022 | export fn wasi_thread_start(tid: i32, arg: *Instance) void { | 1022 | if (!builtin.single_threaded) { |
| 1023 | if (builtin.single_threaded) { | 1023 | @export(wasi_thread_start, .{ .name = "wasi_thread_start" }); |
| 1024 | // ensure function is not analyzed in single-threaded mode | ||
| 1025 | return; | ||
| 1026 | } | 1024 | } |
| 1025 | } | ||
| 1026 | |||
| 1027 | /// Called by the host environment after thread creation. | ||
| 1028 | fn wasi_thread_start(tid: i32, arg: *Instance) callconv(.c) void { | ||
| 1029 | comptime assert(!builtin.single_threaded); | ||
| 1027 | __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset); | 1030 | __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset); |
| 1028 | __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset); | 1031 | __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset); |
| 1029 | @atomicStore(u32, &WasiThreadImpl.tls_thread_id, @intCast(tid), .seq_cst); | 1032 | @atomicStore(u32, &WasiThreadImpl.tls_thread_id, @intCast(tid), .seq_cst); |
lib/std/array_hash_map.zig+4-1| ... | @@ -641,10 +641,13 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -641,10 +641,13 @@ pub fn ArrayHashMapUnmanaged( |
| 641 | return self; | 641 | return self; |
| 642 | } | 642 | } |
| 643 | 643 | ||
| 644 | /// An empty `value_list` may be passed, in which case the values array becomes `undefined`. | ||
| 644 | pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void { | 645 | pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void { |
| 645 | try self.entries.resize(gpa, key_list.len); | 646 | try self.entries.resize(gpa, key_list.len); |
| 646 | @memcpy(self.keys(), key_list); | 647 | @memcpy(self.keys(), key_list); |
| 647 | if (@sizeOf(V) != 0) { | 648 | if (value_list.len == 0) { |
| 649 | @memset(self.values(), undefined); | ||
| 650 | } else { | ||
| 648 | assert(key_list.len == value_list.len); | 651 | assert(key_list.len == value_list.len); |
| 649 | @memcpy(self.values(), value_list); | 652 | @memcpy(self.values(), value_list); |
| 650 | } | 653 | } |
lib/std/array_list.zig+2-4| ... | @@ -267,8 +267,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -267,8 +267,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 267 | /// Never invalidates element pointers. | 267 | /// Never invalidates element pointers. |
| 268 | /// Asserts that the list can hold one additional item. | 268 | /// Asserts that the list can hold one additional item. |
| 269 | pub fn appendAssumeCapacity(self: *Self, item: T) void { | 269 | pub fn appendAssumeCapacity(self: *Self, item: T) void { |
| 270 | const new_item_ptr = self.addOneAssumeCapacity(); | 270 | self.addOneAssumeCapacity().* = item; |
| 271 | new_item_ptr.* = item; | ||
| 272 | } | 271 | } |
| 273 | 272 | ||
| 274 | /// Remove the element at index `i`, shift elements after index | 273 | /// Remove the element at index `i`, shift elements after index |
| ... | @@ -879,8 +878,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -879,8 +878,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 879 | /// Never invalidates element pointers. | 878 | /// Never invalidates element pointers. |
| 880 | /// Asserts that the list can hold one additional item. | 879 | /// Asserts that the list can hold one additional item. |
| 881 | pub fn appendAssumeCapacity(self: *Self, item: T) void { | 880 | pub fn appendAssumeCapacity(self: *Self, item: T) void { |
| 882 | const new_item_ptr = self.addOneAssumeCapacity(); | 881 | self.addOneAssumeCapacity().* = item; |
| 883 | new_item_ptr.* = item; | ||
| 884 | } | 882 | } |
| 885 | 883 | ||
| 886 | /// Remove the element at index `i` from the list and return its value. | 884 | /// Remove the element at index `i` from the list and return its value. |
lib/std/io.zig-12| ... | @@ -16,10 +16,6 @@ const Allocator = std.mem.Allocator; | ... | @@ -16,10 +16,6 @@ const Allocator = std.mem.Allocator; |
| 16 | 16 | ||
| 17 | fn getStdOutHandle() posix.fd_t { | 17 | fn getStdOutHandle() posix.fd_t { |
| 18 | if (is_windows) { | 18 | if (is_windows) { |
| 19 | if (builtin.zig_backend == .stage2_aarch64) { | ||
| 20 | // TODO: this is just a temporary workaround until we advance aarch64 backend further along. | ||
| 21 | return windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch windows.INVALID_HANDLE_VALUE; | ||
| 22 | } | ||
| 23 | return windows.peb().ProcessParameters.hStdOutput; | 19 | return windows.peb().ProcessParameters.hStdOutput; |
| 24 | } | 20 | } |
| 25 | 21 | ||
| ... | @@ -36,10 +32,6 @@ pub fn getStdOut() File { | ... | @@ -36,10 +32,6 @@ pub fn getStdOut() File { |
| 36 | 32 | ||
| 37 | fn getStdErrHandle() posix.fd_t { | 33 | fn getStdErrHandle() posix.fd_t { |
| 38 | if (is_windows) { | 34 | if (is_windows) { |
| 39 | if (builtin.zig_backend == .stage2_aarch64) { | ||
| 40 | // TODO: this is just a temporary workaround until we advance aarch64 backend further along. | ||
| 41 | return windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch windows.INVALID_HANDLE_VALUE; | ||
| 42 | } | ||
| 43 | return windows.peb().ProcessParameters.hStdError; | 35 | return windows.peb().ProcessParameters.hStdError; |
| 44 | } | 36 | } |
| 45 | 37 | ||
| ... | @@ -56,10 +48,6 @@ pub fn getStdErr() File { | ... | @@ -56,10 +48,6 @@ pub fn getStdErr() File { |
| 56 | 48 | ||
| 57 | fn getStdInHandle() posix.fd_t { | 49 | fn getStdInHandle() posix.fd_t { |
| 58 | if (is_windows) { | 50 | if (is_windows) { |
| 59 | if (builtin.zig_backend == .stage2_aarch64) { | ||
| 60 | // TODO: this is just a temporary workaround until we advance aarch64 backend further along. | ||
| 61 | return windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch windows.INVALID_HANDLE_VALUE; | ||
| 62 | } | ||
| 63 | return windows.peb().ProcessParameters.hStdInput; | 51 | return windows.peb().ProcessParameters.hStdInput; |
| 64 | } | 52 | } |
| 65 | 53 |
lib/std/wasm.zig+17-180| ... | @@ -4,8 +4,6 @@ | ... | @@ -4,8 +4,6 @@ |
| 4 | const std = @import("std.zig"); | 4 | const std = @import("std.zig"); |
| 5 | const testing = std.testing; | 5 | const testing = std.testing; |
| 6 | 6 | ||
| 7 | // TODO: Add support for multi-byte ops (e.g. table operations) | ||
| 8 | |||
| 9 | /// Wasm instruction opcodes | 7 | /// 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 | }; |
| 197 | 195 | ||
| 198 | /// Returns the integer value of an `Opcode`. Used by the Zig compiler | ||
| 199 | /// to write instructions to the wasm binary file | ||
| 200 | pub fn opcode(op: Opcode) u8 { | ||
| 201 | return @intFromEnum(op); | ||
| 202 | } | ||
| 203 | |||
| 204 | test "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, meaning | 197 | /// 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 | }; |
| 243 | 220 | ||
| 244 | /// Returns the integer value of an `MiscOpcode`. Used by the Zig compiler | ||
| 245 | /// to write instructions to the wasm binary file | ||
| 246 | pub 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, meaning | 222 | /// 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 | }; |
| 514 | 485 | ||
| 515 | /// Returns the integer value of an `SimdOpcode`. Used by the Zig compiler | ||
| 516 | /// to write instructions to the wasm binary file | ||
| 517 | pub 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, meaning | 487 | /// 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 | }; |
| 594 | 559 | ||
| 595 | /// Returns the integer value of an `AtomicsOpcode`. Used by the Zig compiler | ||
| 596 | /// to write instructions to the wasm binary file | ||
| 597 | pub 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.html | 561 | /// https://webassembly.github.io/spec/core/binary/types.html |
| 603 | pub const Valtype = enum(u8) { | 562 | pub 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 | }; |
| 610 | 569 | ||
| 611 | /// Returns the integer value of a `Valtype` | ||
| 612 | pub fn valtype(value: Valtype) u8 { | ||
| 613 | return @intFromEnum(value); | ||
| 614 | } | ||
| 615 | |||
| 616 | /// Reference types, where the funcref references to a function regardless of its type | 570 | /// 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. |
| 618 | pub const RefType = enum(u8) { | 572 | pub 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 | }; |
| 622 | 576 | ||
| 623 | /// Returns the integer value of a `Reftype` | ||
| 624 | pub fn reftype(value: RefType) u8 { | ||
| 625 | return @intFromEnum(value); | ||
| 626 | } | ||
| 627 | |||
| 628 | test "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. |
| 641 | pub const Limits = struct { | 578 | pub const Limits = struct { |
| 642 | flags: u8, | 579 | flags: Flags, |
| 643 | min: u32, | 580 | min: u32, |
| 644 | max: u32, | 581 | max: u32, |
| 645 | 582 | ||
| 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 | }; |
| 659 | 589 | ||
| 660 | /// Initialization expressions are used to set the initial value on an object | 590 | /// 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 | }; |
| 669 | 599 | ||
| 670 | /// Represents a function entry, holding the index to its type | ||
| 671 | pub 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. | ||
| 677 | pub const Table = struct { | ||
| 678 | limits: Limits, | ||
| 679 | reftype: RefType, | ||
| 680 | }; | ||
| 681 | |||
| 682 | /// Describes the layout of the memory where `min` represents | 600 | /// Describes the layout of the memory where `min` represents |
| 683 | /// the minimal amount of pages, and the optional `max` represents | 601 | /// the minimal amount of pages, and the optional `max` represents |
| 684 | /// the max pages. When `null` will allow the host to determine the | 602 | /// 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 | }; |
| 689 | 607 | ||
| 690 | /// Represents the type of a `Global` or an imported global. | ||
| 691 | pub const GlobalType = struct { | ||
| 692 | valtype: Valtype, | ||
| 693 | mutable: bool, | ||
| 694 | }; | ||
| 695 | |||
| 696 | pub 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. | ||
| 703 | pub 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` | ||
| 711 | pub 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 | ||
| 718 | pub 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. | ||
| 733 | pub 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.html | 609 | /// https://webassembly.github.io/spec/core/binary/modules.html |
| 774 | pub const Section = enum(u8) { | 610 | pub 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 | }; |
| 790 | 626 | ||
| 791 | /// Returns the integer value of a given `Section` | ||
| 792 | pub 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.html | 628 | /// https://webassembly.github.io/spec/core/syntax/modules.html |
| 798 | pub const ExternalKind = enum(u8) { | 629 | pub 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 | }; |
| 804 | 635 | ||
| 805 | /// Returns the integer value of a given `ExternalKind` | ||
| 806 | pub fn externalKind(val: ExternalKind) u8 { | ||
| 807 | return @intFromEnum(val); | ||
| 808 | } | ||
| 809 | |||
| 810 | /// Defines the enum values for each subsection id for the "Names" custom section | 636 | /// 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-section | 638 | /// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section |
| ... | @@ -829,7 +655,18 @@ pub const function_type: u8 = 0x60; | ... | @@ -829,7 +655,18 @@ pub const function_type: u8 = 0x60; |
| 829 | pub const result_type: u8 = 0x40; | 655 | pub const result_type: u8 = 0x40; |
| 830 | 656 | ||
| 831 | /// Represents a block which will not return a value | 657 | /// Represents a block which will not return a value |
| 832 | pub const block_empty: u8 = 0x40; | 658 | pub const BlockType = enum(u8) { |
| 659 | empty = 0x40, | ||
| 660 | i32 = 0x7F, | ||
| 661 | i64 = 0x7E, | ||
| 662 | f32 = 0x7D, | ||
| 663 | f64 = 0x7C, | ||
| 664 | v128 = 0x7B, | ||
| 665 | |||
| 666 | pub fn fromValtype(valtype: Valtype) BlockType { | ||
| 667 | return @enumFromInt(@intFromEnum(valtype)); | ||
| 668 | } | ||
| 669 | }; | ||
| 833 | 670 | ||
| 834 | // binary constants | 671 | // binary constants |
| 835 | pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm | 672 | pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm |
lib/std/zig/ErrorBundle.zig+15-13| ... | @@ -11,6 +11,11 @@ string_bytes: []const u8, | ... | @@ -11,6 +11,11 @@ string_bytes: []const u8, |
| 11 | /// The first thing in this array is an `ErrorMessageList`. | 11 | /// The first thing in this array is an `ErrorMessageList`. |
| 12 | extra: []const u32, | 12 | extra: []const u32, |
| 13 | 13 | ||
| 14 | /// Index into `string_bytes`. | ||
| 15 | pub const String = u32; | ||
| 16 | /// Index into `string_bytes`, or null. | ||
| 17 | pub const OptionalString = u32; | ||
| 18 | |||
| 14 | /// Special encoding when there are no errors. | 19 | /// Special encoding when there are no errors. |
| 15 | pub const empty: ErrorBundle = .{ | 20 | pub const empty: ErrorBundle = .{ |
| 16 | .string_bytes = &.{}, | 21 | .string_bytes = &.{}, |
| ... | @@ -33,14 +38,13 @@ pub const ErrorMessageList = struct { | ... | @@ -33,14 +38,13 @@ pub const ErrorMessageList = struct { |
| 33 | len: u32, | 38 | len: u32, |
| 34 | start: u32, | 39 | start: u32, |
| 35 | /// null-terminated string index. 0 means no compile log text. | 40 | /// null-terminated string index. 0 means no compile log text. |
| 36 | compile_log_text: u32, | 41 | compile_log_text: OptionalString, |
| 37 | }; | 42 | }; |
| 38 | 43 | ||
| 39 | /// Trailing: | 44 | /// Trailing: |
| 40 | /// * ReferenceTrace for each reference_trace_len | 45 | /// * ReferenceTrace for each reference_trace_len |
| 41 | pub const SourceLocation = struct { | 46 | pub const SourceLocation = struct { |
| 42 | /// null terminated string index | 47 | src_path: String, |
| 43 | src_path: u32, | ||
| 44 | line: u32, | 48 | line: u32, |
| 45 | column: u32, | 49 | column: u32, |
| 46 | /// byte offset of starting token | 50 | /// byte offset of starting token |
| ... | @@ -49,17 +53,15 @@ pub const SourceLocation = struct { | ... | @@ -49,17 +53,15 @@ pub const SourceLocation = struct { |
| 49 | span_main: u32, | 53 | span_main: u32, |
| 50 | /// byte offset of end of last token | 54 | /// byte offset of end of last token |
| 51 | span_end: u32, | 55 | span_end: u32, |
| 52 | /// null terminated string index, possibly null. | ||
| 53 | /// Does not include the trailing newline. | 56 | /// Does not include the trailing newline. |
| 54 | source_line: u32 = 0, | 57 | source_line: OptionalString = 0, |
| 55 | reference_trace_len: u32 = 0, | 58 | reference_trace_len: u32 = 0, |
| 56 | }; | 59 | }; |
| 57 | 60 | ||
| 58 | /// Trailing: | 61 | /// Trailing: |
| 59 | /// * MessageIndex for each notes_len. | 62 | /// * MessageIndex for each notes_len. |
| 60 | pub const ErrorMessage = struct { | 63 | pub const ErrorMessage = struct { |
| 61 | /// null terminated string index | 64 | msg: String, |
| 62 | msg: u32, | ||
| 63 | /// Usually one, but incremented for redundant messages. | 65 | /// Usually one, but incremented for redundant messages. |
| 64 | count: u32 = 1, | 66 | count: u32 = 1, |
| 65 | src_loc: SourceLocationIndex = .none, | 67 | src_loc: SourceLocationIndex = .none, |
| ... | @@ -71,7 +73,7 @@ pub const ReferenceTrace = struct { | ... | @@ -71,7 +73,7 @@ pub const ReferenceTrace = struct { |
| 71 | /// Except for the sentinel ReferenceTrace element, in which case: | 73 | /// Except for the sentinel ReferenceTrace element, in which case: |
| 72 | /// * 0 means remaining references hidden | 74 | /// * 0 means remaining references hidden |
| 73 | /// * >0 means N references hidden | 75 | /// * >0 means N references hidden |
| 74 | decl_name: u32, | 76 | decl_name: String, |
| 75 | /// Index into extra of a SourceLocation | 77 | /// Index into extra of a SourceLocation |
| 76 | /// If this is 0, this is the sentinel ReferenceTrace element. | 78 | /// If this is 0, this is the sentinel ReferenceTrace element. |
| 77 | src_loc: SourceLocationIndex, | 79 | src_loc: SourceLocationIndex, |
| ... | @@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, | ... | @@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, |
| 138 | } | 140 | } |
| 139 | 141 | ||
| 140 | /// Given an index into `string_bytes` returns the null-terminated string found there. | 142 | /// Given an index into `string_bytes` returns the null-terminated string found there. |
| 141 | pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 { | 143 | pub fn nullTerminatedString(eb: ErrorBundle, index: String) [:0]const u8 { |
| 142 | const string_bytes = eb.string_bytes; | 144 | const string_bytes = eb.string_bytes; |
| 143 | var end: usize = index; | 145 | var end: usize = index; |
| 144 | while (string_bytes[end] != 0) { | 146 | while (string_bytes[end] != 0) { |
| ... | @@ -384,18 +386,18 @@ pub const Wip = struct { | ... | @@ -384,18 +386,18 @@ pub const Wip = struct { |
| 384 | }; | 386 | }; |
| 385 | } | 387 | } |
| 386 | 388 | ||
| 387 | pub fn addString(wip: *Wip, s: []const u8) Allocator.Error!u32 { | 389 | pub fn addString(wip: *Wip, s: []const u8) Allocator.Error!String { |
| 388 | const gpa = wip.gpa; | 390 | const gpa = wip.gpa; |
| 389 | const index: u32 = @intCast(wip.string_bytes.items.len); | 391 | const index: String = @intCast(wip.string_bytes.items.len); |
| 390 | try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); | 392 | try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); |
| 391 | wip.string_bytes.appendSliceAssumeCapacity(s); | 393 | wip.string_bytes.appendSliceAssumeCapacity(s); |
| 392 | wip.string_bytes.appendAssumeCapacity(0); | 394 | wip.string_bytes.appendAssumeCapacity(0); |
| 393 | return index; | 395 | return index; |
| 394 | } | 396 | } |
| 395 | 397 | ||
| 396 | pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!u32 { | 398 | pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String { |
| 397 | const gpa = wip.gpa; | 399 | const gpa = wip.gpa; |
| 398 | const index: u32 = @intCast(wip.string_bytes.items.len); | 400 | const index: String = @intCast(wip.string_bytes.items.len); |
| 399 | try wip.string_bytes.writer(gpa).print(fmt, args); | 401 | try wip.string_bytes.writer(gpa).print(fmt, args); |
| 400 | try wip.string_bytes.append(gpa, 0); | 402 | try wip.string_bytes.append(gpa, 0); |
| 401 | return index; | 403 | return index; |
src/Compilation.zig+164-8| ... | @@ -113,6 +113,14 @@ link_diags: link.Diags, | ... | @@ -113,6 +113,14 @@ link_diags: link.Diags, |
| 113 | link_task_queue: ThreadSafeQueue(link.Task) = .empty, | 113 | link_task_queue: ThreadSafeQueue(link.Task) = .empty, |
| 114 | /// Ensure only 1 simultaneous call to `flushTaskQueue`. | 114 | /// Ensure only 1 simultaneous call to `flushTaskQueue`. |
| 115 | link_task_queue_safety: std.debug.SafetyLock = .{}, | 115 | link_task_queue_safety: std.debug.SafetyLock = .{}, |
| 116 | /// If any tasks are queued up that depend on prelink being finished, they are moved | ||
| 117 | /// here until prelink finishes. | ||
| 118 | link_task_queue_postponed: std.ArrayListUnmanaged(link.Task) = .empty, | ||
| 119 | /// Initialized with how many link input tasks are expected. After this reaches zero | ||
| 120 | /// the linker will begin the prelink phase. | ||
| 121 | /// Initialized in the Compilation main thread before the pipeline; modified only in | ||
| 122 | /// the linker task thread. | ||
| 123 | remaining_prelink_tasks: u32, | ||
| 116 | 124 | ||
| 117 | work_queues: [ | 125 | work_queues: [ |
| 118 | len: { | 126 | len: { |
| ... | @@ -1515,6 +1523,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1515,6 +1523,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1515 | .file_system_inputs = options.file_system_inputs, | 1523 | .file_system_inputs = options.file_system_inputs, |
| 1516 | .parent_whole_cache = options.parent_whole_cache, | 1524 | .parent_whole_cache = options.parent_whole_cache, |
| 1517 | .link_diags = .init(gpa), | 1525 | .link_diags = .init(gpa), |
| 1526 | .remaining_prelink_tasks = 0, | ||
| 1518 | }; | 1527 | }; |
| 1519 | 1528 | ||
| 1520 | // Prevent some footguns by making the "any" fields of config reflect | 1529 | // Prevent some footguns by making the "any" fields of config reflect |
| ... | @@ -1587,6 +1596,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1587,6 +1596,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1587 | .pdb_source_path = options.pdb_source_path, | 1596 | .pdb_source_path = options.pdb_source_path, |
| 1588 | .pdb_out_path = options.pdb_out_path, | 1597 | .pdb_out_path = options.pdb_out_path, |
| 1589 | .entry_addr = null, // CLI does not expose this option (yet?) | 1598 | .entry_addr = null, // CLI does not expose this option (yet?) |
| 1599 | .object_host_name = "env", | ||
| 1590 | }; | 1600 | }; |
| 1591 | 1601 | ||
| 1592 | switch (options.cache_mode) { | 1602 | switch (options.cache_mode) { |
| ... | @@ -1715,6 +1725,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1715,6 +1725,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1715 | }; | 1725 | }; |
| 1716 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); | 1726 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); |
| 1717 | } | 1727 | } |
| 1728 | comp.remaining_prelink_tasks += @intCast(comp.c_object_table.count()); | ||
| 1718 | 1729 | ||
| 1719 | // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`. | 1730 | // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`. |
| 1720 | const win32_resource_count = | 1731 | const win32_resource_count = |
| ... | @@ -1722,6 +1733,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1722,6 +1733,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1722 | if (win32_resource_count > 0) { | 1733 | if (win32_resource_count > 0) { |
| 1723 | dev.check(.win32_resource); | 1734 | dev.check(.win32_resource); |
| 1724 | try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count); | 1735 | try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count); |
| 1736 | // Add this after adding logic to updateWin32Resource to pass the | ||
| 1737 | // result into link.loadInput. loadInput integration is not implemented | ||
| 1738 | // for Windows linking logic yet. | ||
| 1739 | //comp.remaining_prelink_tasks += @intCast(win32_resource_count); | ||
| 1725 | for (options.rc_source_files) |rc_source_file| { | 1740 | for (options.rc_source_files) |rc_source_file| { |
| 1726 | const win32_resource = try gpa.create(Win32Resource); | 1741 | const win32_resource = try gpa.create(Win32Resource); |
| 1727 | errdefer gpa.destroy(win32_resource); | 1742 | errdefer gpa.destroy(win32_resource); |
| ... | @@ -1732,6 +1747,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1732,6 +1747,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1732 | }; | 1747 | }; |
| 1733 | comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {}); | 1748 | comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {}); |
| 1734 | } | 1749 | } |
| 1750 | |||
| 1735 | if (options.manifest_file) |manifest_path| { | 1751 | if (options.manifest_file) |manifest_path| { |
| 1736 | const win32_resource = try gpa.create(Win32Resource); | 1752 | const win32_resource = try gpa.create(Win32Resource); |
| 1737 | errdefer gpa.destroy(win32_resource); | 1753 | errdefer gpa.destroy(win32_resource); |
| ... | @@ -1779,10 +1795,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1779,10 +1795,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1779 | inline for (fields) |field| { | 1795 | inline for (fields) |field| { |
| 1780 | if (@field(paths, field.name)) |path| { | 1796 | if (@field(paths, field.name)) |path| { |
| 1781 | comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); | 1797 | comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); |
| 1798 | comp.remaining_prelink_tasks += 1; | ||
| 1782 | } | 1799 | } |
| 1783 | } | 1800 | } |
| 1784 | // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. | 1801 | // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. |
| 1785 | comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc); | 1802 | comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc); |
| 1803 | comp.remaining_prelink_tasks += 1; | ||
| 1786 | } else if (target.isMusl() and !target.isWasm()) { | 1804 | } else if (target.isMusl() and !target.isWasm()) { |
| 1787 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | 1805 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1788 | 1806 | ||
| ... | @@ -1791,14 +1809,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1791,14 +1809,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1791 | .{ .musl_crt_file = .crti_o }, | 1809 | .{ .musl_crt_file = .crti_o }, |
| 1792 | .{ .musl_crt_file = .crtn_o }, | 1810 | .{ .musl_crt_file = .crtn_o }, |
| 1793 | }); | 1811 | }); |
| 1812 | comp.remaining_prelink_tasks += 2; | ||
| 1794 | } | 1813 | } |
| 1795 | if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { | 1814 | if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { |
| 1796 | try comp.queueJobs(&.{.{ .musl_crt_file = f }}); | 1815 | try comp.queueJobs(&.{.{ .musl_crt_file = f }}); |
| 1816 | comp.remaining_prelink_tasks += 1; | ||
| 1797 | } | 1817 | } |
| 1798 | try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) { | 1818 | try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) { |
| 1799 | .static => .libc_a, | 1819 | .static => .libc_a, |
| 1800 | .dynamic => .libc_so, | 1820 | .dynamic => .libc_so, |
| 1801 | } }}); | 1821 | } }}); |
| 1822 | comp.remaining_prelink_tasks += 1; | ||
| 1802 | } else if (target.isGnuLibC()) { | 1823 | } else if (target.isGnuLibC()) { |
| 1803 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | 1824 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1804 | 1825 | ||
| ... | @@ -1807,14 +1828,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1807,14 +1828,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1807 | .{ .glibc_crt_file = .crti_o }, | 1828 | .{ .glibc_crt_file = .crti_o }, |
| 1808 | .{ .glibc_crt_file = .crtn_o }, | 1829 | .{ .glibc_crt_file = .crtn_o }, |
| 1809 | }); | 1830 | }); |
| 1831 | comp.remaining_prelink_tasks += 2; | ||
| 1810 | } | 1832 | } |
| 1811 | if (glibc.needsCrt0(comp.config.output_mode)) |f| { | 1833 | if (glibc.needsCrt0(comp.config.output_mode)) |f| { |
| 1812 | try comp.queueJobs(&.{.{ .glibc_crt_file = f }}); | 1834 | try comp.queueJobs(&.{.{ .glibc_crt_file = f }}); |
| 1835 | comp.remaining_prelink_tasks += 1; | ||
| 1813 | } | 1836 | } |
| 1814 | try comp.queueJobs(&[_]Job{ | 1837 | try comp.queueJobs(&[_]Job{ |
| 1815 | .{ .glibc_shared_objects = {} }, | 1838 | .{ .glibc_shared_objects = {} }, |
| 1816 | .{ .glibc_crt_file = .libc_nonshared_a }, | 1839 | .{ .glibc_crt_file = .libc_nonshared_a }, |
| 1817 | }); | 1840 | }); |
| 1841 | comp.remaining_prelink_tasks += 1; | ||
| 1842 | comp.remaining_prelink_tasks += glibc.sharedObjectsCount(&target); | ||
| 1818 | } else if (target.isWasm() and target.os.tag == .wasi) { | 1843 | } else if (target.isWasm() and target.os.tag == .wasi) { |
| 1819 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | 1844 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1820 | 1845 | ||
| ... | @@ -1822,11 +1847,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1822,11 +1847,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1822 | try comp.queueJob(.{ | 1847 | try comp.queueJob(.{ |
| 1823 | .wasi_libc_crt_file = crt_file, | 1848 | .wasi_libc_crt_file = crt_file, |
| 1824 | }); | 1849 | }); |
| 1850 | comp.remaining_prelink_tasks += 1; | ||
| 1825 | } | 1851 | } |
| 1826 | try comp.queueJobs(&[_]Job{ | 1852 | try comp.queueJobs(&[_]Job{ |
| 1827 | .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, | 1853 | .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, |
| 1828 | .{ .wasi_libc_crt_file = .libc_a }, | 1854 | .{ .wasi_libc_crt_file = .libc_a }, |
| 1829 | }); | 1855 | }); |
| 1856 | comp.remaining_prelink_tasks += 2; | ||
| 1830 | } else if (target.isMinGW()) { | 1857 | } else if (target.isMinGW()) { |
| 1831 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; | 1858 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 1832 | 1859 | ||
| ... | @@ -1835,6 +1862,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1835,6 +1862,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1835 | .{ .mingw_crt_file = .mingw32_lib }, | 1862 | .{ .mingw_crt_file = .mingw32_lib }, |
| 1836 | crt_job, | 1863 | crt_job, |
| 1837 | }); | 1864 | }); |
| 1865 | comp.remaining_prelink_tasks += 2; | ||
| 1838 | 1866 | ||
| 1839 | // When linking mingw-w64 there are some import libs we always need. | 1867 | // When linking mingw-w64 there are some import libs we always need. |
| 1840 | try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); | 1868 | try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); |
| ... | @@ -1846,6 +1874,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1846,6 +1874,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1846 | } | 1874 | } |
| 1847 | } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) { | 1875 | } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) { |
| 1848 | try comp.queueJob(.{ .zig_libc = {} }); | 1876 | try comp.queueJob(.{ .zig_libc = {} }); |
| 1877 | comp.remaining_prelink_tasks += 1; | ||
| 1849 | } else { | 1878 | } else { |
| 1850 | return error.LibCUnavailable; | 1879 | return error.LibCUnavailable; |
| 1851 | } | 1880 | } |
| ... | @@ -1860,13 +1889,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1860,13 +1889,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1860 | } | 1889 | } |
| 1861 | if (comp.wantBuildLibUnwindFromSource()) { | 1890 | if (comp.wantBuildLibUnwindFromSource()) { |
| 1862 | try comp.queueJob(.{ .libunwind = {} }); | 1891 | try comp.queueJob(.{ .libunwind = {} }); |
| 1892 | comp.remaining_prelink_tasks += 1; | ||
| 1863 | } | 1893 | } |
| 1864 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { | 1894 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { |
| 1865 | try comp.queueJob(.libcxx); | 1895 | try comp.queueJob(.libcxx); |
| 1866 | try comp.queueJob(.libcxxabi); | 1896 | try comp.queueJob(.libcxxabi); |
| 1897 | comp.remaining_prelink_tasks += 2; | ||
| 1867 | } | 1898 | } |
| 1868 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) { | 1899 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) { |
| 1869 | try comp.queueJob(.libtsan); | 1900 | try comp.queueJob(.libtsan); |
| 1901 | comp.remaining_prelink_tasks += 1; | ||
| 1870 | } | 1902 | } |
| 1871 | 1903 | ||
| 1872 | if (target.isMinGW() and comp.config.any_non_single_threaded) { | 1904 | if (target.isMinGW() and comp.config.any_non_single_threaded) { |
| ... | @@ -1885,22 +1917,27 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil | ... | @@ -1885,22 +1917,27 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1885 | if (is_exe_or_dyn_lib) { | 1917 | if (is_exe_or_dyn_lib) { |
| 1886 | log.debug("queuing a job to build compiler_rt_lib", .{}); | 1918 | log.debug("queuing a job to build compiler_rt_lib", .{}); |
| 1887 | comp.job_queued_compiler_rt_lib = true; | 1919 | comp.job_queued_compiler_rt_lib = true; |
| 1920 | comp.remaining_prelink_tasks += 1; | ||
| 1888 | } else if (output_mode != .Obj) { | 1921 | } else if (output_mode != .Obj) { |
| 1889 | log.debug("queuing a job to build compiler_rt_obj", .{}); | 1922 | log.debug("queuing a job to build compiler_rt_obj", .{}); |
| 1890 | // In this case we are making a static library, so we ask | 1923 | // In this case we are making a static library, so we ask |
| 1891 | // for a compiler-rt object to put in it. | 1924 | // for a compiler-rt object to put in it. |
| 1892 | comp.job_queued_compiler_rt_obj = true; | 1925 | comp.job_queued_compiler_rt_obj = true; |
| 1926 | comp.remaining_prelink_tasks += 1; | ||
| 1893 | } | 1927 | } |
| 1894 | } | 1928 | } |
| 1895 | 1929 | ||
| 1896 | if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) { | 1930 | if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) { |
| 1897 | log.debug("queuing a job to build libfuzzer", .{}); | 1931 | log.debug("queuing a job to build libfuzzer", .{}); |
| 1898 | comp.job_queued_fuzzer_lib = true; | 1932 | comp.job_queued_fuzzer_lib = true; |
| 1933 | comp.remaining_prelink_tasks += 1; | ||
| 1899 | } | 1934 | } |
| 1900 | } | 1935 | } |
| 1901 | 1936 | ||
| 1902 | try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided); | 1937 | try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided); |
| 1938 | comp.remaining_prelink_tasks += 1; | ||
| 1903 | } | 1939 | } |
| 1940 | log.debug("total prelink tasks: {d}", .{comp.remaining_prelink_tasks}); | ||
| 1904 | 1941 | ||
| 1905 | return comp; | 1942 | return comp; |
| 1906 | } | 1943 | } |
| ... | @@ -1976,6 +2013,7 @@ pub fn destroy(comp: *Compilation) void { | ... | @@ -1976,6 +2013,7 @@ pub fn destroy(comp: *Compilation) void { |
| 1976 | 2013 | ||
| 1977 | comp.link_diags.deinit(); | 2014 | comp.link_diags.deinit(); |
| 1978 | comp.link_task_queue.deinit(gpa); | 2015 | comp.link_task_queue.deinit(gpa); |
| 2016 | comp.link_task_queue_postponed.deinit(gpa); | ||
| 1979 | 2017 | ||
| 1980 | comp.clearMiscFailures(); | 2018 | comp.clearMiscFailures(); |
| 1981 | 2019 | ||
| ... | @@ -2438,9 +2476,8 @@ fn flush( | ... | @@ -2438,9 +2476,8 @@ fn flush( |
| 2438 | if (comp.bin_file) |lf| { | 2476 | if (comp.bin_file) |lf| { |
| 2439 | // This is needed before reading the error flags. | 2477 | // This is needed before reading the error flags. |
| 2440 | lf.flush(arena, tid, prog_node) catch |err| switch (err) { | 2478 | lf.flush(arena, tid, prog_node) catch |err| switch (err) { |
| 2441 | error.FlushFailure, error.LinkFailure => {}, // error reported through link_diags.flags | 2479 | error.LinkFailure => {}, // Already reported. |
| 2442 | error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr | 2480 | error.OutOfMemory => return error.OutOfMemory, |
| 2443 | else => |e| return e, | ||
| 2444 | }; | 2481 | }; |
| 2445 | } | 2482 | } |
| 2446 | 2483 | ||
| ... | @@ -3025,8 +3062,120 @@ pub fn saveState(comp: *Compilation) !void { | ... | @@ -3025,8 +3062,120 @@ pub fn saveState(comp: *Compilation) !void { |
| 3025 | //// TODO: compilation errors | 3062 | //// TODO: compilation errors |
| 3026 | //// TODO: namespaces | 3063 | //// TODO: namespaces |
| 3027 | //// TODO: decls | 3064 | //// TODO: decls |
| 3028 | //// TODO: linker state | ||
| 3029 | } | 3065 | } |
| 3066 | |||
| 3067 | // linker state | ||
| 3068 | switch (lf.tag) { | ||
| 3069 | .wasm => { | ||
| 3070 | const wasm = lf.cast(.wasm).?; | ||
| 3071 | const is_obj = comp.config.output_mode == .Obj; | ||
| 3072 | try bufs.ensureUnusedCapacity(85); | ||
| 3073 | addBuf(&bufs, wasm.string_bytes.items); | ||
| 3074 | // TODO make it well-defined memory layout | ||
| 3075 | //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items)); | ||
| 3076 | addBuf(&bufs, mem.sliceAsBytes(wasm.func_types.keys())); | ||
| 3077 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.keys())); | ||
| 3078 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.values())); | ||
| 3079 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_functions.items)); | ||
| 3080 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.keys())); | ||
| 3081 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.values())); | ||
| 3082 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_globals.items)); | ||
| 3083 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.keys())); | ||
| 3084 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.values())); | ||
| 3085 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_tables.items)); | ||
| 3086 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.keys())); | ||
| 3087 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.values())); | ||
| 3088 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_memories.items)); | ||
| 3089 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.tag))); | ||
| 3090 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.offset))); | ||
| 3091 | // TODO handle the union safety field | ||
| 3092 | //addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.pointee))); | ||
| 3093 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.addend))); | ||
| 3094 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_init_funcs.items)); | ||
| 3095 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_segments.items)); | ||
| 3096 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_datas.items)); | ||
| 3097 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.keys())); | ||
| 3098 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.values())); | ||
| 3099 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.keys())); | ||
| 3100 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.values())); | ||
| 3101 | // TODO make it well-defined memory layout | ||
| 3102 | // addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdats.items)); | ||
| 3103 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.keys())); | ||
| 3104 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.values())); | ||
| 3105 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.kind))); | ||
| 3106 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.index))); | ||
| 3107 | addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.tag))); | ||
| 3108 | addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.offset))); | ||
| 3109 | // TODO handle the union safety field | ||
| 3110 | //addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.pointee))); | ||
| 3111 | addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.addend))); | ||
| 3112 | addBuf(&bufs, mem.sliceAsBytes(wasm.uav_fixups.items)); | ||
| 3113 | addBuf(&bufs, mem.sliceAsBytes(wasm.nav_fixups.items)); | ||
| 3114 | addBuf(&bufs, mem.sliceAsBytes(wasm.func_table_fixups.items)); | ||
| 3115 | if (is_obj) { | ||
| 3116 | addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.keys())); | ||
| 3117 | addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.values())); | ||
| 3118 | addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.keys())); | ||
| 3119 | addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.values())); | ||
| 3120 | } else { | ||
| 3121 | addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.keys())); | ||
| 3122 | addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.values())); | ||
| 3123 | addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.keys())); | ||
| 3124 | addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.values())); | ||
| 3125 | } | ||
| 3126 | addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.keys())); | ||
| 3127 | addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.values())); | ||
| 3128 | addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.keys())); | ||
| 3129 | // TODO handle the union safety field | ||
| 3130 | // addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.values())); | ||
| 3131 | addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.keys())); | ||
| 3132 | addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.values())); | ||
| 3133 | addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.keys())); | ||
| 3134 | addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.values())); | ||
| 3135 | addBuf(&bufs, mem.sliceAsBytes(wasm.imports.keys())); | ||
| 3136 | addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys())); | ||
| 3137 | addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys())); | ||
| 3138 | addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values())); | ||
| 3139 | addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys())); | ||
| 3140 | addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values())); | ||
| 3141 | addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items)); | ||
| 3142 | addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys())); | ||
| 3143 | addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys())); | ||
| 3144 | addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.values())); | ||
| 3145 | addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.keys())); | ||
| 3146 | addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.values())); | ||
| 3147 | addBuf(&bufs, mem.sliceAsBytes(wasm.data_segments.keys())); | ||
| 3148 | addBuf(&bufs, mem.sliceAsBytes(wasm.globals.keys())); | ||
| 3149 | addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.keys())); | ||
| 3150 | addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.values())); | ||
| 3151 | addBuf(&bufs, mem.sliceAsBytes(wasm.tables.keys())); | ||
| 3152 | addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.keys())); | ||
| 3153 | addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.values())); | ||
| 3154 | addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_indirect_function_set.keys())); | ||
| 3155 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_import_set.keys())); | ||
| 3156 | addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_set.keys())); | ||
| 3157 | addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.tag))); | ||
| 3158 | // TODO handle the union safety field | ||
| 3159 | //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data))); | ||
| 3160 | addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items)); | ||
| 3161 | addBuf(&bufs, mem.sliceAsBytes(wasm.all_zcu_locals.items)); | ||
| 3162 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items)); | ||
| 3163 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items)); | ||
| 3164 | |||
| 3165 | // TODO add as header fields | ||
| 3166 | // entry_resolution: FunctionImport.Resolution | ||
| 3167 | // function_exports_len: u32 | ||
| 3168 | // global_exports_len: u32 | ||
| 3169 | // functions_end_prelink: u32 | ||
| 3170 | // globals_end_prelink: u32 | ||
| 3171 | // error_name_table_ref_count: u32 | ||
| 3172 | // tag_name_table_ref_count: u32 | ||
| 3173 | // any_tls_relocs: bool | ||
| 3174 | // any_passive_inits: bool | ||
| 3175 | }, | ||
| 3176 | else => log.err("TODO implement saving linker state for {s}", .{@tagName(lf.tag)}), | ||
| 3177 | } | ||
| 3178 | |||
| 3030 | var basename_buf: [255]u8 = undefined; | 3179 | var basename_buf: [255]u8 = undefined; |
| 3031 | const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{ | 3180 | const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{ |
| 3032 | comp.root_name, | 3181 | comp.root_name, |
| ... | @@ -3209,6 +3358,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3209,6 +3358,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3209 | if (!zcu.navFileScope(nav).okToReportErrors()) continue; | 3358 | if (!zcu.navFileScope(nav).okToReportErrors()) continue; |
| 3210 | try addModuleErrorMsg(zcu, &bundle, error_msg.*); | 3359 | try addModuleErrorMsg(zcu, &bundle, error_msg.*); |
| 3211 | } | 3360 | } |
| 3361 | for (zcu.failed_types.keys(), zcu.failed_types.values()) |ty_index, error_msg| { | ||
| 3362 | if (!zcu.typeFileScope(ty_index).okToReportErrors()) continue; | ||
| 3363 | try addModuleErrorMsg(zcu, &bundle, error_msg.*); | ||
| 3364 | } | ||
| 3212 | for (zcu.failed_exports.values()) |value| { | 3365 | for (zcu.failed_exports.values()) |value| { |
| 3213 | try addModuleErrorMsg(zcu, &bundle, value.*); | 3366 | try addModuleErrorMsg(zcu, &bundle, value.*); |
| 3214 | } | 3367 | } |
| ... | @@ -3252,7 +3405,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3252,7 +3405,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3252 | })); | 3405 | })); |
| 3253 | } | 3406 | } |
| 3254 | 3407 | ||
| 3255 | try comp.link_diags.addMessagesToBundle(&bundle); | 3408 | try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file); |
| 3256 | 3409 | ||
| 3257 | if (comp.zcu) |zcu| { | 3410 | if (comp.zcu) |zcu| { |
| 3258 | if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { | 3411 | if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { |
| ... | @@ -3524,9 +3677,9 @@ pub fn performAllTheWork( | ... | @@ -3524,9 +3677,9 @@ pub fn performAllTheWork( |
| 3524 | 3677 | ||
| 3525 | defer if (comp.zcu) |zcu| { | 3678 | defer if (comp.zcu) |zcu| { |
| 3526 | zcu.sema_prog_node.end(); | 3679 | zcu.sema_prog_node.end(); |
| 3527 | zcu.sema_prog_node = std.Progress.Node.none; | 3680 | zcu.sema_prog_node = .none; |
| 3528 | zcu.codegen_prog_node.end(); | 3681 | zcu.codegen_prog_node.end(); |
| 3529 | zcu.codegen_prog_node = std.Progress.Node.none; | 3682 | zcu.codegen_prog_node = .none; |
| 3530 | 3683 | ||
| 3531 | zcu.generation += 1; | 3684 | zcu.generation += 1; |
| 3532 | }; | 3685 | }; |
| ... | @@ -3659,7 +3812,7 @@ fn performAllTheWorkInner( | ... | @@ -3659,7 +3812,7 @@ fn performAllTheWorkInner( |
| 3659 | try zcu.flushRetryableFailures(); | 3812 | try zcu.flushRetryableFailures(); |
| 3660 | 3813 | ||
| 3661 | zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); | 3814 | zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); |
| 3662 | zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); | 3815 | zcu.codegen_prog_node = if (comp.bin_file != null) main_progress_node.start("Code Generation", 0) else .none; |
| 3663 | } | 3816 | } |
| 3664 | 3817 | ||
| 3665 | if (!comp.separateCodegenThreadOk()) { | 3818 | if (!comp.separateCodegenThreadOk()) { |
| ... | @@ -3689,6 +3842,8 @@ fn performAllTheWorkInner( | ... | @@ -3689,6 +3842,8 @@ fn performAllTheWorkInner( |
| 3689 | }); | 3842 | }); |
| 3690 | continue; | 3843 | continue; |
| 3691 | } | 3844 | } |
| 3845 | zcu.sema_prog_node.end(); | ||
| 3846 | zcu.sema_prog_node = .none; | ||
| 3692 | } | 3847 | } |
| 3693 | break; | 3848 | break; |
| 3694 | } | 3849 | } |
| ... | @@ -3962,6 +4117,7 @@ fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) voi | ... | @@ -3962,6 +4117,7 @@ fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) voi |
| 3962 | if (comp.separateCodegenThreadOk()) { | 4117 | if (comp.separateCodegenThreadOk()) { |
| 3963 | comp.queueLinkTasks(&.{link_task}); | 4118 | comp.queueLinkTasks(&.{link_task}); |
| 3964 | } else { | 4119 | } else { |
| 4120 | assert(comp.remaining_prelink_tasks == 0); | ||
| 3965 | link.doTask(comp, tid, link_task); | 4121 | link.doTask(comp, tid, link_task); |
| 3966 | } | 4122 | } |
| 3967 | } | 4123 | } |
src/InternPool.zig+41-1| ... | @@ -552,6 +552,15 @@ pub const Nav = struct { | ... | @@ -552,6 +552,15 @@ pub const Nav = struct { |
| 552 | }; | 552 | }; |
| 553 | } | 553 | } |
| 554 | 554 | ||
| 555 | /// This function is intended to be used by code generation, since semantic | ||
| 556 | /// analysis will ensure that any `Nav` which is potentially `extern` is | ||
| 557 | /// fully resolved. | ||
| 558 | /// Asserts that `status == .fully_resolved`. | ||
| 559 | pub fn getResolvedExtern(nav: Nav, ip: *const InternPool) ?Key.Extern { | ||
| 560 | assert(nav.status == .fully_resolved); | ||
| 561 | return nav.getExtern(ip); | ||
| 562 | } | ||
| 563 | |||
| 555 | /// Always returns `null` for `status == .type_resolved`. This function is inteded | 564 | /// Always returns `null` for `status == .type_resolved`. This function is inteded |
| 556 | /// to be used by code generation, since semantic analysis will ensure that any `Nav` | 565 | /// to be used by code generation, since semantic analysis will ensure that any `Nav` |
| 557 | /// which is potentially `extern` is fully resolved. | 566 | /// which is potentially `extern` is fully resolved. |
| ... | @@ -585,6 +594,15 @@ pub const Nav = struct { | ... | @@ -585,6 +594,15 @@ pub const Nav = struct { |
| 585 | }; | 594 | }; |
| 586 | } | 595 | } |
| 587 | 596 | ||
| 597 | /// Asserts that `status != .unresolved`. | ||
| 598 | pub fn getLinkSection(nav: Nav) OptionalNullTerminatedString { | ||
| 599 | return switch (nav.status) { | ||
| 600 | .unresolved => unreachable, | ||
| 601 | .type_resolved => |r| r.@"linksection", | ||
| 602 | .fully_resolved => |r| r.@"linksection", | ||
| 603 | }; | ||
| 604 | } | ||
| 605 | |||
| 588 | /// Asserts that `status != .unresolved`. | 606 | /// Asserts that `status != .unresolved`. |
| 589 | pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool { | 607 | pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool { |
| 590 | return switch (nav.status) { | 608 | return switch (nav.status) { |
| ... | @@ -598,6 +616,20 @@ pub const Nav = struct { | ... | @@ -598,6 +616,20 @@ pub const Nav = struct { |
| 598 | }; | 616 | }; |
| 599 | } | 617 | } |
| 600 | 618 | ||
| 619 | pub fn isFn(nav: Nav, ip: *const InternPool) bool { | ||
| 620 | return switch (nav.status) { | ||
| 621 | .unresolved => unreachable, | ||
| 622 | .type_resolved => |r| { | ||
| 623 | const tag = ip.zigTypeTagOrPoison(r.type) catch unreachable; | ||
| 624 | return tag == .@"fn"; | ||
| 625 | }, | ||
| 626 | .fully_resolved => |r| { | ||
| 627 | const tag = ip.zigTypeTagOrPoison(ip.typeOf(r.val)) catch unreachable; | ||
| 628 | return tag == .@"fn"; | ||
| 629 | }, | ||
| 630 | }; | ||
| 631 | } | ||
| 632 | |||
| 601 | /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer | 633 | /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer |
| 602 | /// to some other `Nav` due to an extern definition or extern alias (see #21027). | 634 | /// to some other `Nav` due to an extern definition or extern alias (see #21027). |
| 603 | /// This query is valid on `Nav`s for whom only the type is resolved. | 635 | /// This query is valid on `Nav`s for whom only the type is resolved. |
| ... | @@ -3360,6 +3392,10 @@ pub const LoadedUnionType = struct { | ... | @@ -3360,6 +3392,10 @@ pub const LoadedUnionType = struct { |
| 3360 | return flags.status == .field_types_wip; | 3392 | return flags.status == .field_types_wip; |
| 3361 | } | 3393 | } |
| 3362 | 3394 | ||
| 3395 | pub fn requiresComptime(u: LoadedUnionType, ip: *const InternPool) RequiresComptime { | ||
| 3396 | return u.flagsUnordered(ip).requires_comptime; | ||
| 3397 | } | ||
| 3398 | |||
| 3363 | pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime { | 3399 | pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime { |
| 3364 | const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; | 3400 | const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; |
| 3365 | extra_mutex.lock(); | 3401 | extra_mutex.lock(); |
| ... | @@ -4014,7 +4050,7 @@ pub const LoadedStructType = struct { | ... | @@ -4014,7 +4050,7 @@ pub const LoadedStructType = struct { |
| 4014 | } | 4050 | } |
| 4015 | } | 4051 | } |
| 4016 | 4052 | ||
| 4017 | pub fn haveLayout(s: LoadedStructType, ip: *InternPool) bool { | 4053 | pub fn haveLayout(s: LoadedStructType, ip: *const InternPool) bool { |
| 4018 | return switch (s.layout) { | 4054 | return switch (s.layout) { |
| 4019 | .@"packed" => s.backingIntTypeUnordered(ip) != .none, | 4055 | .@"packed" => s.backingIntTypeUnordered(ip) != .none, |
| 4020 | .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved, | 4056 | .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved, |
| ... | @@ -11797,6 +11833,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E { | ... | @@ -11797,6 +11833,10 @@ pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E { |
| 11797 | return @enumFromInt(ip.indexToKey(int).int.storage.u64); | 11833 | return @enumFromInt(ip.indexToKey(int).int.storage.u64); |
| 11798 | } | 11834 | } |
| 11799 | 11835 | ||
| 11836 | pub fn toFunc(ip: *const InternPool, i: Index) Key.Func { | ||
| 11837 | return ip.indexToKey(i).func; | ||
| 11838 | } | ||
| 11839 | |||
| 11800 | pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 { | 11840 | pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 { |
| 11801 | return switch (ip.indexToKey(ty)) { | 11841 | return switch (ip.indexToKey(ty)) { |
| 11802 | .struct_type => ip.loadStructType(ty).field_types.len, | 11842 | .struct_type => ip.loadStructType(ty).field_types.len, |
src/Sema.zig+5-5| ... | @@ -38298,7 +38298,7 @@ pub fn flushExports(sema: *Sema) !void { | ... | @@ -38298,7 +38298,7 @@ pub fn flushExports(sema: *Sema) !void { |
| 38298 | // So, pick up and delete any existing exports. This strategy performs | 38298 | // So, pick up and delete any existing exports. This strategy performs |
| 38299 | // redundant work, but that's okay, because this case is exceedingly rare. | 38299 | // redundant work, but that's okay, because this case is exceedingly rare. |
| 38300 | if (zcu.single_exports.get(sema.owner)) |export_idx| { | 38300 | if (zcu.single_exports.get(sema.owner)) |export_idx| { |
| 38301 | try sema.exports.append(gpa, zcu.all_exports.items[export_idx]); | 38301 | try sema.exports.append(gpa, export_idx.ptr(zcu).*); |
| 38302 | } else if (zcu.multi_exports.get(sema.owner)) |info| { | 38302 | } else if (zcu.multi_exports.get(sema.owner)) |info| { |
| 38303 | try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); | 38303 | try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); |
| 38304 | } | 38304 | } |
| ... | @@ -38307,12 +38307,12 @@ pub fn flushExports(sema: *Sema) !void { | ... | @@ -38307,12 +38307,12 @@ pub fn flushExports(sema: *Sema) !void { |
| 38307 | // `sema.exports` is completed; store the data into the `Zcu`. | 38307 | // `sema.exports` is completed; store the data into the `Zcu`. |
| 38308 | if (sema.exports.items.len == 1) { | 38308 | if (sema.exports.items.len == 1) { |
| 38309 | try zcu.single_exports.ensureUnusedCapacity(gpa, 1); | 38309 | try zcu.single_exports.ensureUnusedCapacity(gpa, 1); |
| 38310 | const export_idx = zcu.free_exports.popOrNull() orelse idx: { | 38310 | const export_idx: Zcu.Export.Index = zcu.free_exports.popOrNull() orelse idx: { |
| 38311 | _ = try zcu.all_exports.addOne(gpa); | 38311 | _ = try zcu.all_exports.addOne(gpa); |
| 38312 | break :idx zcu.all_exports.items.len - 1; | 38312 | break :idx @enumFromInt(zcu.all_exports.items.len - 1); |
| 38313 | }; | 38313 | }; |
| 38314 | zcu.all_exports.items[export_idx] = sema.exports.items[0]; | 38314 | export_idx.ptr(zcu).* = sema.exports.items[0]; |
| 38315 | zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, @intCast(export_idx)); | 38315 | zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx); |
| 38316 | } else { | 38316 | } else { |
| 38317 | try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); | 38317 | try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); |
| 38318 | const exports_base = zcu.all_exports.items.len; | 38318 | const exports_base = zcu.all_exports.items.len; |
src/Type.zig+108-85| ... | @@ -441,7 +441,7 @@ pub fn toValue(self: Type) Value { | ... | @@ -441,7 +441,7 @@ pub fn toValue(self: Type) Value { |
| 441 | 441 | ||
| 442 | const RuntimeBitsError = SemaError || error{NeedLazy}; | 442 | const RuntimeBitsError = SemaError || error{NeedLazy}; |
| 443 | 443 | ||
| 444 | pub fn hasRuntimeBits(ty: Type, zcu: *Zcu) bool { | 444 | pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool { |
| 445 | return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable; | 445 | return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable; |
| 446 | } | 446 | } |
| 447 | 447 | ||
| ... | @@ -452,7 +452,7 @@ pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { | ... | @@ -452,7 +452,7 @@ pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { |
| 452 | }; | 452 | }; |
| 453 | } | 453 | } |
| 454 | 454 | ||
| 455 | pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *Zcu) bool { | 455 | pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool { |
| 456 | return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; | 456 | return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; |
| 457 | } | 457 | } |
| 458 | 458 | ||
| ... | @@ -471,7 +471,7 @@ pub fn hasRuntimeBitsInner( | ... | @@ -471,7 +471,7 @@ pub fn hasRuntimeBitsInner( |
| 471 | ty: Type, | 471 | ty: Type, |
| 472 | ignore_comptime_only: bool, | 472 | ignore_comptime_only: bool, |
| 473 | comptime strat: ResolveStratLazy, | 473 | comptime strat: ResolveStratLazy, |
| 474 | zcu: *Zcu, | 474 | zcu: strat.ZcuPtr(), |
| 475 | tid: strat.Tid(), | 475 | tid: strat.Tid(), |
| 476 | ) RuntimeBitsError!bool { | 476 | ) RuntimeBitsError!bool { |
| 477 | const ip = &zcu.intern_pool; | 477 | const ip = &zcu.intern_pool; |
| ... | @@ -560,7 +560,7 @@ pub fn hasRuntimeBitsInner( | ... | @@ -560,7 +560,7 @@ pub fn hasRuntimeBitsInner( |
| 560 | }, | 560 | }, |
| 561 | .struct_type => { | 561 | .struct_type => { |
| 562 | const struct_type = ip.loadStructType(ty.toIntern()); | 562 | const struct_type = ip.loadStructType(ty.toIntern()); |
| 563 | if (struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) { | 563 | if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) { |
| 564 | // In this case, we guess that hasRuntimeBits() for this type is true, | 564 | // In this case, we guess that hasRuntimeBits() for this type is true, |
| 565 | // and then later if our guess was incorrect, we emit a compile error. | 565 | // and then later if our guess was incorrect, we emit a compile error. |
| 566 | return true; | 566 | return true; |
| ... | @@ -596,7 +596,7 @@ pub fn hasRuntimeBitsInner( | ... | @@ -596,7 +596,7 @@ pub fn hasRuntimeBitsInner( |
| 596 | const union_type = ip.loadUnionType(ty.toIntern()); | 596 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 597 | const union_flags = union_type.flagsUnordered(ip); | 597 | const union_flags = union_type.flagsUnordered(ip); |
| 598 | switch (union_flags.runtime_tag) { | 598 | switch (union_flags.runtime_tag) { |
| 599 | .none => { | 599 | .none => if (strat != .eager) { |
| 600 | // In this case, we guess that hasRuntimeBits() for this type is true, | 600 | // In this case, we guess that hasRuntimeBits() for this type is true, |
| 601 | // and then later if our guess was incorrect, we emit a compile error. | 601 | // and then later if our guess was incorrect, we emit a compile error. |
| 602 | if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true; | 602 | if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true; |
| ... | @@ -774,7 +774,7 @@ pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { | ... | @@ -774,7 +774,7 @@ pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { |
| 774 | pub fn fnHasRuntimeBitsInner( | 774 | pub fn fnHasRuntimeBitsInner( |
| 775 | ty: Type, | 775 | ty: Type, |
| 776 | comptime strat: ResolveStrat, | 776 | comptime strat: ResolveStrat, |
| 777 | zcu: *Zcu, | 777 | zcu: strat.ZcuPtr(), |
| 778 | tid: strat.Tid(), | 778 | tid: strat.Tid(), |
| 779 | ) SemaError!bool { | 779 | ) SemaError!bool { |
| 780 | const fn_info = zcu.typeToFunc(ty).?; | 780 | const fn_info = zcu.typeToFunc(ty).?; |
| ... | @@ -815,7 +815,7 @@ pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { | ... | @@ -815,7 +815,7 @@ pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { |
| 815 | pub fn ptrAlignmentInner( | 815 | pub fn ptrAlignmentInner( |
| 816 | ty: Type, | 816 | ty: Type, |
| 817 | comptime strat: ResolveStrat, | 817 | comptime strat: ResolveStrat, |
| 818 | zcu: *Zcu, | 818 | zcu: strat.ZcuPtr(), |
| 819 | tid: strat.Tid(), | 819 | tid: strat.Tid(), |
| 820 | ) !Alignment { | 820 | ) !Alignment { |
| 821 | return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { | 821 | return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { |
| ... | @@ -868,14 +868,25 @@ pub const ResolveStratLazy = enum { | ... | @@ -868,14 +868,25 @@ pub const ResolveStratLazy = enum { |
| 868 | /// This should typically be used from semantic analysis. | 868 | /// This should typically be used from semantic analysis. |
| 869 | sema, | 869 | sema, |
| 870 | 870 | ||
| 871 | pub fn Tid(comptime strat: ResolveStratLazy) type { | 871 | pub fn Tid(strat: ResolveStratLazy) type { |
| 872 | return switch (strat) { | 872 | return switch (strat) { |
| 873 | .lazy, .sema => Zcu.PerThread.Id, | 873 | .lazy, .sema => Zcu.PerThread.Id, |
| 874 | .eager => void, | 874 | .eager => void, |
| 875 | }; | 875 | }; |
| 876 | } | 876 | } |
| 877 | 877 | ||
| 878 | pub fn pt(comptime strat: ResolveStratLazy, zcu: *Zcu, tid: strat.Tid()) switch (strat) { | 878 | pub fn ZcuPtr(strat: ResolveStratLazy) type { |
| 879 | return switch (strat) { | ||
| 880 | .eager => *const Zcu, | ||
| 881 | .sema, .lazy => *Zcu, | ||
| 882 | }; | ||
| 883 | } | ||
| 884 | |||
| 885 | pub fn pt( | ||
| 886 | comptime strat: ResolveStratLazy, | ||
| 887 | zcu: strat.ZcuPtr(), | ||
| 888 | tid: strat.Tid(), | ||
| 889 | ) switch (strat) { | ||
| 879 | .lazy, .sema => Zcu.PerThread, | 890 | .lazy, .sema => Zcu.PerThread, |
| 880 | .eager => void, | 891 | .eager => void, |
| 881 | } { | 892 | } { |
| ... | @@ -896,14 +907,21 @@ pub const ResolveStrat = enum { | ... | @@ -896,14 +907,21 @@ pub const ResolveStrat = enum { |
| 896 | /// This should typically be used from semantic analysis. | 907 | /// This should typically be used from semantic analysis. |
| 897 | sema, | 908 | sema, |
| 898 | 909 | ||
| 899 | pub fn Tid(comptime strat: ResolveStrat) type { | 910 | pub fn Tid(strat: ResolveStrat) type { |
| 900 | return switch (strat) { | 911 | return switch (strat) { |
| 901 | .sema => Zcu.PerThread.Id, | 912 | .sema => Zcu.PerThread.Id, |
| 902 | .normal => void, | 913 | .normal => void, |
| 903 | }; | 914 | }; |
| 904 | } | 915 | } |
| 905 | 916 | ||
| 906 | pub fn pt(comptime strat: ResolveStrat, zcu: *Zcu, tid: strat.Tid()) switch (strat) { | 917 | pub fn ZcuPtr(strat: ResolveStrat) type { |
| 918 | return switch (strat) { | ||
| 919 | .normal => *const Zcu, | ||
| 920 | .sema => *Zcu, | ||
| 921 | }; | ||
| 922 | } | ||
| 923 | |||
| 924 | pub fn pt(comptime strat: ResolveStrat, zcu: strat.ZcuPtr(), tid: strat.Tid()) switch (strat) { | ||
| 907 | .sema => Zcu.PerThread, | 925 | .sema => Zcu.PerThread, |
| 908 | .normal => void, | 926 | .normal => void, |
| 909 | } { | 927 | } { |
| ... | @@ -922,7 +940,7 @@ pub const ResolveStrat = enum { | ... | @@ -922,7 +940,7 @@ pub const ResolveStrat = enum { |
| 922 | }; | 940 | }; |
| 923 | 941 | ||
| 924 | /// Never returns `none`. Asserts that all necessary type resolution is already done. | 942 | /// Never returns `none`. Asserts that all necessary type resolution is already done. |
| 925 | pub fn abiAlignment(ty: Type, zcu: *Zcu) Alignment { | 943 | pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { |
| 926 | return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar; | 944 | return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar; |
| 927 | } | 945 | } |
| 928 | 946 | ||
| ... | @@ -939,7 +957,7 @@ pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { | ... | @@ -939,7 +957,7 @@ pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { |
| 939 | pub fn abiAlignmentInner( | 957 | pub fn abiAlignmentInner( |
| 940 | ty: Type, | 958 | ty: Type, |
| 941 | comptime strat: ResolveStratLazy, | 959 | comptime strat: ResolveStratLazy, |
| 942 | zcu: *Zcu, | 960 | zcu: strat.ZcuPtr(), |
| 943 | tid: strat.Tid(), | 961 | tid: strat.Tid(), |
| 944 | ) SemaError!AbiAlignmentInner { | 962 | ) SemaError!AbiAlignmentInner { |
| 945 | const pt = strat.pt(zcu, tid); | 963 | const pt = strat.pt(zcu, tid); |
| ... | @@ -1156,7 +1174,7 @@ pub fn abiAlignmentInner( | ... | @@ -1156,7 +1174,7 @@ pub fn abiAlignmentInner( |
| 1156 | fn abiAlignmentInnerErrorUnion( | 1174 | fn abiAlignmentInnerErrorUnion( |
| 1157 | ty: Type, | 1175 | ty: Type, |
| 1158 | comptime strat: ResolveStratLazy, | 1176 | comptime strat: ResolveStratLazy, |
| 1159 | zcu: *Zcu, | 1177 | zcu: strat.ZcuPtr(), |
| 1160 | tid: strat.Tid(), | 1178 | tid: strat.Tid(), |
| 1161 | payload_ty: Type, | 1179 | payload_ty: Type, |
| 1162 | ) SemaError!AbiAlignmentInner { | 1180 | ) SemaError!AbiAlignmentInner { |
| ... | @@ -1198,7 +1216,7 @@ fn abiAlignmentInnerErrorUnion( | ... | @@ -1198,7 +1216,7 @@ fn abiAlignmentInnerErrorUnion( |
| 1198 | fn abiAlignmentInnerOptional( | 1216 | fn abiAlignmentInnerOptional( |
| 1199 | ty: Type, | 1217 | ty: Type, |
| 1200 | comptime strat: ResolveStratLazy, | 1218 | comptime strat: ResolveStratLazy, |
| 1201 | zcu: *Zcu, | 1219 | zcu: strat.ZcuPtr(), |
| 1202 | tid: strat.Tid(), | 1220 | tid: strat.Tid(), |
| 1203 | ) SemaError!AbiAlignmentInner { | 1221 | ) SemaError!AbiAlignmentInner { |
| 1204 | const pt = strat.pt(zcu, tid); | 1222 | const pt = strat.pt(zcu, tid); |
| ... | @@ -1244,7 +1262,7 @@ const AbiSizeInner = union(enum) { | ... | @@ -1244,7 +1262,7 @@ const AbiSizeInner = union(enum) { |
| 1244 | 1262 | ||
| 1245 | /// Asserts the type has the ABI size already resolved. | 1263 | /// Asserts the type has the ABI size already resolved. |
| 1246 | /// Types that return false for hasRuntimeBits() return 0. | 1264 | /// Types that return false for hasRuntimeBits() return 0. |
| 1247 | pub fn abiSize(ty: Type, zcu: *Zcu) u64 { | 1265 | pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { |
| 1248 | return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar; | 1266 | return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar; |
| 1249 | } | 1267 | } |
| 1250 | 1268 | ||
| ... | @@ -1269,7 +1287,7 @@ pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 { | ... | @@ -1269,7 +1287,7 @@ pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 { |
| 1269 | pub fn abiSizeInner( | 1287 | pub fn abiSizeInner( |
| 1270 | ty: Type, | 1288 | ty: Type, |
| 1271 | comptime strat: ResolveStratLazy, | 1289 | comptime strat: ResolveStratLazy, |
| 1272 | zcu: *Zcu, | 1290 | zcu: strat.ZcuPtr(), |
| 1273 | tid: strat.Tid(), | 1291 | tid: strat.Tid(), |
| 1274 | ) SemaError!AbiSizeInner { | 1292 | ) SemaError!AbiSizeInner { |
| 1275 | const target = zcu.getTarget(); | 1293 | const target = zcu.getTarget(); |
| ... | @@ -1542,7 +1560,7 @@ pub fn abiSizeInner( | ... | @@ -1542,7 +1560,7 @@ pub fn abiSizeInner( |
| 1542 | fn abiSizeInnerOptional( | 1560 | fn abiSizeInnerOptional( |
| 1543 | ty: Type, | 1561 | ty: Type, |
| 1544 | comptime strat: ResolveStratLazy, | 1562 | comptime strat: ResolveStratLazy, |
| 1545 | zcu: *Zcu, | 1563 | zcu: strat.ZcuPtr(), |
| 1546 | tid: strat.Tid(), | 1564 | tid: strat.Tid(), |
| 1547 | ) SemaError!AbiSizeInner { | 1565 | ) SemaError!AbiSizeInner { |
| 1548 | const child_ty = ty.optionalChild(zcu); | 1566 | const child_ty = ty.optionalChild(zcu); |
| ... | @@ -1701,7 +1719,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 { | ... | @@ -1701,7 +1719,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 { |
| 1701 | }; | 1719 | }; |
| 1702 | } | 1720 | } |
| 1703 | 1721 | ||
| 1704 | pub fn bitSize(ty: Type, zcu: *Zcu) u64 { | 1722 | pub fn bitSize(ty: Type, zcu: *const Zcu) u64 { |
| 1705 | return bitSizeInner(ty, .normal, zcu, {}) catch unreachable; | 1723 | return bitSizeInner(ty, .normal, zcu, {}) catch unreachable; |
| 1706 | } | 1724 | } |
| 1707 | 1725 | ||
| ... | @@ -1712,7 +1730,7 @@ pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 { | ... | @@ -1712,7 +1730,7 @@ pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 { |
| 1712 | pub fn bitSizeInner( | 1730 | pub fn bitSizeInner( |
| 1713 | ty: Type, | 1731 | ty: Type, |
| 1714 | comptime strat: ResolveStrat, | 1732 | comptime strat: ResolveStrat, |
| 1715 | zcu: *Zcu, | 1733 | zcu: strat.ZcuPtr(), |
| 1716 | tid: strat.Tid(), | 1734 | tid: strat.Tid(), |
| 1717 | ) SemaError!u64 { | 1735 | ) SemaError!u64 { |
| 1718 | const target = zcu.getTarget(); | 1736 | const target = zcu.getTarget(); |
| ... | @@ -2148,7 +2166,7 @@ pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type { | ... | @@ -2148,7 +2166,7 @@ pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type { |
| 2148 | }; | 2166 | }; |
| 2149 | } | 2167 | } |
| 2150 | 2168 | ||
| 2151 | pub fn unionGetLayout(ty: Type, zcu: *Zcu) Zcu.UnionLayout { | 2169 | pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout { |
| 2152 | const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); | 2170 | const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); |
| 2153 | return Type.getUnionLayout(union_obj, zcu); | 2171 | return Type.getUnionLayout(union_obj, zcu); |
| 2154 | } | 2172 | } |
| ... | @@ -2746,7 +2764,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value { | ... | @@ -2746,7 +2764,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value { |
| 2746 | 2764 | ||
| 2747 | /// During semantic analysis, instead call `ty.comptimeOnlySema` which | 2765 | /// During semantic analysis, instead call `ty.comptimeOnlySema` which |
| 2748 | /// resolves field types rather than asserting they are already resolved. | 2766 | /// resolves field types rather than asserting they are already resolved. |
| 2749 | pub fn comptimeOnly(ty: Type, zcu: *Zcu) bool { | 2767 | pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool { |
| 2750 | return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable; | 2768 | return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable; |
| 2751 | } | 2769 | } |
| 2752 | 2770 | ||
| ... | @@ -2759,7 +2777,7 @@ pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool { | ... | @@ -2759,7 +2777,7 @@ pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool { |
| 2759 | pub fn comptimeOnlyInner( | 2777 | pub fn comptimeOnlyInner( |
| 2760 | ty: Type, | 2778 | ty: Type, |
| 2761 | comptime strat: ResolveStrat, | 2779 | comptime strat: ResolveStrat, |
| 2762 | zcu: *Zcu, | 2780 | zcu: strat.ZcuPtr(), |
| 2763 | tid: strat.Tid(), | 2781 | tid: strat.Tid(), |
| 2764 | ) SemaError!bool { | 2782 | ) SemaError!bool { |
| 2765 | const ip = &zcu.intern_pool; | 2783 | const ip = &zcu.intern_pool; |
| ... | @@ -2834,40 +2852,44 @@ pub fn comptimeOnlyInner( | ... | @@ -2834,40 +2852,44 @@ pub fn comptimeOnlyInner( |
| 2834 | if (struct_type.layout == .@"packed") | 2852 | if (struct_type.layout == .@"packed") |
| 2835 | return false; | 2853 | return false; |
| 2836 | 2854 | ||
| 2837 | // A struct with no fields is not comptime-only. | 2855 | return switch (strat) { |
| 2838 | return switch (struct_type.setRequiresComptimeWip(ip)) { | 2856 | .normal => switch (struct_type.requiresComptime(ip)) { |
| 2839 | .no, .wip => false, | 2857 | .wip => unreachable, |
| 2840 | .yes => true, | 2858 | .no => false, |
| 2841 | .unknown => { | 2859 | .yes => true, |
| 2842 | // Inlined `assert` so that the resolution calls below are not statically reachable. | 2860 | .unknown => unreachable, |
| 2843 | if (strat != .sema) unreachable; | 2861 | }, |
| 2844 | 2862 | .sema => switch (struct_type.setRequiresComptimeWip(ip)) { | |
| 2845 | if (struct_type.flagsUnordered(ip).field_types_wip) { | 2863 | .no, .wip => false, |
| 2846 | struct_type.setRequiresComptime(ip, .unknown); | 2864 | .yes => true, |
| 2847 | return false; | 2865 | .unknown => { |
| 2848 | } | 2866 | if (struct_type.flagsUnordered(ip).field_types_wip) { |
| 2867 | struct_type.setRequiresComptime(ip, .unknown); | ||
| 2868 | return false; | ||
| 2869 | } | ||
| 2849 | 2870 | ||
| 2850 | errdefer struct_type.setRequiresComptime(ip, .unknown); | 2871 | errdefer struct_type.setRequiresComptime(ip, .unknown); |
| 2851 | 2872 | ||
| 2852 | const pt = strat.pt(zcu, tid); | 2873 | const pt = strat.pt(zcu, tid); |
| 2853 | try ty.resolveFields(pt); | 2874 | try ty.resolveFields(pt); |
| 2854 | 2875 | ||
| 2855 | for (0..struct_type.field_types.len) |i_usize| { | 2876 | for (0..struct_type.field_types.len) |i_usize| { |
| 2856 | const i: u32 = @intCast(i_usize); | 2877 | const i: u32 = @intCast(i_usize); |
| 2857 | if (struct_type.fieldIsComptime(ip, i)) continue; | 2878 | if (struct_type.fieldIsComptime(ip, i)) continue; |
| 2858 | const field_ty = struct_type.field_types.get(ip)[i]; | 2879 | const field_ty = struct_type.field_types.get(ip)[i]; |
| 2859 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { | 2880 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { |
| 2860 | // Note that this does not cause the layout to | 2881 | // Note that this does not cause the layout to |
| 2861 | // be considered resolved. Comptime-only types | 2882 | // be considered resolved. Comptime-only types |
| 2862 | // still maintain a layout of their | 2883 | // still maintain a layout of their |
| 2863 | // runtime-known fields. | 2884 | // runtime-known fields. |
| 2864 | struct_type.setRequiresComptime(ip, .yes); | 2885 | struct_type.setRequiresComptime(ip, .yes); |
| 2865 | return true; | 2886 | return true; |
| 2887 | } | ||
| 2866 | } | 2888 | } |
| 2867 | } | ||
| 2868 | 2889 | ||
| 2869 | struct_type.setRequiresComptime(ip, .no); | 2890 | struct_type.setRequiresComptime(ip, .no); |
| 2870 | return false; | 2891 | return false; |
| 2892 | }, | ||
| 2871 | }, | 2893 | }, |
| 2872 | }; | 2894 | }; |
| 2873 | }, | 2895 | }, |
| ... | @@ -2882,35 +2904,40 @@ pub fn comptimeOnlyInner( | ... | @@ -2882,35 +2904,40 @@ pub fn comptimeOnlyInner( |
| 2882 | 2904 | ||
| 2883 | .union_type => { | 2905 | .union_type => { |
| 2884 | const union_type = ip.loadUnionType(ty.toIntern()); | 2906 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 2885 | switch (union_type.setRequiresComptimeWip(ip)) { | 2907 | return switch (strat) { |
| 2886 | .no, .wip => return false, | 2908 | .normal => switch (union_type.requiresComptime(ip)) { |
| 2887 | .yes => return true, | 2909 | .wip => unreachable, |
| 2888 | .unknown => { | 2910 | .no => false, |
| 2889 | // Inlined `assert` so that the resolution calls below are not statically reachable. | 2911 | .yes => true, |
| 2890 | if (strat != .sema) unreachable; | 2912 | .unknown => unreachable, |
| 2891 | 2913 | }, | |
| 2892 | if (union_type.flagsUnordered(ip).status == .field_types_wip) { | 2914 | .sema => switch (union_type.setRequiresComptimeWip(ip)) { |
| 2893 | union_type.setRequiresComptime(ip, .unknown); | 2915 | .no, .wip => return false, |
| 2894 | return false; | 2916 | .yes => return true, |
| 2895 | } | 2917 | .unknown => { |
| 2918 | if (union_type.flagsUnordered(ip).status == .field_types_wip) { | ||
| 2919 | union_type.setRequiresComptime(ip, .unknown); | ||
| 2920 | return false; | ||
| 2921 | } | ||
| 2896 | 2922 | ||
| 2897 | errdefer union_type.setRequiresComptime(ip, .unknown); | 2923 | errdefer union_type.setRequiresComptime(ip, .unknown); |
| 2898 | 2924 | ||
| 2899 | const pt = strat.pt(zcu, tid); | 2925 | const pt = strat.pt(zcu, tid); |
| 2900 | try ty.resolveFields(pt); | 2926 | try ty.resolveFields(pt); |
| 2901 | 2927 | ||
| 2902 | for (0..union_type.field_types.len) |field_idx| { | 2928 | for (0..union_type.field_types.len) |field_idx| { |
| 2903 | const field_ty = union_type.field_types.get(ip)[field_idx]; | 2929 | const field_ty = union_type.field_types.get(ip)[field_idx]; |
| 2904 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { | 2930 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { |
| 2905 | union_type.setRequiresComptime(ip, .yes); | 2931 | union_type.setRequiresComptime(ip, .yes); |
| 2906 | return true; | 2932 | return true; |
| 2933 | } | ||
| 2907 | } | 2934 | } |
| 2908 | } | ||
| 2909 | 2935 | ||
| 2910 | union_type.setRequiresComptime(ip, .no); | 2936 | union_type.setRequiresComptime(ip, .no); |
| 2911 | return false; | 2937 | return false; |
| 2938 | }, | ||
| 2912 | }, | 2939 | }, |
| 2913 | } | 2940 | }; |
| 2914 | }, | 2941 | }, |
| 2915 | 2942 | ||
| 2916 | .opaque_type => false, | 2943 | .opaque_type => false, |
| ... | @@ -3207,7 +3234,7 @@ pub fn fieldAlignmentInner( | ... | @@ -3207,7 +3234,7 @@ pub fn fieldAlignmentInner( |
| 3207 | ty: Type, | 3234 | ty: Type, |
| 3208 | index: usize, | 3235 | index: usize, |
| 3209 | comptime strat: ResolveStrat, | 3236 | comptime strat: ResolveStrat, |
| 3210 | zcu: *Zcu, | 3237 | zcu: strat.ZcuPtr(), |
| 3211 | tid: strat.Tid(), | 3238 | tid: strat.Tid(), |
| 3212 | ) SemaError!Alignment { | 3239 | ) SemaError!Alignment { |
| 3213 | const ip = &zcu.intern_pool; | 3240 | const ip = &zcu.intern_pool; |
| ... | @@ -3281,7 +3308,7 @@ pub fn structFieldAlignmentInner( | ... | @@ -3281,7 +3308,7 @@ pub fn structFieldAlignmentInner( |
| 3281 | explicit_alignment: Alignment, | 3308 | explicit_alignment: Alignment, |
| 3282 | layout: std.builtin.Type.ContainerLayout, | 3309 | layout: std.builtin.Type.ContainerLayout, |
| 3283 | comptime strat: Type.ResolveStrat, | 3310 | comptime strat: Type.ResolveStrat, |
| 3284 | zcu: *Zcu, | 3311 | zcu: strat.ZcuPtr(), |
| 3285 | tid: strat.Tid(), | 3312 | tid: strat.Tid(), |
| 3286 | ) SemaError!Alignment { | 3313 | ) SemaError!Alignment { |
| 3287 | assert(layout != .@"packed"); | 3314 | assert(layout != .@"packed"); |
| ... | @@ -3323,7 +3350,7 @@ pub fn unionFieldAlignmentInner( | ... | @@ -3323,7 +3350,7 @@ pub fn unionFieldAlignmentInner( |
| 3323 | explicit_alignment: Alignment, | 3350 | explicit_alignment: Alignment, |
| 3324 | layout: std.builtin.Type.ContainerLayout, | 3351 | layout: std.builtin.Type.ContainerLayout, |
| 3325 | comptime strat: Type.ResolveStrat, | 3352 | comptime strat: Type.ResolveStrat, |
| 3326 | zcu: *Zcu, | 3353 | zcu: strat.ZcuPtr(), |
| 3327 | tid: strat.Tid(), | 3354 | tid: strat.Tid(), |
| 3328 | ) SemaError!Alignment { | 3355 | ) SemaError!Alignment { |
| 3329 | assert(layout != .@"packed"); | 3356 | assert(layout != .@"packed"); |
| ... | @@ -3392,11 +3419,7 @@ pub const FieldOffset = struct { | ... | @@ -3392,11 +3419,7 @@ pub const FieldOffset = struct { |
| 3392 | }; | 3419 | }; |
| 3393 | 3420 | ||
| 3394 | /// Supports structs and unions. | 3421 | /// Supports structs and unions. |
| 3395 | pub fn structFieldOffset( | 3422 | pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 { |
| 3396 | ty: Type, | ||
| 3397 | index: usize, | ||
| 3398 | zcu: *Zcu, | ||
| 3399 | ) u64 { | ||
| 3400 | const ip = &zcu.intern_pool; | 3423 | const ip = &zcu.intern_pool; |
| 3401 | switch (ip.indexToKey(ty.toIntern())) { | 3424 | switch (ip.indexToKey(ty.toIntern())) { |
| 3402 | .struct_type => { | 3425 | .struct_type => { |
| ... | @@ -3944,7 +3967,7 @@ fn resolveUnionInner( | ... | @@ -3944,7 +3967,7 @@ fn resolveUnionInner( |
| 3944 | }; | 3967 | }; |
| 3945 | } | 3968 | } |
| 3946 | 3969 | ||
| 3947 | pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *Zcu) Zcu.UnionLayout { | 3970 | pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout { |
| 3948 | const ip = &zcu.intern_pool; | 3971 | const ip = &zcu.intern_pool; |
| 3949 | assert(loaded_union.haveLayout(ip)); | 3972 | assert(loaded_union.haveLayout(ip)); |
| 3950 | var most_aligned_field: u32 = undefined; | 3973 | var most_aligned_field: u32 = undefined; |
src/Value.zig+4-4| ... | @@ -241,12 +241,12 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable { | ... | @@ -241,12 +241,12 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable { |
| 241 | 241 | ||
| 242 | /// If the value fits in a u64, return it, otherwise null. | 242 | /// If the value fits in a u64, return it, otherwise null. |
| 243 | /// Asserts not undefined. | 243 | /// Asserts not undefined. |
| 244 | pub fn getUnsignedInt(val: Value, zcu: *Zcu) ?u64 { | 244 | pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 { |
| 245 | return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable; | 245 | return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable; |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | /// Asserts the value is an integer and it fits in a u64 | 248 | /// Asserts the value is an integer and it fits in a u64 |
| 249 | pub fn toUnsignedInt(val: Value, zcu: *Zcu) u64 { | 249 | pub fn toUnsignedInt(val: Value, zcu: *const Zcu) u64 { |
| 250 | return getUnsignedInt(val, zcu).?; | 250 | return getUnsignedInt(val, zcu).?; |
| 251 | } | 251 | } |
| 252 | 252 | ||
| ... | @@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 { | ... | @@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 { |
| 259 | pub fn getUnsignedIntInner( | 259 | pub fn getUnsignedIntInner( |
| 260 | val: Value, | 260 | val: Value, |
| 261 | comptime strat: ResolveStrat, | 261 | comptime strat: ResolveStrat, |
| 262 | zcu: *Zcu, | 262 | zcu: strat.ZcuPtr(), |
| 263 | tid: strat.Tid(), | 263 | tid: strat.Tid(), |
| 264 | ) !?u64 { | 264 | ) !?u64 { |
| 265 | return switch (val.toIntern()) { | 265 | return switch (val.toIntern()) { |
| ... | @@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 { | ... | @@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 { |
| 304 | } | 304 | } |
| 305 | 305 | ||
| 306 | /// Asserts the value is an integer and it fits in a i64 | 306 | /// Asserts the value is an integer and it fits in a i64 |
| 307 | pub fn toSignedInt(val: Value, zcu: *Zcu) i64 { | 307 | pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 { |
| 308 | return switch (val.toIntern()) { | 308 | return switch (val.toIntern()) { |
| 309 | .bool_false => 0, | 309 | .bool_false => 0, |
| 310 | .bool_true => 1, | 310 | .bool_true => 1, |
src/Zcu.zig+93-24| ... | @@ -19,8 +19,8 @@ const Ast = std.zig.Ast; | ... | @@ -19,8 +19,8 @@ const Ast = std.zig.Ast; |
| 19 | const Zcu = @This(); | 19 | const Zcu = @This(); |
| 20 | const Compilation = @import("Compilation.zig"); | 20 | const Compilation = @import("Compilation.zig"); |
| 21 | const Cache = std.Build.Cache; | 21 | const Cache = std.Build.Cache; |
| 22 | const Value = @import("Value.zig"); | 22 | pub const Value = @import("Value.zig"); |
| 23 | const Type = @import("Type.zig"); | 23 | pub const Type = @import("Type.zig"); |
| 24 | const Package = @import("Package.zig"); | 24 | const Package = @import("Package.zig"); |
| 25 | const link = @import("link.zig"); | 25 | const link = @import("link.zig"); |
| 26 | const Air = @import("Air.zig"); | 26 | const Air = @import("Air.zig"); |
| ... | @@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory, | ... | @@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory, |
| 79 | all_exports: std.ArrayListUnmanaged(Export) = .empty, | 79 | all_exports: std.ArrayListUnmanaged(Export) = .empty, |
| 80 | /// This is a list of free indices in `all_exports`. These indices may be reused by exports from | 80 | /// This is a list of free indices in `all_exports`. These indices may be reused by exports from |
| 81 | /// future semantic analysis. | 81 | /// future semantic analysis. |
| 82 | free_exports: std.ArrayListUnmanaged(u32) = .empty, | 82 | free_exports: std.ArrayListUnmanaged(Export.Index) = .empty, |
| 83 | /// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of | 83 | /// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of |
| 84 | /// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit` | 84 | /// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit` |
| 85 | /// whose analysis triggered the export. | 85 | /// whose analysis triggered the export. |
| 86 | single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, | 86 | single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, Export.Index) = .empty, |
| 87 | /// Like `single_exports`, but for `AnalUnit`s which perform multiple exports. | 87 | /// Like `single_exports`, but for `AnalUnit`s which perform multiple exports. |
| 88 | /// The exports are `all_exports.items[index..][0..len]`. | 88 | /// The exports are `all_exports.items[index..][0..len]`. |
| 89 | multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { | 89 | multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { |
| ... | @@ -127,6 +127,7 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp | ... | @@ -127,6 +127,7 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp |
| 127 | /// This may be a simple "value" `Nav`, or it may be a function. | 127 | /// This may be a simple "value" `Nav`, or it may be a function. |
| 128 | /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. | 128 | /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. |
| 129 | failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, | 129 | failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, |
| 130 | failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty, | ||
| 130 | /// Keep track of one `@compileLog` callsite per `AnalUnit`. | 131 | /// Keep track of one `@compileLog` callsite per `AnalUnit`. |
| 131 | /// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`. | 132 | /// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`. |
| 132 | compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { | 133 | compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { |
| ... | @@ -144,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { | ... | @@ -144,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { |
| 144 | failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty, | 145 | failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty, |
| 145 | /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. | 146 | /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. |
| 146 | failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty, | 147 | failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty, |
| 147 | /// Key is index into `all_exports`. | 148 | failed_exports: std.AutoArrayHashMapUnmanaged(Export.Index, *ErrorMsg) = .empty, |
| 148 | failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .empty, | ||
| 149 | /// If analysis failed due to a cimport error, the corresponding Clang errors | 149 | /// If analysis failed due to a cimport error, the corresponding Clang errors |
| 150 | /// are stored here. | 150 | /// are stored here. |
| 151 | cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty, | 151 | cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty, |
| ... | @@ -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 | }; |
| 528 | 537 | ||
| 529 | pub const Reference = struct { | 538 | pub const Reference = struct { |
| ... | @@ -2439,16 +2448,14 @@ pub fn deinit(zcu: *Zcu) void { | ... | @@ -2439,16 +2448,14 @@ pub fn deinit(zcu: *Zcu) void { |
| 2439 | zcu.local_zir_cache.handle.close(); | 2448 | zcu.local_zir_cache.handle.close(); |
| 2440 | zcu.global_zir_cache.handle.close(); | 2449 | zcu.global_zir_cache.handle.close(); |
| 2441 | 2450 | ||
| 2442 | for (zcu.failed_analysis.values()) |value| { | 2451 | for (zcu.failed_analysis.values()) |value| value.destroy(gpa); |
| 2443 | value.destroy(gpa); | 2452 | for (zcu.failed_codegen.values()) |value| value.destroy(gpa); |
| 2444 | } | 2453 | for (zcu.failed_types.values()) |value| value.destroy(gpa); |
| 2445 | for (zcu.failed_codegen.values()) |value| { | ||
| 2446 | value.destroy(gpa); | ||
| 2447 | } | ||
| 2448 | zcu.analysis_in_progress.deinit(gpa); | 2454 | zcu.analysis_in_progress.deinit(gpa); |
| 2449 | zcu.failed_analysis.deinit(gpa); | 2455 | zcu.failed_analysis.deinit(gpa); |
| 2450 | zcu.transitive_failed_analysis.deinit(gpa); | 2456 | zcu.transitive_failed_analysis.deinit(gpa); |
| 2451 | zcu.failed_codegen.deinit(gpa); | 2457 | zcu.failed_codegen.deinit(gpa); |
| 2458 | zcu.failed_types.deinit(gpa); | ||
| 2452 | 2459 | ||
| 2453 | for (zcu.failed_files.values()) |value| { | 2460 | for (zcu.failed_files.values()) |value| { |
| 2454 | if (value) |msg| msg.destroy(gpa); | 2461 | if (value) |msg| msg.destroy(gpa); |
| ... | @@ -3093,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { | ... | @@ -3093,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3093 | const gpa = zcu.gpa; | 3100 | const gpa = zcu.gpa; |
| 3094 | 3101 | ||
| 3095 | const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv| | 3102 | const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv| |
| 3096 | .{ kv.value, 1 } | 3103 | .{ @intFromEnum(kv.value), 1 } |
| 3097 | else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| | 3104 | else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| |
| 3098 | .{ info.value.index, info.value.len } | 3105 | .{ info.value.index, info.value.len } |
| 3099 | else | 3106 | else |
| ... | @@ -3107,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { | ... | @@ -3107,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3107 | // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports | 3114 | // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports |
| 3108 | // within a single update. | 3115 | // within a single update. |
| 3109 | if (dev.env.supports(.incremental)) { | 3116 | if (dev.env.supports(.incremental)) { |
| 3110 | for (exports, exports_base..) |exp, export_idx| { | 3117 | for (exports, exports_base..) |exp, export_index_usize| { |
| 3118 | const export_idx: Export.Index = @enumFromInt(export_index_usize); | ||
| 3111 | if (zcu.comp.bin_file) |lf| { | 3119 | if (zcu.comp.bin_file) |lf| { |
| 3112 | lf.deleteExport(exp.exported, exp.opts.name); | 3120 | lf.deleteExport(exp.exported, exp.opts.name); |
| 3113 | } | 3121 | } |
| 3114 | if (zcu.failed_exports.fetchSwapRemove(@intCast(export_idx))) |failed_kv| { | 3122 | if (zcu.failed_exports.fetchSwapRemove(export_idx)) |failed_kv| { |
| 3115 | failed_kv.value.destroy(gpa); | 3123 | failed_kv.value.destroy(gpa); |
| 3116 | } | 3124 | } |
| 3117 | } | 3125 | } |
| ... | @@ -3123,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { | ... | @@ -3123,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3123 | return; | 3131 | return; |
| 3124 | }; | 3132 | }; |
| 3125 | for (exports_base..exports_base + exports_len) |export_idx| { | 3133 | for (exports_base..exports_base + exports_len) |export_idx| { |
| 3126 | zcu.free_exports.appendAssumeCapacity(@intCast(export_idx)); | 3134 | zcu.free_exports.appendAssumeCapacity(@enumFromInt(export_idx)); |
| 3127 | } | 3135 | } |
| 3128 | } | 3136 | } |
| 3129 | 3137 | ||
| ... | @@ -3269,7 +3277,7 @@ fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void { | ... | @@ -3269,7 +3277,7 @@ fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void { |
| 3269 | 3277 | ||
| 3270 | pub fn handleUpdateExports( | 3278 | pub fn handleUpdateExports( |
| 3271 | zcu: *Zcu, | 3279 | zcu: *Zcu, |
| 3272 | export_indices: []const u32, | 3280 | export_indices: []const Export.Index, |
| 3273 | result: link.File.UpdateExportsError!void, | 3281 | result: link.File.UpdateExportsError!void, |
| 3274 | ) Allocator.Error!void { | 3282 | ) Allocator.Error!void { |
| 3275 | const gpa = zcu.gpa; | 3283 | const gpa = zcu.gpa; |
| ... | @@ -3277,12 +3285,10 @@ pub fn handleUpdateExports( | ... | @@ -3277,12 +3285,10 @@ pub fn handleUpdateExports( |
| 3277 | error.OutOfMemory => return error.OutOfMemory, | 3285 | error.OutOfMemory => return error.OutOfMemory, |
| 3278 | error.AnalysisFail => { | 3286 | error.AnalysisFail => { |
| 3279 | const export_idx = export_indices[0]; | 3287 | const export_idx = export_indices[0]; |
| 3280 | const new_export = &zcu.all_exports.items[export_idx]; | 3288 | const new_export = export_idx.ptr(zcu); |
| 3281 | new_export.status = .failed_retryable; | 3289 | new_export.status = .failed_retryable; |
| 3282 | try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); | 3290 | try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); |
| 3283 | const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{ | 3291 | const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)}); |
| 3284 | @errorName(err), | ||
| 3285 | }); | ||
| 3286 | zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); | 3292 | zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); |
| 3287 | }, | 3293 | }, |
| 3288 | }; | 3294 | }; |
| ... | @@ -3443,7 +3449,7 @@ pub fn atomicPtrAlignment( | ... | @@ -3443,7 +3449,7 @@ pub fn atomicPtrAlignment( |
| 3443 | /// * `@TypeOf(.{})` | 3449 | /// * `@TypeOf(.{})` |
| 3444 | /// * A struct which has no fields (`struct {}`). | 3450 | /// * A struct which has no fields (`struct {}`). |
| 3445 | /// * Not a struct. | 3451 | /// * Not a struct. |
| 3446 | pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType { | 3452 | pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType { |
| 3447 | if (ty.ip_index == .none) return null; | 3453 | if (ty.ip_index == .none) return null; |
| 3448 | const ip = &zcu.intern_pool; | 3454 | const ip = &zcu.intern_pool; |
| 3449 | return switch (ip.indexToKey(ty.ip_index)) { | 3455 | return switch (ip.indexToKey(ty.ip_index)) { |
| ... | @@ -3452,7 +3458,7 @@ pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType { | ... | @@ -3452,7 +3458,7 @@ pub fn typeToStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType { |
| 3452 | }; | 3458 | }; |
| 3453 | } | 3459 | } |
| 3454 | 3460 | ||
| 3455 | pub fn typeToPackedStruct(zcu: *Zcu, ty: Type) ?InternPool.LoadedStructType { | 3461 | pub fn typeToPackedStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType { |
| 3456 | const s = zcu.typeToStruct(ty) orelse return null; | 3462 | const s = zcu.typeToStruct(ty) orelse return null; |
| 3457 | if (s.layout != .@"packed") return null; | 3463 | if (s.layout != .@"packed") return null; |
| 3458 | return s; | 3464 | return s; |
| ... | @@ -3477,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind | ... | @@ -3477,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind |
| 3477 | } | 3483 | } |
| 3478 | 3484 | ||
| 3479 | pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func { | 3485 | pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func { |
| 3480 | return zcu.intern_pool.indexToKey(func_index).func; | 3486 | return zcu.intern_pool.toFunc(func_index); |
| 3481 | } | 3487 | } |
| 3482 | 3488 | ||
| 3483 | pub fn toEnum(zcu: *const Zcu, comptime E: type, val: Value) E { | 3489 | pub fn toEnum(zcu: *const Zcu, comptime E: type, val: Value) E { |
| ... | @@ -3791,6 +3797,18 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc { | ... | @@ -3791,6 +3797,18 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc { |
| 3791 | }; | 3797 | }; |
| 3792 | } | 3798 | } |
| 3793 | 3799 | ||
| 3800 | pub fn typeSrcLoc(zcu: *const Zcu, ty_index: InternPool.Index) LazySrcLoc { | ||
| 3801 | _ = zcu; | ||
| 3802 | _ = ty_index; | ||
| 3803 | @panic("TODO"); | ||
| 3804 | } | ||
| 3805 | |||
| 3806 | pub fn typeFileScope(zcu: *Zcu, ty_index: InternPool.Index) *File { | ||
| 3807 | _ = zcu; | ||
| 3808 | _ = ty_index; | ||
| 3809 | @panic("TODO"); | ||
| 3810 | } | ||
| 3811 | |||
| 3794 | pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { | 3812 | pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { |
| 3795 | const ip = &zcu.intern_pool; | 3813 | const ip = &zcu.intern_pool; |
| 3796 | const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?; | 3814 | const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?; |
| ... | @@ -4051,3 +4069,54 @@ pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool { | ... | @@ -4051,3 +4069,54 @@ pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool { |
| 4051 | else => true, | 4069 | else => true, |
| 4052 | }; | 4070 | }; |
| 4053 | } | 4071 | } |
| 4072 | |||
| 4073 | pub const CodegenFailError = error{ | ||
| 4074 | /// Indicates the error message has been already stored at `Zcu.failed_codegen`. | ||
| 4075 | CodegenFail, | ||
| 4076 | OutOfMemory, | ||
| 4077 | }; | ||
| 4078 | |||
| 4079 | pub fn codegenFail( | ||
| 4080 | zcu: *Zcu, | ||
| 4081 | nav_index: InternPool.Nav.Index, | ||
| 4082 | comptime format: []const u8, | ||
| 4083 | args: anytype, | ||
| 4084 | ) CodegenFailError { | ||
| 4085 | const gpa = zcu.gpa; | ||
| 4086 | try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1); | ||
| 4087 | const msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(nav_index), format, args); | ||
| 4088 | zcu.failed_codegen.putAssumeCapacityNoClobber(nav_index, msg); | ||
| 4089 | return error.CodegenFail; | ||
| 4090 | } | ||
| 4091 | |||
| 4092 | pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError { | ||
| 4093 | const gpa = zcu.gpa; | ||
| 4094 | { | ||
| 4095 | errdefer msg.deinit(gpa); | ||
| 4096 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg); | ||
| 4097 | } | ||
| 4098 | return error.CodegenFail; | ||
| 4099 | } | ||
| 4100 | |||
| 4101 | pub fn codegenFailType( | ||
| 4102 | zcu: *Zcu, | ||
| 4103 | ty_index: InternPool.Index, | ||
| 4104 | comptime format: []const u8, | ||
| 4105 | args: anytype, | ||
| 4106 | ) CodegenFailError { | ||
| 4107 | const gpa = zcu.gpa; | ||
| 4108 | try zcu.failed_types.ensureUnusedCapacity(gpa, 1); | ||
| 4109 | const msg = try Zcu.ErrorMsg.create(gpa, zcu.typeSrcLoc(ty_index), format, args); | ||
| 4110 | zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); | ||
| 4111 | return error.CodegenFail; | ||
| 4112 | } | ||
| 4113 | |||
| 4114 | pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) CodegenFailError { | ||
| 4115 | const gpa = zcu.gpa; | ||
| 4116 | { | ||
| 4117 | errdefer msg.deinit(gpa); | ||
| 4118 | try zcu.failed_types.ensureUnusedCapacity(gpa, 1); | ||
| 4119 | } | ||
| 4120 | zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); | ||
| 4121 | return error.CodegenFail; | ||
| 4122 | } |
src/Zcu/PerThread.zig+26-38| ... | @@ -1722,22 +1722,18 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai | ... | @@ -1722,22 +1722,18 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai |
| 1722 | // Correcting this failure will involve changing a type this function | 1722 | // Correcting this failure will involve changing a type this function |
| 1723 | // depends on, hence triggering re-analysis of this function, so this | 1723 | // 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.Overflow => { |
| 1732 | }, | ||
| 1733 | else => { | ||
| 1734 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( | 1730 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( |
| 1735 | gpa, | 1731 | gpa, |
| 1736 | zcu.navSrcLoc(nav_index), | 1732 | zcu.navSrcLoc(nav_index), |
| 1737 | "unable to codegen: {s}", | 1733 | "unable to codegen: {s}", |
| 1738 | .{@errorName(err)}, | 1734 | .{@errorName(err)}, |
| 1739 | )); | 1735 | )); |
| 1740 | try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index })); | 1736 | // Not a retryable failure. |
| 1741 | }, | 1737 | }, |
| 1742 | }; | 1738 | }; |
| 1743 | } else if (zcu.llvm_object) |llvm_object| { | 1739 | } else if (zcu.llvm_object) |llvm_object| { |
| ... | @@ -2819,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void { | ... | @@ -2819,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2819 | const gpa = zcu.gpa; | 2815 | const gpa = zcu.gpa; |
| 2820 | 2816 | ||
| 2821 | // First, construct a mapping of every exported value and Nav to the indices of all its different exports. | 2817 | // First, construct a mapping of every exported value and Nav to the indices of all its different exports. |
| 2822 | var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(u32)) = .empty; | 2818 | var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty; |
| 2823 | var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(u32)) = .empty; | 2819 | var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty; |
| 2824 | defer { | 2820 | defer { |
| 2825 | for (nav_exports.values()) |*exports| { | 2821 | for (nav_exports.values()) |*exports| { |
| 2826 | exports.deinit(gpa); | 2822 | exports.deinit(gpa); |
| ... | @@ -2839,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { | ... | @@ -2839,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2839 | try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count()); | 2835 | try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count()); |
| 2840 | 2836 | ||
| 2841 | for (zcu.single_exports.values()) |export_idx| { | 2837 | for (zcu.single_exports.values()) |export_idx| { |
| 2842 | const exp = zcu.all_exports.items[export_idx]; | 2838 | const exp = export_idx.ptr(zcu); |
| 2843 | const value_ptr, const found_existing = switch (exp.exported) { | 2839 | const value_ptr, const found_existing = switch (exp.exported) { |
| 2844 | .nav => |nav| gop: { | 2840 | .nav => |nav| gop: { |
| 2845 | const gop = try nav_exports.getOrPut(gpa, nav); | 2841 | const gop = try nav_exports.getOrPut(gpa, nav); |
| ... | @@ -2867,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { | ... | @@ -2867,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2867 | }, | 2863 | }, |
| 2868 | }; | 2864 | }; |
| 2869 | if (!found_existing) value_ptr.* = .{}; | 2865 | if (!found_existing) value_ptr.* = .{}; |
| 2870 | try value_ptr.append(gpa, @intCast(export_idx)); | 2866 | try value_ptr.append(gpa, @enumFromInt(export_idx)); |
| 2871 | } | 2867 | } |
| 2872 | } | 2868 | } |
| 2873 | 2869 | ||
| ... | @@ -2886,20 +2882,20 @@ pub fn processExports(pt: Zcu.PerThread) !void { | ... | @@ -2886,20 +2882,20 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2886 | } | 2882 | } |
| 2887 | } | 2883 | } |
| 2888 | 2884 | ||
| 2889 | const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, u32); | 2885 | const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Zcu.Export.Index); |
| 2890 | 2886 | ||
| 2891 | fn processExportsInner( | 2887 | fn processExportsInner( |
| 2892 | pt: Zcu.PerThread, | 2888 | pt: Zcu.PerThread, |
| 2893 | symbol_exports: *SymbolExports, | 2889 | symbol_exports: *SymbolExports, |
| 2894 | exported: Zcu.Exported, | 2890 | exported: Zcu.Exported, |
| 2895 | export_indices: []const u32, | 2891 | export_indices: []const Zcu.Export.Index, |
| 2896 | ) error{OutOfMemory}!void { | 2892 | ) error{OutOfMemory}!void { |
| 2897 | const zcu = pt.zcu; | 2893 | const zcu = pt.zcu; |
| 2898 | const gpa = zcu.gpa; | 2894 | const gpa = zcu.gpa; |
| 2899 | const ip = &zcu.intern_pool; | 2895 | const ip = &zcu.intern_pool; |
| 2900 | 2896 | ||
| 2901 | for (export_indices) |export_idx| { | 2897 | for (export_indices) |export_idx| { |
| 2902 | const new_export = &zcu.all_exports.items[export_idx]; | 2898 | const new_export = export_idx.ptr(zcu); |
| 2903 | const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); | 2899 | const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); |
| 2904 | if (gop.found_existing) { | 2900 | if (gop.found_existing) { |
| 2905 | new_export.status = .failed_retryable; | 2901 | new_export.status = .failed_retryable; |
| ... | @@ -2908,7 +2904,7 @@ fn processExportsInner( | ... | @@ -2908,7 +2904,7 @@ fn processExportsInner( |
| 2908 | new_export.opts.name.fmt(ip), | 2904 | new_export.opts.name.fmt(ip), |
| 2909 | }); | 2905 | }); |
| 2910 | errdefer msg.destroy(gpa); | 2906 | errdefer msg.destroy(gpa); |
| 2911 | const other_export = zcu.all_exports.items[gop.value_ptr.*]; | 2907 | const other_export = gop.value_ptr.ptr(zcu); |
| 2912 | try zcu.errNote(other_export.src, msg, "other symbol here", .{}); | 2908 | try zcu.errNote(other_export.src, msg, "other symbol here", .{}); |
| 2913 | zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); | 2909 | zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); |
| 2914 | new_export.status = .failed; | 2910 | new_export.status = .failed; |
| ... | @@ -3100,6 +3096,7 @@ pub fn populateTestFunctions( | ... | @@ -3100,6 +3096,7 @@ pub fn populateTestFunctions( |
| 3100 | pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void { | 3096 | pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void { |
| 3101 | const zcu = pt.zcu; | 3097 | const zcu = pt.zcu; |
| 3102 | const comp = zcu.comp; | 3098 | const comp = zcu.comp; |
| 3099 | const gpa = zcu.gpa; | ||
| 3103 | const ip = &zcu.intern_pool; | 3100 | const ip = &zcu.intern_pool; |
| 3104 | 3101 | ||
| 3105 | const nav = zcu.intern_pool.getNav(nav_index); | 3102 | const nav = zcu.intern_pool.getNav(nav_index); |
| ... | @@ -3113,26 +3110,15 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error | ... | @@ -3113,26 +3110,15 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error |
| 3113 | } else if (comp.bin_file) |lf| { | 3110 | } else if (comp.bin_file) |lf| { |
| 3114 | lf.updateNav(pt, nav_index) catch |err| switch (err) { | 3111 | lf.updateNav(pt, nav_index) catch |err| switch (err) { |
| 3115 | error.OutOfMemory => return error.OutOfMemory, | 3112 | error.OutOfMemory => return error.OutOfMemory, |
| 3116 | error.AnalysisFail => { | 3113 | error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)), |
| 3117 | assert(zcu.failed_codegen.contains(nav_index)); | 3114 | error.Overflow => { |
| 3118 | }, | 3115 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( |
| 3119 | else => { | ||
| 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, | 3116 | gpa, |
| 3124 | zcu.navSrcLoc(nav_index), | 3117 | zcu.navSrcLoc(nav_index), |
| 3125 | "unable to codegen: {s}", | 3118 | "unable to codegen: {s}", |
| 3126 | .{@errorName(err)}, | 3119 | .{@errorName(err)}, |
| 3127 | )); | 3120 | )); |
| 3128 | if (nav.analysis != null) { | 3121 | // 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 | }, | 3122 | }, |
| 3137 | }; | 3123 | }; |
| 3138 | } else if (zcu.llvm_object) |llvm_object| { | 3124 | } else if (zcu.llvm_object) |llvm_object| { |
| ... | @@ -3142,24 +3128,26 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error | ... | @@ -3142,24 +3128,26 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error |
| 3142 | } | 3128 | } |
| 3143 | } | 3129 | } |
| 3144 | 3130 | ||
| 3145 | pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) !void { | 3131 | pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) error{OutOfMemory}!void { |
| 3146 | const zcu = pt.zcu; | 3132 | const zcu = pt.zcu; |
| 3133 | const gpa = zcu.gpa; | ||
| 3147 | const comp = zcu.comp; | 3134 | const comp = zcu.comp; |
| 3148 | const ip = &zcu.intern_pool; | 3135 | const ip = &zcu.intern_pool; |
| 3149 | 3136 | ||
| 3150 | const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0); | 3137 | const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0); |
| 3151 | defer codegen_prog_node.end(); | 3138 | defer codegen_prog_node.end(); |
| 3152 | 3139 | ||
| 3140 | if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(gpa); | ||
| 3141 | |||
| 3153 | if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) { | 3142 | if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) { |
| 3154 | // This type failed to resolve. This is a transitive failure. | 3143 | // This type failed to resolve. This is a transitive failure. |
| 3155 | // TODO: do we need to mark this failure anywhere? I don't think so, since compilation | 3144 | return; |
| 3156 | // will fail due to the type error anyway. | ||
| 3157 | } else if (comp.bin_file) |lf| { | ||
| 3158 | lf.updateContainerType(pt, ty) catch |err| switch (err) { | ||
| 3159 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3160 | else => |e| log.err("codegen type failed: {s}", .{@errorName(e)}), | ||
| 3161 | }; | ||
| 3162 | } | 3145 | } |
| 3146 | |||
| 3147 | if (comp.bin_file) |lf| lf.updateContainerType(pt, ty) catch |err| switch (err) { | ||
| 3148 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3149 | error.TypeFailureReported => assert(zcu.failed_types.contains(ty)), | ||
| 3150 | }; | ||
| 3163 | } | 3151 | } |
| 3164 | 3152 | ||
| 3165 | pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Index) !void { | 3153 | pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Index) !void { |
src/arch/aarch64/CodeGen.zig+139-159| ... | @@ -24,7 +24,6 @@ const build_options = @import("build_options"); | ... | @@ -24,7 +24,6 @@ const build_options = @import("build_options"); |
| 24 | const Alignment = InternPool.Alignment; | 24 | const Alignment = InternPool.Alignment; |
| 25 | 25 | ||
| 26 | const CodeGenError = codegen.CodeGenError; | 26 | const CodeGenError = codegen.CodeGenError; |
| 27 | const Result = codegen.Result; | ||
| 28 | 27 | ||
| 29 | const bits = @import("bits.zig"); | 28 | const bits = @import("bits.zig"); |
| 30 | const abi = @import("abi.zig"); | 29 | const abi = @import("abi.zig"); |
| ... | @@ -51,7 +50,6 @@ debug_output: link.File.DebugInfoOutput, | ... | @@ -51,7 +50,6 @@ debug_output: link.File.DebugInfoOutput, |
| 51 | target: *const std.Target, | 50 | target: *const std.Target, |
| 52 | func_index: InternPool.Index, | 51 | func_index: InternPool.Index, |
| 53 | owner_nav: InternPool.Nav.Index, | 52 | owner_nav: InternPool.Nav.Index, |
| 54 | err_msg: ?*ErrorMsg, | ||
| 55 | args: []MCValue, | 53 | args: []MCValue, |
| 56 | ret_mcv: MCValue, | 54 | ret_mcv: MCValue, |
| 57 | fn_type: Type, | 55 | fn_type: Type, |
| ... | @@ -325,9 +323,9 @@ pub fn generate( | ... | @@ -325,9 +323,9 @@ pub fn generate( |
| 325 | func_index: InternPool.Index, | 323 | func_index: InternPool.Index, |
| 326 | air: Air, | 324 | air: Air, |
| 327 | liveness: Liveness, | 325 | liveness: Liveness, |
| 328 | code: *std.ArrayList(u8), | 326 | code: *std.ArrayListUnmanaged(u8), |
| 329 | debug_output: link.File.DebugInfoOutput, | 327 | debug_output: link.File.DebugInfoOutput, |
| 330 | ) CodeGenError!Result { | 328 | ) CodeGenError!void { |
| 331 | const zcu = pt.zcu; | 329 | const zcu = pt.zcu; |
| 332 | const gpa = zcu.gpa; | 330 | const gpa = zcu.gpa; |
| 333 | const func = zcu.funcInfo(func_index); | 331 | const func = zcu.funcInfo(func_index); |
| ... | @@ -353,7 +351,6 @@ pub fn generate( | ... | @@ -353,7 +351,6 @@ pub fn generate( |
| 353 | .bin_file = lf, | 351 | .bin_file = lf, |
| 354 | .func_index = func_index, | 352 | .func_index = func_index, |
| 355 | .owner_nav = func.owner_nav, | 353 | .owner_nav = func.owner_nav, |
| 356 | .err_msg = null, | ||
| 357 | .args = undefined, // populated after `resolveCallingConventionValues` | 354 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 358 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` | 355 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 359 | .fn_type = fn_type, | 356 | .fn_type = fn_type, |
| ... | @@ -370,10 +367,7 @@ pub fn generate( | ... | @@ -370,10 +367,7 @@ pub fn generate( |
| 370 | defer function.dbg_info_relocs.deinit(gpa); | 367 | defer function.dbg_info_relocs.deinit(gpa); |
| 371 | 368 | ||
| 372 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 369 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 373 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 370 | error.CodegenFail => return error.CodegenFail, |
| 374 | error.OutOfRegisters => return Result{ | ||
| 375 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 376 | }, | ||
| 377 | else => |e| return e, | 371 | else => |e| return e, |
| 378 | }; | 372 | }; |
| 379 | defer call_info.deinit(&function); | 373 | defer call_info.deinit(&function); |
| ... | @@ -384,24 +378,23 @@ pub fn generate( | ... | @@ -384,24 +378,23 @@ pub fn generate( |
| 384 | function.max_end_stack = call_info.stack_byte_count; | 378 | function.max_end_stack = call_info.stack_byte_count; |
| 385 | 379 | ||
| 386 | function.gen() catch |err| switch (err) { | 380 | function.gen() catch |err| switch (err) { |
| 387 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 381 | error.CodegenFail => return error.CodegenFail, |
| 388 | error.OutOfRegisters => return Result{ | 382 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 389 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 390 | }, | ||
| 391 | else => |e| return e, | 383 | else => |e| return e, |
| 392 | }; | 384 | }; |
| 393 | 385 | ||
| 394 | for (function.dbg_info_relocs.items) |reloc| { | 386 | for (function.dbg_info_relocs.items) |reloc| { |
| 395 | try reloc.genDbgInfo(function); | 387 | reloc.genDbgInfo(function) catch |err| |
| 388 | return function.fail("failed to generate debug info: {s}", .{@errorName(err)}); | ||
| 396 | } | 389 | } |
| 397 | 390 | ||
| 398 | var mir = Mir{ | 391 | var mir: Mir = .{ |
| 399 | .instructions = function.mir_instructions.toOwnedSlice(), | 392 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 400 | .extra = try function.mir_extra.toOwnedSlice(gpa), | 393 | .extra = try function.mir_extra.toOwnedSlice(gpa), |
| 401 | }; | 394 | }; |
| 402 | defer mir.deinit(gpa); | 395 | defer mir.deinit(gpa); |
| 403 | 396 | ||
| 404 | var emit = Emit{ | 397 | var emit: Emit = .{ |
| 405 | .mir = mir, | 398 | .mir = mir, |
| 406 | .bin_file = lf, | 399 | .bin_file = lf, |
| 407 | .debug_output = debug_output, | 400 | .debug_output = debug_output, |
| ... | @@ -417,15 +410,9 @@ pub fn generate( | ... | @@ -417,15 +410,9 @@ pub fn generate( |
| 417 | defer emit.deinit(); | 410 | defer emit.deinit(); |
| 418 | 411 | ||
| 419 | emit.emitMir() catch |err| switch (err) { | 412 | emit.emitMir() catch |err| switch (err) { |
| 420 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | 413 | error.EmitFail => return function.failMsg(emit.err_msg.?), |
| 421 | else => |e| return e, | 414 | else => |e| return e, |
| 422 | }; | 415 | }; |
| 423 | |||
| 424 | if (function.err_msg) |em| { | ||
| 425 | return Result{ .fail = em }; | ||
| 426 | } else { | ||
| 427 | return Result.ok; | ||
| 428 | } | ||
| 429 | } | 416 | } |
| 430 | 417 | ||
| 431 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { | 418 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| ... | @@ -567,7 +554,7 @@ fn gen(self: *Self) !void { | ... | @@ -567,7 +554,7 @@ fn gen(self: *Self) !void { |
| 567 | .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, | 554 | .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, |
| 568 | }); | 555 | }); |
| 569 | } else { | 556 | } else { |
| 570 | return self.failSymbol("TODO AArch64: allow larger stacks", .{}); | 557 | @panic("TODO AArch64: allow larger stacks"); |
| 571 | } | 558 | } |
| 572 | 559 | ||
| 573 | _ = try self.addInst(.{ | 560 | _ = try self.addInst(.{ |
| ... | @@ -723,7 +710,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -723,7 +710,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 723 | .cmp_gt => try self.airCmp(inst, .gt), | 710 | .cmp_gt => try self.airCmp(inst, .gt), |
| 724 | .cmp_neq => try self.airCmp(inst, .neq), | 711 | .cmp_neq => try self.airCmp(inst, .neq), |
| 725 | 712 | ||
| 726 | .cmp_vector => try self.airCmpVector(inst), | 713 | .cmp_vector => try self.airCmpVector(inst), |
| 727 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), | 714 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 728 | 715 | ||
| 729 | .alloc => try self.airAlloc(inst), | 716 | .alloc => try self.airAlloc(inst), |
| ... | @@ -744,7 +731,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -744,7 +731,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 744 | .fpext => try self.airFpext(inst), | 731 | .fpext => try self.airFpext(inst), |
| 745 | .intcast => try self.airIntCast(inst), | 732 | .intcast => try self.airIntCast(inst), |
| 746 | .trunc => try self.airTrunc(inst), | 733 | .trunc => try self.airTrunc(inst), |
| 747 | .int_from_bool => try self.airIntFromBool(inst), | 734 | .int_from_bool => try self.airIntFromBool(inst), |
| 748 | .is_non_null => try self.airIsNonNull(inst), | 735 | .is_non_null => try self.airIsNonNull(inst), |
| 749 | .is_non_null_ptr => try self.airIsNonNullPtr(inst), | 736 | .is_non_null_ptr => try self.airIsNonNullPtr(inst), |
| 750 | .is_null => try self.airIsNull(inst), | 737 | .is_null => try self.airIsNull(inst), |
| ... | @@ -756,7 +743,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -756,7 +743,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 756 | .load => try self.airLoad(inst), | 743 | .load => try self.airLoad(inst), |
| 757 | .loop => try self.airLoop(inst), | 744 | .loop => try self.airLoop(inst), |
| 758 | .not => try self.airNot(inst), | 745 | .not => try self.airNot(inst), |
| 759 | .int_from_ptr => try self.airIntFromPtr(inst), | 746 | .int_from_ptr => try self.airIntFromPtr(inst), |
| 760 | .ret => try self.airRet(inst), | 747 | .ret => try self.airRet(inst), |
| 761 | .ret_safe => try self.airRet(inst), // TODO | 748 | .ret_safe => try self.airRet(inst), // TODO |
| 762 | .ret_load => try self.airRetLoad(inst), | 749 | .ret_load => try self.airRetLoad(inst), |
| ... | @@ -765,8 +752,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -765,8 +752,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 765 | .struct_field_ptr=> try self.airStructFieldPtr(inst), | 752 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 766 | .struct_field_val=> try self.airStructFieldVal(inst), | 753 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 767 | .array_to_slice => try self.airArrayToSlice(inst), | 754 | .array_to_slice => try self.airArrayToSlice(inst), |
| 768 | .float_from_int => try self.airFloatFromInt(inst), | 755 | .float_from_int => try self.airFloatFromInt(inst), |
| 769 | .int_from_float => try self.airIntFromFloat(inst), | 756 | .int_from_float => try self.airIntFromFloat(inst), |
| 770 | .cmpxchg_strong => try self.airCmpxchg(inst), | 757 | .cmpxchg_strong => try self.airCmpxchg(inst), |
| 771 | .cmpxchg_weak => try self.airCmpxchg(inst), | 758 | .cmpxchg_weak => try self.airCmpxchg(inst), |
| 772 | .atomic_rmw => try self.airAtomicRmw(inst), | 759 | .atomic_rmw => try self.airAtomicRmw(inst), |
| ... | @@ -1107,7 +1094,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { | ... | @@ -1107,7 +1094,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 1107 | /// Copies a value to a register without tracking the register. The register is not considered | 1094 | /// 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. | 1095 | /// 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. | 1096 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1110 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { | 1097 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) InnerError!Register { |
| 1111 | const raw_reg = try self.register_manager.allocReg(null, gp); | 1098 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1112 | const reg = self.registerAlias(raw_reg, ty); | 1099 | const reg = self.registerAlias(raw_reg, ty); |
| 1113 | try self.genSetReg(ty, reg, mcv); | 1100 | try self.genSetReg(ty, reg, mcv); |
| ... | @@ -1125,12 +1112,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa | ... | @@ -1125,12 +1112,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa |
| 1125 | return MCValue{ .register = reg }; | 1112 | return MCValue{ .register = reg }; |
| 1126 | } | 1113 | } |
| 1127 | 1114 | ||
| 1128 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { | 1115 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1129 | const stack_offset = try self.allocMemPtr(inst); | 1116 | const stack_offset = try self.allocMemPtr(inst); |
| 1130 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); | 1117 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 1131 | } | 1118 | } |
| 1132 | 1119 | ||
| 1133 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { | 1120 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1134 | const pt = self.pt; | 1121 | const pt = self.pt; |
| 1135 | const zcu = pt.zcu; | 1122 | const zcu = pt.zcu; |
| 1136 | const result: MCValue = switch (self.ret_mcv) { | 1123 | const result: MCValue = switch (self.ret_mcv) { |
| ... | @@ -1152,19 +1139,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1152,19 +1139,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1152 | return self.finishAir(inst, result, .{ .none, .none, .none }); | 1139 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 1153 | } | 1140 | } |
| 1154 | 1141 | ||
| 1155 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { | 1142 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1156 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1143 | 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}); | 1144 | 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 }); | 1145 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1159 | } | 1146 | } |
| 1160 | 1147 | ||
| 1161 | fn airFpext(self: *Self, inst: Air.Inst.Index) !void { | 1148 | fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1162 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1149 | 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}); | 1150 | 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 }); | 1151 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1165 | } | 1152 | } |
| 1166 | 1153 | ||
| 1167 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | 1154 | fn airIntCast(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1168 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1155 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1169 | if (self.liveness.isUnused(inst)) | 1156 | if (self.liveness.isUnused(inst)) |
| 1170 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); | 1157 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| ... | @@ -1293,7 +1280,7 @@ fn trunc( | ... | @@ -1293,7 +1280,7 @@ fn trunc( |
| 1293 | } | 1280 | } |
| 1294 | } | 1281 | } |
| 1295 | 1282 | ||
| 1296 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { | 1283 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1297 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1284 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1298 | const operand = try self.resolveInst(ty_op.operand); | 1285 | const operand = try self.resolveInst(ty_op.operand); |
| 1299 | const operand_ty = self.typeOf(ty_op.operand); | 1286 | const operand_ty = self.typeOf(ty_op.operand); |
| ... | @@ -1306,14 +1293,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1306,14 +1293,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1293 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1307 | } | 1294 | } |
| 1308 | 1295 | ||
| 1309 | fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void { | 1296 | fn airIntFromBool(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1310 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 1297 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 1311 | const operand = try self.resolveInst(un_op); | 1298 | const operand = try self.resolveInst(un_op); |
| 1312 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; | 1299 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; |
| 1313 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 1300 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 1314 | } | 1301 | } |
| 1315 | 1302 | ||
| 1316 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { | 1303 | fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1317 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1304 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1318 | const pt = self.pt; | 1305 | const pt = self.pt; |
| 1319 | const zcu = pt.zcu; | 1306 | const zcu = pt.zcu; |
| ... | @@ -1484,7 +1471,7 @@ fn minMax( | ... | @@ -1484,7 +1471,7 @@ fn minMax( |
| 1484 | } | 1471 | } |
| 1485 | } | 1472 | } |
| 1486 | 1473 | ||
| 1487 | fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { | 1474 | fn airMinMax(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1488 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; | 1475 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 1489 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 1476 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 1490 | const lhs_ty = self.typeOf(bin_op.lhs); | 1477 | const lhs_ty = self.typeOf(bin_op.lhs); |
| ... | @@ -1502,7 +1489,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1502,7 +1489,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1502 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1489 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1503 | } | 1490 | } |
| 1504 | 1491 | ||
| 1505 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | 1492 | fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1506 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 1493 | 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; | 1494 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1508 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1495 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | @@ -2440,7 +2427,7 @@ fn ptrArithmetic( | ... | @@ -2440,7 +2427,7 @@ fn ptrArithmetic( |
| 2440 | } | 2427 | } |
| 2441 | } | 2428 | } |
| 2442 | 2429 | ||
| 2443 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | 2430 | fn 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; | 2431 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2445 | const lhs_ty = self.typeOf(bin_op.lhs); | 2432 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 2446 | const rhs_ty = self.typeOf(bin_op.rhs); | 2433 | const rhs_ty = self.typeOf(bin_op.rhs); |
| ... | @@ -2490,7 +2477,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | ... | @@ -2490,7 +2477,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 }); | 2477 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2491 | } | 2478 | } |
| 2492 | 2479 | ||
| 2493 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | 2480 | fn 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; | 2481 | 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; | 2482 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2496 | const lhs_ty = self.typeOf(bin_op.lhs); | 2483 | const lhs_ty = self.typeOf(bin_op.lhs); |
| ... | @@ -2505,25 +2492,25 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void | ... | @@ -2505,25 +2492,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 }); | 2492 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2506 | } | 2493 | } |
| 2507 | 2494 | ||
| 2508 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { | 2495 | fn airAddSat(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2509 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 2496 | 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}); | 2497 | 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 }); | 2498 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2512 | } | 2499 | } |
| 2513 | 2500 | ||
| 2514 | fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { | 2501 | fn airSubSat(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2515 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 2502 | 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}); | 2503 | 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 }); | 2504 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2518 | } | 2505 | } |
| 2519 | 2506 | ||
| 2520 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | 2507 | fn airMulSat(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2521 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 2508 | 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}); | 2509 | 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 }); | 2510 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2524 | } | 2511 | } |
| 2525 | 2512 | ||
| 2526 | fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | 2513 | fn airOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2527 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; | 2514 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 2528 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 2515 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2529 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2516 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| ... | @@ -2536,9 +2523,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2536,9 +2523,9 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2536 | const rhs_ty = self.typeOf(extra.rhs); | 2523 | const rhs_ty = self.typeOf(extra.rhs); |
| 2537 | 2524 | ||
| 2538 | const tuple_ty = self.typeOfIndex(inst); | 2525 | const tuple_ty = self.typeOfIndex(inst); |
| 2539 | const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu))); | 2526 | const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu)); |
| 2540 | const tuple_align = tuple_ty.abiAlignment(zcu); | 2527 | const tuple_align = tuple_ty.abiAlignment(zcu); |
| 2541 | const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu))); | 2528 | const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); |
| 2542 | 2529 | ||
| 2543 | switch (lhs_ty.zigTypeTag(zcu)) { | 2530 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 2544 | .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), | 2531 | .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), |
| ... | @@ -2652,7 +2639,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2652,7 +2639,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2652 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 2639 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2653 | } | 2640 | } |
| 2654 | 2641 | ||
| 2655 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 2642 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2656 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 2643 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2657 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2644 | 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 }); | 2645 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| ... | @@ -2876,7 +2863,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2876,7 +2863,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2876 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 2863 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2877 | } | 2864 | } |
| 2878 | 2865 | ||
| 2879 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 2866 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2880 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 2867 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2881 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2868 | 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 }); | 2869 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| ... | @@ -3012,13 +2999,13 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3012,13 +2999,13 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 3012 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 2999 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 3013 | } | 3000 | } |
| 3014 | 3001 | ||
| 3015 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { | 3002 | fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3016 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3003 | 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}); | 3004 | 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 }); | 3005 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3019 | } | 3006 | } |
| 3020 | 3007 | ||
| 3021 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 3008 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3022 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3009 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3023 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3010 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3024 | const optional_ty = self.typeOf(ty_op.operand); | 3011 | const optional_ty = self.typeOf(ty_op.operand); |
| ... | @@ -3055,13 +3042,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: | ... | @@ -3055,13 +3042,13 @@ fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: |
| 3055 | } | 3042 | } |
| 3056 | } | 3043 | } |
| 3057 | 3044 | ||
| 3058 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { | 3045 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3059 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3046 | 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}); | 3047 | 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 }); | 3048 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3062 | } | 3049 | } |
| 3063 | 3050 | ||
| 3064 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { | 3051 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3065 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3052 | 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}); | 3053 | 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 }); | 3054 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | @@ -3137,7 +3124,7 @@ fn errUnionErr( | ... | @@ -3137,7 +3124,7 @@ fn errUnionErr( |
| 3137 | } | 3124 | } |
| 3138 | } | 3125 | } |
| 3139 | 3126 | ||
| 3140 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | 3127 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3141 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3128 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3142 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3129 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3143 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; | 3130 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| ... | @@ -3218,7 +3205,7 @@ fn errUnionPayload( | ... | @@ -3218,7 +3205,7 @@ fn errUnionPayload( |
| 3218 | } | 3205 | } |
| 3219 | } | 3206 | } |
| 3220 | 3207 | ||
| 3221 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 3208 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3222 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3209 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3223 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3210 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3224 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; | 3211 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| ... | @@ -3230,26 +3217,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3230,26 +3217,26 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 3230 | } | 3217 | } |
| 3231 | 3218 | ||
| 3232 | // *(E!T) -> E | 3219 | // *(E!T) -> E |
| 3233 | fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void { | 3220 | fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3234 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3221 | 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}); | 3222 | 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 }); | 3223 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3237 | } | 3224 | } |
| 3238 | 3225 | ||
| 3239 | // *(E!T) -> *T | 3226 | // *(E!T) -> *T |
| 3240 | fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { | 3227 | fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3241 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3228 | 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}); | 3229 | 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 }); | 3230 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3244 | } | 3231 | } |
| 3245 | 3232 | ||
| 3246 | fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { | 3233 | fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3247 | 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; |
| 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}); | 3235 | 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 }); | 3236 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3250 | } | 3237 | } |
| 3251 | 3238 | ||
| 3252 | fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | 3239 | fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3253 | const result: MCValue = if (self.liveness.isUnused(inst)) | 3240 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 3254 | .dead | 3241 | .dead |
| 3255 | else | 3242 | else |
| ... | @@ -3257,17 +3244,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3257,17 +3244,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { |
| 3257 | return self.finishAir(inst, result, .{ .none, .none, .none }); | 3244 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 3258 | } | 3245 | } |
| 3259 | 3246 | ||
| 3260 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | 3247 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3261 | _ = inst; | 3248 | _ = inst; |
| 3262 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); | 3249 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); |
| 3263 | } | 3250 | } |
| 3264 | 3251 | ||
| 3265 | fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { | 3252 | fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3266 | _ = inst; | 3253 | _ = inst; |
| 3267 | return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch}); | 3254 | return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch}); |
| 3268 | } | 3255 | } |
| 3269 | 3256 | ||
| 3270 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { | 3257 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3271 | const pt = self.pt; | 3258 | const pt = self.pt; |
| 3272 | const zcu = pt.zcu; | 3259 | const zcu = pt.zcu; |
| 3273 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3260 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| ... | @@ -3313,7 +3300,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3313,7 +3300,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 3313 | } | 3300 | } |
| 3314 | 3301 | ||
| 3315 | /// T to E!T | 3302 | /// T to E!T |
| 3316 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { | 3303 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3317 | const pt = self.pt; | 3304 | const pt = self.pt; |
| 3318 | const zcu = pt.zcu; | 3305 | const zcu = pt.zcu; |
| 3319 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3306 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| ... | @@ -3338,7 +3325,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3338,7 +3325,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 3338 | } | 3325 | } |
| 3339 | 3326 | ||
| 3340 | /// E to E!T | 3327 | /// E to E!T |
| 3341 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { | 3328 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3342 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3329 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3343 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3330 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3344 | const pt = self.pt; | 3331 | const pt = self.pt; |
| ... | @@ -3379,7 +3366,7 @@ fn slicePtr(mcv: MCValue) MCValue { | ... | @@ -3379,7 +3366,7 @@ fn slicePtr(mcv: MCValue) MCValue { |
| 3379 | } | 3366 | } |
| 3380 | } | 3367 | } |
| 3381 | 3368 | ||
| 3382 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { | 3369 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3383 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3370 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3384 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3371 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3385 | const mcv = try self.resolveInst(ty_op.operand); | 3372 | const mcv = try self.resolveInst(ty_op.operand); |
| ... | @@ -3388,7 +3375,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3388,7 +3375,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3388 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3375 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3389 | } | 3376 | } |
| 3390 | 3377 | ||
| 3391 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | 3378 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3392 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3379 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3393 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3380 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3394 | const ptr_bits = 64; | 3381 | const ptr_bits = 64; |
| ... | @@ -3412,7 +3399,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3412,7 +3399,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 3412 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3399 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3413 | } | 3400 | } |
| 3414 | 3401 | ||
| 3415 | fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { | 3402 | fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3416 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3403 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3417 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3404 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3418 | const ptr_bits = 64; | 3405 | const ptr_bits = 64; |
| ... | @@ -3429,7 +3416,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3429,7 +3416,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3429 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3416 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3430 | } | 3417 | } |
| 3431 | 3418 | ||
| 3432 | fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { | 3419 | fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3433 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3420 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3434 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3421 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3435 | const mcv = try self.resolveInst(ty_op.operand); | 3422 | const mcv = try self.resolveInst(ty_op.operand); |
| ... | @@ -3444,7 +3431,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3444,7 +3431,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3444 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3431 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3445 | } | 3432 | } |
| 3446 | 3433 | ||
| 3447 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | 3434 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3448 | const pt = self.pt; | 3435 | const pt = self.pt; |
| 3449 | const zcu = pt.zcu; | 3436 | const zcu = pt.zcu; |
| 3450 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3437 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | @@ -3487,7 +3474,7 @@ fn ptrElemVal( | ... | @@ -3487,7 +3474,7 @@ fn ptrElemVal( |
| 3487 | } | 3474 | } |
| 3488 | } | 3475 | } |
| 3489 | 3476 | ||
| 3490 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { | 3477 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3491 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3478 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3492 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 3479 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3493 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3480 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | @@ -3506,13 +3493,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3506,13 +3493,13 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3506 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 3493 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 3507 | } | 3494 | } |
| 3508 | 3495 | ||
| 3509 | fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { | 3496 | fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3510 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3497 | 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}); | 3498 | 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 }); | 3499 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3513 | } | 3500 | } |
| 3514 | 3501 | ||
| 3515 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | 3502 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3516 | const pt = self.pt; | 3503 | const pt = self.pt; |
| 3517 | const zcu = pt.zcu; | 3504 | const zcu = pt.zcu; |
| 3518 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3505 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | @@ -3526,7 +3513,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3526,7 +3513,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3526 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 3513 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3527 | } | 3514 | } |
| 3528 | 3515 | ||
| 3529 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | 3516 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3530 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3517 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3531 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 3518 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3532 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3519 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | @@ -3542,55 +3529,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3542,55 +3529,55 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3542 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 3529 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 3543 | } | 3530 | } |
| 3544 | 3531 | ||
| 3545 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | 3532 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3546 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3533 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3547 | _ = bin_op; | 3534 | _ = bin_op; |
| 3548 | return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); | 3535 | return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); |
| 3549 | } | 3536 | } |
| 3550 | 3537 | ||
| 3551 | fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | 3538 | fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3552 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3539 | 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}); | 3540 | 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 }); | 3541 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3555 | } | 3542 | } |
| 3556 | 3543 | ||
| 3557 | fn airClz(self: *Self, inst: Air.Inst.Index) !void { | 3544 | fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3558 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3545 | 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}); | 3546 | 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 }); | 3547 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3561 | } | 3548 | } |
| 3562 | 3549 | ||
| 3563 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { | 3550 | fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3564 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3551 | 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}); | 3552 | 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 }); | 3553 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3567 | } | 3554 | } |
| 3568 | 3555 | ||
| 3569 | fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { | 3556 | fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3570 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3557 | 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}); | 3558 | 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 }); | 3559 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3573 | } | 3560 | } |
| 3574 | 3561 | ||
| 3575 | fn airAbs(self: *Self, inst: Air.Inst.Index) !void { | 3562 | fn airAbs(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3576 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3563 | 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}); | 3564 | 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 }); | 3565 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3579 | } | 3566 | } |
| 3580 | 3567 | ||
| 3581 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { | 3568 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3582 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3569 | 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}); | 3570 | 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 }); | 3571 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3585 | } | 3572 | } |
| 3586 | 3573 | ||
| 3587 | fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { | 3574 | fn airBitReverse(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3588 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3575 | 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}); | 3576 | 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 }); | 3577 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3591 | } | 3578 | } |
| 3592 | 3579 | ||
| 3593 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | 3580 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3594 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 3581 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 3595 | const result: MCValue = if (self.liveness.isUnused(inst)) | 3582 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 3596 | .dead | 3583 | .dead |
| ... | @@ -3885,7 +3872,7 @@ fn genInlineMemsetCode( | ... | @@ -3885,7 +3872,7 @@ fn genInlineMemsetCode( |
| 3885 | // end: | 3872 | // end: |
| 3886 | } | 3873 | } |
| 3887 | 3874 | ||
| 3888 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | 3875 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3889 | const pt = self.pt; | 3876 | const pt = self.pt; |
| 3890 | const zcu = pt.zcu; | 3877 | const zcu = pt.zcu; |
| 3891 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3878 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| ... | @@ -4086,7 +4073,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -4086,7 +4073,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 4086 | } | 4073 | } |
| 4087 | } | 4074 | } |
| 4088 | 4075 | ||
| 4089 | fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | 4076 | fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void { |
| 4090 | if (safety) { | 4077 | if (safety) { |
| 4091 | // TODO if the value is undef, write 0xaa bytes to dest | 4078 | // TODO if the value is undef, write 0xaa bytes to dest |
| 4092 | } else { | 4079 | } else { |
| ... | @@ -4103,14 +4090,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | ... | @@ -4103,14 +4090,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 }); | 4090 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 4104 | } | 4091 | } |
| 4105 | 4092 | ||
| 4106 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { | 4093 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4107 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4094 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4108 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; | 4095 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 4109 | const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); | 4096 | const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); |
| 4110 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); | 4097 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 4111 | } | 4098 | } |
| 4112 | 4099 | ||
| 4113 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { | 4100 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) InnerError!void { |
| 4114 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4101 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4115 | const result = try self.structFieldPtr(inst, ty_op.operand, index); | 4102 | const result = try self.structFieldPtr(inst, ty_op.operand, index); |
| 4116 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 4103 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | @@ -4138,7 +4125,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde | ... | @@ -4138,7 +4125,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 4138 | }; | 4125 | }; |
| 4139 | } | 4126 | } |
| 4140 | 4127 | ||
| 4141 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | 4128 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4142 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4129 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4143 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; | 4130 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 4144 | const operand = extra.struct_operand; | 4131 | const operand = extra.struct_operand; |
| ... | @@ -4194,7 +4181,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4194,7 +4181,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 4194 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); | 4181 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 4195 | } | 4182 | } |
| 4196 | 4183 | ||
| 4197 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | 4184 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4198 | const pt = self.pt; | 4185 | const pt = self.pt; |
| 4199 | const zcu = pt.zcu; | 4186 | const zcu = pt.zcu; |
| 4200 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4187 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| ... | @@ -4218,7 +4205,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4218,7 +4205,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4218 | return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); | 4205 | return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); |
| 4219 | } | 4206 | } |
| 4220 | 4207 | ||
| 4221 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { | 4208 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4222 | // skip zero-bit arguments as they don't have a corresponding arg instruction | 4209 | // skip zero-bit arguments as they don't have a corresponding arg instruction |
| 4223 | var arg_index = self.arg_index; | 4210 | var arg_index = self.arg_index; |
| 4224 | while (self.args[arg_index] == .none) arg_index += 1; | 4211 | while (self.args[arg_index] == .none) arg_index += 1; |
| ... | @@ -4238,7 +4225,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4238,7 +4225,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 4238 | return self.finishAir(inst, result, .{ .none, .none, .none }); | 4225 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 4239 | } | 4226 | } |
| 4240 | 4227 | ||
| 4241 | fn airTrap(self: *Self) !void { | 4228 | fn airTrap(self: *Self) InnerError!void { |
| 4242 | _ = try self.addInst(.{ | 4229 | _ = try self.addInst(.{ |
| 4243 | .tag = .brk, | 4230 | .tag = .brk, |
| 4244 | .data = .{ .imm16 = 0x0001 }, | 4231 | .data = .{ .imm16 = 0x0001 }, |
| ... | @@ -4246,7 +4233,7 @@ fn airTrap(self: *Self) !void { | ... | @@ -4246,7 +4233,7 @@ fn airTrap(self: *Self) !void { |
| 4246 | return self.finishAirBookkeeping(); | 4233 | return self.finishAirBookkeeping(); |
| 4247 | } | 4234 | } |
| 4248 | 4235 | ||
| 4249 | fn airBreakpoint(self: *Self) !void { | 4236 | fn airBreakpoint(self: *Self) InnerError!void { |
| 4250 | _ = try self.addInst(.{ | 4237 | _ = try self.addInst(.{ |
| 4251 | .tag = .brk, | 4238 | .tag = .brk, |
| 4252 | .data = .{ .imm16 = 0xf000 }, | 4239 | .data = .{ .imm16 = 0xf000 }, |
| ... | @@ -4254,17 +4241,17 @@ fn airBreakpoint(self: *Self) !void { | ... | @@ -4254,17 +4241,17 @@ fn airBreakpoint(self: *Self) !void { |
| 4254 | return self.finishAirBookkeeping(); | 4241 | return self.finishAirBookkeeping(); |
| 4255 | } | 4242 | } |
| 4256 | 4243 | ||
| 4257 | fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { | 4244 | fn 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", .{}); | 4245 | 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 }); | 4246 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 4260 | } | 4247 | } |
| 4261 | 4248 | ||
| 4262 | fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { | 4249 | fn 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", .{}); | 4250 | 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 }); | 4251 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 4265 | } | 4252 | } |
| 4266 | 4253 | ||
| 4267 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 4254 | fn 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", .{}); | 4255 | 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; | 4256 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4270 | const callee = pl_op.operand; | 4257 | const callee = pl_op.operand; |
| ... | @@ -4422,7 +4409,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4422,7 +4409,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4422 | return bt.finishAir(result); | 4409 | return bt.finishAir(result); |
| 4423 | } | 4410 | } |
| 4424 | 4411 | ||
| 4425 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { | 4412 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4426 | const pt = self.pt; | 4413 | const pt = self.pt; |
| 4427 | const zcu = pt.zcu; | 4414 | const zcu = pt.zcu; |
| 4428 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4415 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| ... | @@ -4455,7 +4442,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4455,7 +4442,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 4455 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); | 4442 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 4456 | } | 4443 | } |
| 4457 | 4444 | ||
| 4458 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | 4445 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4459 | const pt = self.pt; | 4446 | const pt = self.pt; |
| 4460 | const zcu = pt.zcu; | 4447 | const zcu = pt.zcu; |
| 4461 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4448 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| ... | @@ -4499,7 +4486,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4499,7 +4486,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 4499 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); | 4486 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 4500 | } | 4487 | } |
| 4501 | 4488 | ||
| 4502 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | 4489 | fn 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; | 4490 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4504 | const lhs_ty = self.typeOf(bin_op.lhs); | 4491 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 4505 | 4492 | ||
| ... | @@ -4597,12 +4584,12 @@ fn cmp( | ... | @@ -4597,12 +4584,12 @@ fn cmp( |
| 4597 | } | 4584 | } |
| 4598 | } | 4585 | } |
| 4599 | 4586 | ||
| 4600 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { | 4587 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4601 | _ = inst; | 4588 | _ = inst; |
| 4602 | return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); | 4589 | return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); |
| 4603 | } | 4590 | } |
| 4604 | 4591 | ||
| 4605 | fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { | 4592 | fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4606 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4593 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4607 | const operand = try self.resolveInst(un_op); | 4594 | const operand = try self.resolveInst(un_op); |
| 4608 | _ = operand; | 4595 | _ = operand; |
| ... | @@ -4610,7 +4597,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4610,7 +4597,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { |
| 4610 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4597 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4611 | } | 4598 | } |
| 4612 | 4599 | ||
| 4613 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { | 4600 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4614 | const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; | 4601 | const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; |
| 4615 | 4602 | ||
| 4616 | _ = try self.addInst(.{ | 4603 | _ = try self.addInst(.{ |
| ... | @@ -4624,7 +4611,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4624,7 +4611,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 4624 | return self.finishAirBookkeeping(); | 4611 | return self.finishAirBookkeeping(); |
| 4625 | } | 4612 | } |
| 4626 | 4613 | ||
| 4627 | fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { | 4614 | fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4628 | const pt = self.pt; | 4615 | const pt = self.pt; |
| 4629 | const zcu = pt.zcu; | 4616 | const zcu = pt.zcu; |
| 4630 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4617 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| ... | @@ -4635,7 +4622,7 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4635,7 +4622,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])); | 4622 | try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); |
| 4636 | } | 4623 | } |
| 4637 | 4624 | ||
| 4638 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { | 4625 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4639 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 4626 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4640 | const operand = pl_op.operand; | 4627 | const operand = pl_op.operand; |
| 4641 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; | 4628 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| ... | @@ -4686,7 +4673,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { | ... | @@ -4686,7 +4673,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { |
| 4686 | } | 4673 | } |
| 4687 | } | 4674 | } |
| 4688 | 4675 | ||
| 4689 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | 4676 | fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4690 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 4677 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4691 | const cond = try self.resolveInst(pl_op.operand); | 4678 | const cond = try self.resolveInst(pl_op.operand); |
| 4692 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); | 4679 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| ... | @@ -4919,7 +4906,7 @@ fn isNonErr( | ... | @@ -4919,7 +4906,7 @@ fn isNonErr( |
| 4919 | } | 4906 | } |
| 4920 | } | 4907 | } |
| 4921 | 4908 | ||
| 4922 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | 4909 | fn airIsNull(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4923 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4910 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4924 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4911 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4925 | const operand = try self.resolveInst(un_op); | 4912 | const operand = try self.resolveInst(un_op); |
| ... | @@ -4930,7 +4917,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4930,7 +4917,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4930 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4917 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4931 | } | 4918 | } |
| 4932 | 4919 | ||
| 4933 | fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | 4920 | fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4934 | const pt = self.pt; | 4921 | const pt = self.pt; |
| 4935 | const zcu = pt.zcu; | 4922 | const zcu = pt.zcu; |
| 4936 | 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; |
| ... | @@ -4947,7 +4934,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4947,7 +4934,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4947 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4934 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4948 | } | 4935 | } |
| 4949 | 4936 | ||
| 4950 | fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { | 4937 | fn airIsNonNull(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4951 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4938 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4952 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4939 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4953 | const operand = try self.resolveInst(un_op); | 4940 | const operand = try self.resolveInst(un_op); |
| ... | @@ -4958,7 +4945,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4958,7 +4945,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4958 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4945 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4959 | } | 4946 | } |
| 4960 | 4947 | ||
| 4961 | fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | 4948 | fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4962 | const pt = self.pt; | 4949 | const pt = self.pt; |
| 4963 | const zcu = pt.zcu; | 4950 | const zcu = pt.zcu; |
| 4964 | 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; |
| ... | @@ -4975,7 +4962,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4975,7 +4962,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4975 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4962 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4976 | } | 4963 | } |
| 4977 | 4964 | ||
| 4978 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | 4965 | fn airIsErr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4979 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4966 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4980 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4967 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4981 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; | 4968 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| ... | @@ -4986,7 +4973,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4986,7 +4973,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4986 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4973 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4987 | } | 4974 | } |
| 4988 | 4975 | ||
| 4989 | fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | 4976 | fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 4990 | const pt = self.pt; | 4977 | const pt = self.pt; |
| 4991 | const zcu = pt.zcu; | 4978 | const zcu = pt.zcu; |
| 4992 | 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; |
| ... | @@ -5003,7 +4990,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5003,7 +4990,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 5003 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4990 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 5004 | } | 4991 | } |
| 5005 | 4992 | ||
| 5006 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { | 4993 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5007 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4994 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5008 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4995 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5009 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; | 4996 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| ... | @@ -5014,7 +5001,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5014,7 +5001,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 5014 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 5001 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 5015 | } | 5002 | } |
| 5016 | 5003 | ||
| 5017 | fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | 5004 | fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5018 | const pt = self.pt; | 5005 | const pt = self.pt; |
| 5019 | const zcu = pt.zcu; | 5006 | const zcu = pt.zcu; |
| 5020 | 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; |
| ... | @@ -5031,7 +5018,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5031,7 +5018,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 5031 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 5018 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 5032 | } | 5019 | } |
| 5033 | 5020 | ||
| 5034 | fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | 5021 | fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5035 | // A loop is a setup to be able to jump back to the beginning. | 5022 | // 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; | 5023 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5037 | const loop = self.air.extraData(Air.Block, ty_pl.payload); | 5024 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| ... | @@ -5052,7 +5039,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void { | ... | @@ -5052,7 +5039,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void { |
| 5052 | }); | 5039 | }); |
| 5053 | } | 5040 | } |
| 5054 | 5041 | ||
| 5055 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { | 5042 | fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5056 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5043 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5057 | const extra = self.air.extraData(Air.Block, ty_pl.payload); | 5044 | 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])); | 5045 | try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); |
| ... | @@ -5090,7 +5077,7 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! | ... | @@ -5090,7 +5077,7 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! |
| 5090 | return self.finishAir(inst, result, .{ .none, .none, .none }); | 5077 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 5091 | } | 5078 | } |
| 5092 | 5079 | ||
| 5093 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | 5080 | fn airSwitch(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5094 | const switch_br = self.air.unwrapSwitch(inst); | 5081 | const switch_br = self.air.unwrapSwitch(inst); |
| 5095 | const condition_ty = self.typeOf(switch_br.operand); | 5082 | const condition_ty = self.typeOf(switch_br.operand); |
| 5096 | const liveness = try self.liveness.getSwitchBr( | 5083 | const liveness = try self.liveness.getSwitchBr( |
| ... | @@ -5224,7 +5211,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { | ... | @@ -5224,7 +5211,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |
| 5224 | } | 5211 | } |
| 5225 | } | 5212 | } |
| 5226 | 5213 | ||
| 5227 | fn airBr(self: *Self, inst: Air.Inst.Index) !void { | 5214 | fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5228 | const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; | 5215 | const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; |
| 5229 | try self.br(branch.block_inst, branch.operand); | 5216 | try self.br(branch.block_inst, branch.operand); |
| 5230 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); | 5217 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| ... | @@ -5268,7 +5255,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -5268,7 +5255,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 5268 | })); | 5255 | })); |
| 5269 | } | 5256 | } |
| 5270 | 5257 | ||
| 5271 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 5258 | fn airAsm(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5272 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5259 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5273 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); | 5260 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 5274 | const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; | 5261 | const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; |
| ... | @@ -5601,7 +5588,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5601,7 +5588,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5601 | .tag = .ldr_ptr_stack, | 5588 | .tag = .ldr_ptr_stack, |
| 5602 | .data = .{ .load_store_stack = .{ | 5589 | .data = .{ .load_store_stack = .{ |
| 5603 | .rt = reg, | 5590 | .rt = reg, |
| 5604 | .offset = @as(u32, @intCast(off)), | 5591 | .offset = @intCast(off), |
| 5605 | } }, | 5592 | } }, |
| 5606 | }); | 5593 | }); |
| 5607 | }, | 5594 | }, |
| ... | @@ -5617,13 +5604,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5617,13 +5604,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5617 | .immediate => |x| { | 5604 | .immediate => |x| { |
| 5618 | _ = try self.addInst(.{ | 5605 | _ = try self.addInst(.{ |
| 5619 | .tag = .movz, | 5606 | .tag = .movz, |
| 5620 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x)) } }, | 5607 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x) } }, |
| 5621 | }); | 5608 | }); |
| 5622 | 5609 | ||
| 5623 | if (x & 0x0000_0000_ffff_0000 != 0) { | 5610 | if (x & 0x0000_0000_ffff_0000 != 0) { |
| 5624 | _ = try self.addInst(.{ | 5611 | _ = try self.addInst(.{ |
| 5625 | .tag = .movk, | 5612 | .tag = .movk, |
| 5626 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 16)), .hw = 1 } }, | 5613 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 16), .hw = 1 } }, |
| 5627 | }); | 5614 | }); |
| 5628 | } | 5615 | } |
| 5629 | 5616 | ||
| ... | @@ -5631,13 +5618,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5631,13 +5618,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5631 | if (x & 0x0000_ffff_0000_0000 != 0) { | 5618 | if (x & 0x0000_ffff_0000_0000 != 0) { |
| 5632 | _ = try self.addInst(.{ | 5619 | _ = try self.addInst(.{ |
| 5633 | .tag = .movk, | 5620 | .tag = .movk, |
| 5634 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 32)), .hw = 2 } }, | 5621 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 32), .hw = 2 } }, |
| 5635 | }); | 5622 | }); |
| 5636 | } | 5623 | } |
| 5637 | if (x & 0xffff_0000_0000_0000 != 0) { | 5624 | if (x & 0xffff_0000_0000_0000 != 0) { |
| 5638 | _ = try self.addInst(.{ | 5625 | _ = try self.addInst(.{ |
| 5639 | .tag = .movk, | 5626 | .tag = .movk, |
| 5640 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @as(u16, @truncate(x >> 48)), .hw = 3 } }, | 5627 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 48), .hw = 3 } }, |
| 5641 | }); | 5628 | }); |
| 5642 | } | 5629 | } |
| 5643 | } | 5630 | } |
| ... | @@ -5709,7 +5696,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5709,7 +5696,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5709 | .tag = tag, | 5696 | .tag = tag, |
| 5710 | .data = .{ .load_store_stack = .{ | 5697 | .data = .{ .load_store_stack = .{ |
| 5711 | .rt = reg, | 5698 | .rt = reg, |
| 5712 | .offset = @as(u32, @intCast(off)), | 5699 | .offset = @intCast(off), |
| 5713 | } }, | 5700 | } }, |
| 5714 | }); | 5701 | }); |
| 5715 | }, | 5702 | }, |
| ... | @@ -5733,7 +5720,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5733,7 +5720,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5733 | .tag = tag, | 5720 | .tag = tag, |
| 5734 | .data = .{ .load_store_stack = .{ | 5721 | .data = .{ .load_store_stack = .{ |
| 5735 | .rt = reg, | 5722 | .rt = reg, |
| 5736 | .offset = @as(u32, @intCast(off)), | 5723 | .offset = @intCast(off), |
| 5737 | } }, | 5724 | } }, |
| 5738 | }); | 5725 | }); |
| 5739 | }, | 5726 | }, |
| ... | @@ -5918,13 +5905,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I | ... | @@ -5918,13 +5905,13 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 5918 | } | 5905 | } |
| 5919 | } | 5906 | } |
| 5920 | 5907 | ||
| 5921 | fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void { | 5908 | fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5922 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 5909 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5923 | const result = try self.resolveInst(un_op); | 5910 | const result = try self.resolveInst(un_op); |
| 5924 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 5911 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 5925 | } | 5912 | } |
| 5926 | 5913 | ||
| 5927 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | 5914 | fn airBitCast(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5928 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5915 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5929 | const result = if (self.liveness.isUnused(inst)) .dead else result: { | 5916 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5930 | const operand = try self.resolveInst(ty_op.operand); | 5917 | const operand = try self.resolveInst(ty_op.operand); |
| ... | @@ -5945,7 +5932,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5945,7 +5932,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 5945 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5932 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5946 | } | 5933 | } |
| 5947 | 5934 | ||
| 5948 | fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { | 5935 | fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5949 | const pt = self.pt; | 5936 | const pt = self.pt; |
| 5950 | const zcu = pt.zcu; | 5937 | const zcu = pt.zcu; |
| 5951 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5938 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| ... | @@ -5963,7 +5950,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5963,7 +5950,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 5963 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5950 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5964 | } | 5951 | } |
| 5965 | 5952 | ||
| 5966 | fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { | 5953 | fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5967 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5954 | 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 {}", .{ | 5955 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{ |
| 5969 | self.target.cpu.arch, | 5956 | self.target.cpu.arch, |
| ... | @@ -5971,7 +5958,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5971,7 +5958,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { |
| 5971 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5958 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5972 | } | 5959 | } |
| 5973 | 5960 | ||
| 5974 | fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { | 5961 | fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5975 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5962 | 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 {}", .{ | 5963 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{ |
| 5977 | self.target.cpu.arch, | 5964 | self.target.cpu.arch, |
| ... | @@ -5979,7 +5966,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5979,7 +5966,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 5979 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5966 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5980 | } | 5967 | } |
| 5981 | 5968 | ||
| 5982 | fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { | 5969 | fn airCmpxchg(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5983 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5970 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5984 | const extra = self.air.extraData(Air.Block, ty_pl.payload); | 5971 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 5985 | _ = extra; | 5972 | _ = extra; |
| ... | @@ -5989,23 +5976,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5989,23 +5976,23 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 5989 | }); | 5976 | }); |
| 5990 | } | 5977 | } |
| 5991 | 5978 | ||
| 5992 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { | 5979 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5993 | _ = inst; | 5980 | _ = inst; |
| 5994 | return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); | 5981 | return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); |
| 5995 | } | 5982 | } |
| 5996 | 5983 | ||
| 5997 | fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { | 5984 | fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5998 | _ = inst; | 5985 | _ = inst; |
| 5999 | return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); | 5986 | return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); |
| 6000 | } | 5987 | } |
| 6001 | 5988 | ||
| 6002 | fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { | 5989 | fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) InnerError!void { |
| 6003 | _ = inst; | 5990 | _ = inst; |
| 6004 | _ = order; | 5991 | _ = order; |
| 6005 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); | 5992 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 6006 | } | 5993 | } |
| 6007 | 5994 | ||
| 6008 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | 5995 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void { |
| 6009 | _ = inst; | 5996 | _ = inst; |
| 6010 | if (safety) { | 5997 | if (safety) { |
| 6011 | // TODO if the value is undef, write 0xaa bytes to dest | 5998 | // TODO if the value is undef, write 0xaa bytes to dest |
| ... | @@ -6015,12 +6002,12 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | ... | @@ -6015,12 +6002,12 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 6015 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | 6002 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 6016 | } | 6003 | } |
| 6017 | 6004 | ||
| 6018 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | 6005 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6019 | _ = inst; | 6006 | _ = inst; |
| 6020 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); | 6007 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); |
| 6021 | } | 6008 | } |
| 6022 | 6009 | ||
| 6023 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { | 6010 | fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6024 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 6011 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 6025 | const operand = try self.resolveInst(un_op); | 6012 | const operand = try self.resolveInst(un_op); |
| 6026 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | 6013 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { |
| ... | @@ -6030,7 +6017,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6030,7 +6017,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 6030 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 6017 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 6031 | } | 6018 | } |
| 6032 | 6019 | ||
| 6033 | fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { | 6020 | fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6034 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 6021 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 6035 | const operand = try self.resolveInst(un_op); | 6022 | const operand = try self.resolveInst(un_op); |
| 6036 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | 6023 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { |
| ... | @@ -6040,33 +6027,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6040,33 +6027,33 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { |
| 6040 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 6027 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 6041 | } | 6028 | } |
| 6042 | 6029 | ||
| 6043 | fn airSplat(self: *Self, inst: Air.Inst.Index) !void { | 6030 | fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6044 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 6031 | 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}); | 6032 | 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 }); | 6033 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 6047 | } | 6034 | } |
| 6048 | 6035 | ||
| 6049 | fn airSelect(self: *Self, inst: Air.Inst.Index) !void { | 6036 | fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6050 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 6037 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6051 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | 6038 | 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}); | 6039 | 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 }); | 6040 | return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); |
| 6054 | } | 6041 | } |
| 6055 | 6042 | ||
| 6056 | fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | 6043 | fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6057 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 6044 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6058 | const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; | 6045 | 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}); | 6046 | 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 }); | 6047 | return self.finishAir(inst, result, .{ extra.a, extra.b, .none }); |
| 6061 | } | 6048 | } |
| 6062 | 6049 | ||
| 6063 | fn airReduce(self: *Self, inst: Air.Inst.Index) !void { | 6050 | fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6064 | const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; | 6051 | 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", .{}); | 6052 | 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 }); | 6053 | return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); |
| 6067 | } | 6054 | } |
| 6068 | 6055 | ||
| 6069 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | 6056 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6070 | const pt = self.pt; | 6057 | const pt = self.pt; |
| 6071 | const zcu = pt.zcu; | 6058 | const zcu = pt.zcu; |
| 6072 | const vector_ty = self.typeOfIndex(inst); | 6059 | const vector_ty = self.typeOfIndex(inst); |
| ... | @@ -6090,19 +6077,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6090,19 +6077,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 6090 | return bt.finishAir(result); | 6077 | return bt.finishAir(result); |
| 6091 | } | 6078 | } |
| 6092 | 6079 | ||
| 6093 | fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { | 6080 | fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6094 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 6081 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6095 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; | 6082 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; |
| 6096 | _ = extra; | 6083 | _ = extra; |
| 6097 | return self.fail("TODO implement airUnionInit for aarch64", .{}); | 6084 | return self.fail("TODO implement airUnionInit for aarch64", .{}); |
| 6098 | } | 6085 | } |
| 6099 | 6086 | ||
| 6100 | fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { | 6087 | fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6101 | const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; | 6088 | const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; |
| 6102 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); | 6089 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); |
| 6103 | } | 6090 | } |
| 6104 | 6091 | ||
| 6105 | fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | 6092 | fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6106 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 6093 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6107 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | 6094 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; |
| 6108 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | 6095 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { |
| ... | @@ -6111,7 +6098,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6111,7 +6098,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 6111 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); | 6098 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); |
| 6112 | } | 6099 | } |
| 6113 | 6100 | ||
| 6114 | fn airTry(self: *Self, inst: Air.Inst.Index) !void { | 6101 | fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6115 | const pt = self.pt; | 6102 | const pt = self.pt; |
| 6116 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 6103 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6117 | const extra = self.air.extraData(Air.Try, pl_op.payload); | 6104 | const extra = self.air.extraData(Air.Try, pl_op.payload); |
| ... | @@ -6139,7 +6126,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6139,7 +6126,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { |
| 6139 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); | 6126 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); |
| 6140 | } | 6127 | } |
| 6141 | 6128 | ||
| 6142 | fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { | 6129 | fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 6143 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 6130 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6144 | const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); | 6131 | const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); |
| 6145 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; | 6132 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| ... | @@ -6191,10 +6178,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { | ... | @@ -6191,10 +6178,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { |
| 6191 | .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, | 6178 | .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, |
| 6192 | .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO | 6179 | .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO |
| 6193 | }, | 6180 | }, |
| 6194 | .fail => |msg| { | 6181 | .fail => |msg| return self.failMsg(msg), |
| 6195 | self.err_msg = msg; | ||
| 6196 | return error.CodegenFail; | ||
| 6197 | }, | ||
| 6198 | }; | 6182 | }; |
| 6199 | return mcv; | 6183 | return mcv; |
| 6200 | } | 6184 | } |
| ... | @@ -6355,18 +6339,14 @@ fn wantSafety(self: *Self) bool { | ... | @@ -6355,18 +6339,14 @@ fn wantSafety(self: *Self) bool { |
| 6355 | }; | 6339 | }; |
| 6356 | } | 6340 | } |
| 6357 | 6341 | ||
| 6358 | fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { | 6342 | fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 6359 | @branchHint(.cold); | 6343 | @branchHint(.cold); |
| 6360 | assert(self.err_msg == null); | 6344 | return self.pt.zcu.codegenFail(self.owner_nav, format, args); |
| 6361 | self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); | ||
| 6362 | return error.CodegenFail; | ||
| 6363 | } | 6345 | } |
| 6364 | 6346 | ||
| 6365 | fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { | 6347 | fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { |
| 6366 | @branchHint(.cold); | 6348 | @branchHint(.cold); |
| 6367 | assert(self.err_msg == null); | 6349 | return self.pt.zcu.codegenFailMsg(self.owner_nav, msg); |
| 6368 | self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); | ||
| 6369 | return error.CodegenFail; | ||
| 6370 | } | 6350 | } |
| 6371 | 6351 | ||
| 6372 | fn parseRegName(name: []const u8) ?Register { | 6352 | fn parseRegName(name: []const u8) ?Register { |
src/arch/aarch64/Emit.zig+4-2| ... | @@ -20,7 +20,7 @@ debug_output: link.File.DebugInfoOutput, | ... | @@ -20,7 +20,7 @@ debug_output: link.File.DebugInfoOutput, |
| 20 | target: *const std.Target, | 20 | target: *const std.Target, |
| 21 | err_msg: ?*ErrorMsg = null, | 21 | err_msg: ?*ErrorMsg = null, |
| 22 | src_loc: Zcu.LazySrcLoc, | 22 | src_loc: Zcu.LazySrcLoc, |
| 23 | code: *std.ArrayList(u8), | 23 | code: *std.ArrayListUnmanaged(u8), |
| 24 | 24 | ||
| 25 | prev_di_line: u32, | 25 | prev_di_line: u32, |
| 26 | prev_di_column: u32, | 26 | prev_di_column: u32, |
| ... | @@ -424,8 +424,10 @@ fn lowerBranches(emit: *Emit) !void { | ... | @@ -424,8 +424,10 @@ fn lowerBranches(emit: *Emit) !void { |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { | 426 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 427 | const comp = emit.bin_file.comp; | ||
| 428 | const gpa = comp.gpa; | ||
| 427 | const endian = emit.target.cpu.arch.endian(); | 429 | const endian = emit.target.cpu.arch.endian(); |
| 428 | std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian); | 430 | std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian); |
| 429 | } | 431 | } |
| 430 | 432 | ||
| 431 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { | 433 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
src/arch/arm/CodeGen.zig+17-29| ... | @@ -23,7 +23,6 @@ const log = std.log.scoped(.codegen); | ... | @@ -23,7 +23,6 @@ const log = std.log.scoped(.codegen); |
| 23 | const build_options = @import("build_options"); | 23 | const build_options = @import("build_options"); |
| 24 | const Alignment = InternPool.Alignment; | 24 | const Alignment = InternPool.Alignment; |
| 25 | 25 | ||
| 26 | const Result = codegen.Result; | ||
| 27 | const CodeGenError = codegen.CodeGenError; | 26 | const CodeGenError = codegen.CodeGenError; |
| 28 | 27 | ||
| 29 | const bits = @import("bits.zig"); | 28 | const bits = @import("bits.zig"); |
| ... | @@ -333,9 +332,9 @@ pub fn generate( | ... | @@ -333,9 +332,9 @@ pub fn generate( |
| 333 | func_index: InternPool.Index, | 332 | func_index: InternPool.Index, |
| 334 | air: Air, | 333 | air: Air, |
| 335 | liveness: Liveness, | 334 | liveness: Liveness, |
| 336 | code: *std.ArrayList(u8), | 335 | code: *std.ArrayListUnmanaged(u8), |
| 337 | debug_output: link.File.DebugInfoOutput, | 336 | debug_output: link.File.DebugInfoOutput, |
| 338 | ) CodeGenError!Result { | 337 | ) CodeGenError!void { |
| 339 | const zcu = pt.zcu; | 338 | const zcu = pt.zcu; |
| 340 | const gpa = zcu.gpa; | 339 | const gpa = zcu.gpa; |
| 341 | const func = zcu.funcInfo(func_index); | 340 | const func = zcu.funcInfo(func_index); |
| ... | @@ -377,10 +376,7 @@ pub fn generate( | ... | @@ -377,10 +376,7 @@ pub fn generate( |
| 377 | defer function.dbg_info_relocs.deinit(gpa); | 376 | defer function.dbg_info_relocs.deinit(gpa); |
| 378 | 377 | ||
| 379 | var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) { | 378 | var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) { |
| 380 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 379 | error.CodegenFail => return error.CodegenFail, |
| 381 | error.OutOfRegisters => return Result{ | ||
| 382 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 383 | }, | ||
| 384 | else => |e| return e, | 380 | else => |e| return e, |
| 385 | }; | 381 | }; |
| 386 | defer call_info.deinit(&function); | 382 | defer call_info.deinit(&function); |
| ... | @@ -391,15 +387,14 @@ pub fn generate( | ... | @@ -391,15 +387,14 @@ pub fn generate( |
| 391 | function.max_end_stack = call_info.stack_byte_count; | 387 | function.max_end_stack = call_info.stack_byte_count; |
| 392 | 388 | ||
| 393 | function.gen() catch |err| switch (err) { | 389 | function.gen() catch |err| switch (err) { |
| 394 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 390 | error.CodegenFail => return error.CodegenFail, |
| 395 | error.OutOfRegisters => return Result{ | 391 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 396 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 397 | }, | ||
| 398 | else => |e| return e, | 392 | else => |e| return e, |
| 399 | }; | 393 | }; |
| 400 | 394 | ||
| 401 | for (function.dbg_info_relocs.items) |reloc| { | 395 | for (function.dbg_info_relocs.items) |reloc| { |
| 402 | try reloc.genDbgInfo(function); | 396 | reloc.genDbgInfo(function) catch |err| |
| 397 | return function.fail("failed to generate debug info: {s}", .{@errorName(err)}); | ||
| 403 | } | 398 | } |
| 404 | 399 | ||
| 405 | var mir = Mir{ | 400 | var mir = Mir{ |
| ... | @@ -424,15 +419,9 @@ pub fn generate( | ... | @@ -424,15 +419,9 @@ pub fn generate( |
| 424 | defer emit.deinit(); | 419 | defer emit.deinit(); |
| 425 | 420 | ||
| 426 | emit.emitMir() catch |err| switch (err) { | 421 | emit.emitMir() catch |err| switch (err) { |
| 427 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | 422 | error.EmitFail => return function.failMsg(emit.err_msg.?), |
| 428 | else => |e| return e, | 423 | else => |e| return e, |
| 429 | }; | 424 | }; |
| 430 | |||
| 431 | if (function.err_msg) |em| { | ||
| 432 | return Result{ .fail = em }; | ||
| 433 | } else { | ||
| 434 | return Result.ok; | ||
| 435 | } | ||
| 436 | } | 425 | } |
| 437 | 426 | ||
| 438 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { | 427 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| ... | @@ -6310,20 +6299,19 @@ fn wantSafety(self: *Self) bool { | ... | @@ -6310,20 +6299,19 @@ fn wantSafety(self: *Self) bool { |
| 6310 | }; | 6299 | }; |
| 6311 | } | 6300 | } |
| 6312 | 6301 | ||
| 6313 | fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { | 6302 | fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 6314 | @branchHint(.cold); | 6303 | @branchHint(.cold); |
| 6315 | assert(self.err_msg == null); | 6304 | const zcu = self.pt.zcu; |
| 6316 | const gpa = self.gpa; | 6305 | const func = zcu.funcInfo(self.func_index); |
| 6317 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | 6306 | const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args); |
| 6318 | return error.CodegenFail; | 6307 | return zcu.codegenFailMsg(func.owner_nav, msg); |
| 6319 | } | 6308 | } |
| 6320 | 6309 | ||
| 6321 | fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { | 6310 | fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { |
| 6322 | @branchHint(.cold); | 6311 | @branchHint(.cold); |
| 6323 | assert(self.err_msg == null); | 6312 | const zcu = self.pt.zcu; |
| 6324 | const gpa = self.gpa; | 6313 | const func = zcu.funcInfo(self.func_index); |
| 6325 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | 6314 | return zcu.codegenFailMsg(func.owner_nav, msg); |
| 6326 | return error.CodegenFail; | ||
| 6327 | } | 6315 | } |
| 6328 | 6316 | ||
| 6329 | fn parseRegName(name: []const u8) ?Register { | 6317 | fn parseRegName(name: []const u8) ?Register { |
src/arch/arm/Emit.zig+4-2| ... | @@ -24,7 +24,7 @@ debug_output: link.File.DebugInfoOutput, | ... | @@ -24,7 +24,7 @@ debug_output: link.File.DebugInfoOutput, |
| 24 | target: *const std.Target, | 24 | target: *const std.Target, |
| 25 | err_msg: ?*ErrorMsg = null, | 25 | err_msg: ?*ErrorMsg = null, |
| 26 | src_loc: Zcu.LazySrcLoc, | 26 | src_loc: Zcu.LazySrcLoc, |
| 27 | code: *std.ArrayList(u8), | 27 | code: *std.ArrayListUnmanaged(u8), |
| 28 | 28 | ||
| 29 | prev_di_line: u32, | 29 | prev_di_line: u32, |
| 30 | prev_di_column: u32, | 30 | prev_di_column: u32, |
| ... | @@ -342,8 +342,10 @@ fn lowerBranches(emit: *Emit) !void { | ... | @@ -342,8 +342,10 @@ fn lowerBranches(emit: *Emit) !void { |
| 342 | } | 342 | } |
| 343 | 343 | ||
| 344 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { | 344 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 345 | const comp = emit.bin_file.comp; | ||
| 346 | const gpa = comp.gpa; | ||
| 345 | const endian = emit.target.cpu.arch.endian(); | 347 | const endian = emit.target.cpu.arch.endian(); |
| 346 | std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian); | 348 | std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian); |
| 347 | } | 349 | } |
| 348 | 350 | ||
| 349 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { | 351 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
src/arch/riscv64/CodeGen.zig+33-76| ... | @@ -32,7 +32,6 @@ const wip_mir_log = std.log.scoped(.wip_mir); | ... | @@ -32,7 +32,6 @@ const wip_mir_log = std.log.scoped(.wip_mir); |
| 32 | const Alignment = InternPool.Alignment; | 32 | const Alignment = InternPool.Alignment; |
| 33 | 33 | ||
| 34 | const CodeGenError = codegen.CodeGenError; | 34 | const CodeGenError = codegen.CodeGenError; |
| 35 | const Result = codegen.Result; | ||
| 36 | 35 | ||
| 37 | const bits = @import("bits.zig"); | 36 | const bits = @import("bits.zig"); |
| 38 | const abi = @import("abi.zig"); | 37 | const abi = @import("abi.zig"); |
| ... | @@ -62,7 +61,6 @@ gpa: Allocator, | ... | @@ -62,7 +61,6 @@ gpa: Allocator, |
| 62 | mod: *Package.Module, | 61 | mod: *Package.Module, |
| 63 | target: *const std.Target, | 62 | target: *const std.Target, |
| 64 | debug_output: link.File.DebugInfoOutput, | 63 | debug_output: link.File.DebugInfoOutput, |
| 65 | err_msg: ?*ErrorMsg, | ||
| 66 | args: []MCValue, | 64 | args: []MCValue, |
| 67 | ret_mcv: InstTracking, | 65 | ret_mcv: InstTracking, |
| 68 | fn_type: Type, | 66 | fn_type: Type, |
| ... | @@ -759,9 +757,9 @@ pub fn generate( | ... | @@ -759,9 +757,9 @@ pub fn generate( |
| 759 | func_index: InternPool.Index, | 757 | func_index: InternPool.Index, |
| 760 | air: Air, | 758 | air: Air, |
| 761 | liveness: Liveness, | 759 | liveness: Liveness, |
| 762 | code: *std.ArrayList(u8), | 760 | code: *std.ArrayListUnmanaged(u8), |
| 763 | debug_output: link.File.DebugInfoOutput, | 761 | debug_output: link.File.DebugInfoOutput, |
| 764 | ) CodeGenError!Result { | 762 | ) CodeGenError!void { |
| 765 | const zcu = pt.zcu; | 763 | const zcu = pt.zcu; |
| 766 | const comp = zcu.comp; | 764 | const comp = zcu.comp; |
| 767 | const gpa = zcu.gpa; | 765 | const gpa = zcu.gpa; |
| ... | @@ -788,7 +786,6 @@ pub fn generate( | ... | @@ -788,7 +786,6 @@ pub fn generate( |
| 788 | .target = &mod.resolved_target.result, | 786 | .target = &mod.resolved_target.result, |
| 789 | .debug_output = debug_output, | 787 | .debug_output = debug_output, |
| 790 | .owner = .{ .nav_index = func.owner_nav }, | 788 | .owner = .{ .nav_index = func.owner_nav }, |
| 791 | .err_msg = null, | ||
| 792 | .args = undefined, // populated after `resolveCallingConventionValues` | 789 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 793 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` | 790 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 794 | .fn_type = fn_type, | 791 | .fn_type = fn_type, |
| ... | @@ -829,10 +826,7 @@ pub fn generate( | ... | @@ -829,10 +826,7 @@ pub fn generate( |
| 829 | 826 | ||
| 830 | const fn_info = zcu.typeToFunc(fn_type).?; | 827 | const fn_info = zcu.typeToFunc(fn_type).?; |
| 831 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) { | 828 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) { |
| 832 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 829 | error.CodegenFail => return error.CodegenFail, |
| 833 | error.OutOfRegisters => return Result{ | ||
| 834 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 835 | }, | ||
| 836 | else => |e| return e, | 830 | else => |e| return e, |
| 837 | }; | 831 | }; |
| 838 | 832 | ||
| ... | @@ -861,10 +855,8 @@ pub fn generate( | ... | @@ -861,10 +855,8 @@ pub fn generate( |
| 861 | })); | 855 | })); |
| 862 | 856 | ||
| 863 | function.gen() catch |err| switch (err) { | 857 | function.gen() catch |err| switch (err) { |
| 864 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 858 | error.CodegenFail => return error.CodegenFail, |
| 865 | error.OutOfRegisters => return Result{ | 859 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 866 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 867 | }, | ||
| 868 | else => |e| return e, | 860 | else => |e| return e, |
| 869 | }; | 861 | }; |
| 870 | 862 | ||
| ... | @@ -895,28 +887,10 @@ pub fn generate( | ... | @@ -895,28 +887,10 @@ pub fn generate( |
| 895 | defer emit.deinit(); | 887 | defer emit.deinit(); |
| 896 | 888 | ||
| 897 | emit.emitMir() catch |err| switch (err) { | 889 | emit.emitMir() catch |err| switch (err) { |
| 898 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | 890 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), |
| 899 | error.InvalidInstruction => |e| { | 891 | error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), |
| 900 | const msg = switch (e) { | ||
| 901 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | ||
| 902 | }; | ||
| 903 | return Result{ | ||
| 904 | .fail = try ErrorMsg.create( | ||
| 905 | gpa, | ||
| 906 | src_loc, | ||
| 907 | "{s} This is a bug in the Zig compiler.", | ||
| 908 | .{msg}, | ||
| 909 | ), | ||
| 910 | }; | ||
| 911 | }, | ||
| 912 | else => |e| return e, | 892 | else => |e| return e, |
| 913 | }; | 893 | }; |
| 914 | |||
| 915 | if (function.err_msg) |em| { | ||
| 916 | return Result{ .fail = em }; | ||
| 917 | } else { | ||
| 918 | return Result.ok; | ||
| 919 | } | ||
| 920 | } | 894 | } |
| 921 | 895 | ||
| 922 | pub fn generateLazy( | 896 | pub fn generateLazy( |
| ... | @@ -924,9 +898,9 @@ pub fn generateLazy( | ... | @@ -924,9 +898,9 @@ pub fn generateLazy( |
| 924 | pt: Zcu.PerThread, | 898 | pt: Zcu.PerThread, |
| 925 | src_loc: Zcu.LazySrcLoc, | 899 | src_loc: Zcu.LazySrcLoc, |
| 926 | lazy_sym: link.File.LazySymbol, | 900 | lazy_sym: link.File.LazySymbol, |
| 927 | code: *std.ArrayList(u8), | 901 | code: *std.ArrayListUnmanaged(u8), |
| 928 | debug_output: link.File.DebugInfoOutput, | 902 | debug_output: link.File.DebugInfoOutput, |
| 929 | ) CodeGenError!Result { | 903 | ) CodeGenError!void { |
| 930 | const comp = bin_file.comp; | 904 | const comp = bin_file.comp; |
| 931 | const gpa = comp.gpa; | 905 | const gpa = comp.gpa; |
| 932 | const mod = comp.root_mod; | 906 | const mod = comp.root_mod; |
| ... | @@ -941,7 +915,6 @@ pub fn generateLazy( | ... | @@ -941,7 +915,6 @@ pub fn generateLazy( |
| 941 | .target = &mod.resolved_target.result, | 915 | .target = &mod.resolved_target.result, |
| 942 | .debug_output = debug_output, | 916 | .debug_output = debug_output, |
| 943 | .owner = .{ .lazy_sym = lazy_sym }, | 917 | .owner = .{ .lazy_sym = lazy_sym }, |
| 944 | .err_msg = null, | ||
| 945 | .args = undefined, // populated after `resolveCallingConventionValues` | 918 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 946 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` | 919 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 947 | .fn_type = undefined, | 920 | .fn_type = undefined, |
| ... | @@ -957,10 +930,8 @@ pub fn generateLazy( | ... | @@ -957,10 +930,8 @@ pub fn generateLazy( |
| 957 | defer function.mir_instructions.deinit(gpa); | 930 | defer function.mir_instructions.deinit(gpa); |
| 958 | 931 | ||
| 959 | function.genLazy(lazy_sym) catch |err| switch (err) { | 932 | function.genLazy(lazy_sym) catch |err| switch (err) { |
| 960 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 933 | error.CodegenFail => return error.CodegenFail, |
| 961 | error.OutOfRegisters => return Result{ | 934 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 962 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 963 | }, | ||
| 964 | else => |e| return e, | 935 | else => |e| return e, |
| 965 | }; | 936 | }; |
| 966 | 937 | ||
| ... | @@ -991,28 +962,10 @@ pub fn generateLazy( | ... | @@ -991,28 +962,10 @@ pub fn generateLazy( |
| 991 | defer emit.deinit(); | 962 | defer emit.deinit(); |
| 992 | 963 | ||
| 993 | emit.emitMir() catch |err| switch (err) { | 964 | emit.emitMir() catch |err| switch (err) { |
| 994 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | 965 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), |
| 995 | error.InvalidInstruction => |e| { | 966 | error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), |
| 996 | const msg = switch (e) { | ||
| 997 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | ||
| 998 | }; | ||
| 999 | return Result{ | ||
| 1000 | .fail = try ErrorMsg.create( | ||
| 1001 | gpa, | ||
| 1002 | src_loc, | ||
| 1003 | "{s} This is a bug in the Zig compiler.", | ||
| 1004 | .{msg}, | ||
| 1005 | ), | ||
| 1006 | }; | ||
| 1007 | }, | ||
| 1008 | else => |e| return e, | 967 | else => |e| return e, |
| 1009 | }; | 968 | }; |
| 1010 | |||
| 1011 | if (function.err_msg) |em| { | ||
| 1012 | return Result{ .fail = em }; | ||
| 1013 | } else { | ||
| 1014 | return Result.ok; | ||
| 1015 | } | ||
| 1016 | } | 969 | } |
| 1017 | 970 | ||
| 1018 | const FormatWipMirData = struct { | 971 | const FormatWipMirData = struct { |
| ... | @@ -4758,19 +4711,19 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void { | ... | @@ -4758,19 +4711,19 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4758 | return func.fail("TODO implement codegen airFieldParentPtr", .{}); | 4711 | return func.fail("TODO implement codegen airFieldParentPtr", .{}); |
| 4759 | } | 4712 | } |
| 4760 | 4713 | ||
| 4761 | fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void { | 4714 | fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerError!void { |
| 4762 | const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; | 4715 | const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; |
| 4763 | const ty = arg.ty.toType(); | 4716 | const ty = arg.ty.toType(); |
| 4764 | if (arg.name == .none) return; | 4717 | if (arg.name == .none) return; |
| 4765 | 4718 | ||
| 4766 | switch (func.debug_output) { | 4719 | switch (func.debug_output) { |
| 4767 | .dwarf => |dw| switch (mcv) { | 4720 | .dwarf => |dw| switch (mcv) { |
| 4768 | .register => |reg| try dw.genLocalDebugInfo( | 4721 | .register => |reg| dw.genLocalDebugInfo( |
| 4769 | .local_arg, | 4722 | .local_arg, |
| 4770 | arg.name.toSlice(func.air), | 4723 | arg.name.toSlice(func.air), |
| 4771 | ty, | 4724 | ty, |
| 4772 | .{ .reg = reg.dwarfNum() }, | 4725 | .{ .reg = reg.dwarfNum() }, |
| 4773 | ), | 4726 | ) catch |err| return func.fail("failed to generate debug info: {s}", .{@errorName(err)}), |
| 4774 | .load_frame => {}, | 4727 | .load_frame => {}, |
| 4775 | else => {}, | 4728 | else => {}, |
| 4776 | }, | 4729 | }, |
| ... | @@ -4779,7 +4732,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void { | ... | @@ -4779,7 +4732,7 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 4779 | } | 4732 | } |
| 4780 | } | 4733 | } |
| 4781 | 4734 | ||
| 4782 | fn airArg(func: *Func, inst: Air.Inst.Index) !void { | 4735 | fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void { |
| 4783 | var arg_index = func.arg_index; | 4736 | var arg_index = func.arg_index; |
| 4784 | 4737 | ||
| 4785 | // we skip over args that have no bits | 4738 | // we skip over args that have no bits |
| ... | @@ -5255,7 +5208,7 @@ fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void { | ... | @@ -5255,7 +5208,7 @@ fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void { |
| 5255 | try func.lowerBlock(inst, @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); | 5208 | try func.lowerBlock(inst, @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); |
| 5256 | } | 5209 | } |
| 5257 | 5210 | ||
| 5258 | fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void { | 5211 | fn airDbgVar(func: *Func, inst: Air.Inst.Index) InnerError!void { |
| 5259 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 5212 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 5260 | const operand = pl_op.operand; | 5213 | const operand = pl_op.operand; |
| 5261 | const ty = func.typeOf(operand); | 5214 | const ty = func.typeOf(operand); |
| ... | @@ -5263,7 +5216,8 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void { | ... | @@ -5263,7 +5216,8 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void { |
| 5263 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); | 5216 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); |
| 5264 | 5217 | ||
| 5265 | const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)]; | 5218 | const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 5266 | try func.genVarDbgInfo(tag, ty, mcv, name.toSlice(func.air)); | 5219 | func.genVarDbgInfo(tag, ty, mcv, name.toSlice(func.air)) catch |err| |
| 5220 | return func.fail("failed to generate variable debug info: {s}", .{@errorName(err)}); | ||
| 5267 | 5221 | ||
| 5268 | return func.finishAir(inst, .unreach, .{ operand, .none, .none }); | 5222 | return func.finishAir(inst, .unreach, .{ operand, .none, .none }); |
| 5269 | } | 5223 | } |
| ... | @@ -8236,10 +8190,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { | ... | @@ -8236,10 +8190,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { |
| 8236 | return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); | 8190 | return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); |
| 8237 | }, | 8191 | }, |
| 8238 | }, | 8192 | }, |
| 8239 | .fail => |msg| { | 8193 | .fail => |msg| return func.failMsg(msg), |
| 8240 | func.err_msg = msg; | ||
| 8241 | return error.CodegenFail; | ||
| 8242 | }, | ||
| 8243 | }; | 8194 | }; |
| 8244 | return mcv; | 8195 | return mcv; |
| 8245 | } | 8196 | } |
| ... | @@ -8427,17 +8378,23 @@ fn wantSafety(func: *Func) bool { | ... | @@ -8427,17 +8378,23 @@ fn wantSafety(func: *Func) bool { |
| 8427 | }; | 8378 | }; |
| 8428 | } | 8379 | } |
| 8429 | 8380 | ||
| 8430 | fn fail(func: *Func, comptime format: []const u8, args: anytype) InnerError { | 8381 | fn fail(func: *const Func, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 8431 | @branchHint(.cold); | 8382 | @branchHint(.cold); |
| 8432 | assert(func.err_msg == null); | 8383 | const zcu = func.pt.zcu; |
| 8433 | func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args); | 8384 | switch (func.owner) { |
| 8385 | .nav_index => |i| return zcu.codegenFail(i, format, args), | ||
| 8386 | .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args), | ||
| 8387 | } | ||
| 8434 | return error.CodegenFail; | 8388 | return error.CodegenFail; |
| 8435 | } | 8389 | } |
| 8436 | 8390 | ||
| 8437 | fn failSymbol(func: *Func, comptime format: []const u8, args: anytype) InnerError { | 8391 | fn failMsg(func: *const Func, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { |
| 8438 | @branchHint(.cold); | 8392 | @branchHint(.cold); |
| 8439 | assert(func.err_msg == null); | 8393 | const zcu = func.pt.zcu; |
| 8440 | func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args); | 8394 | switch (func.owner) { |
| 8395 | .nav_index => |i| return zcu.codegenFailMsg(i, msg), | ||
| 8396 | .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg), | ||
| 8397 | } | ||
| 8441 | return error.CodegenFail; | 8398 | return error.CodegenFail; |
| 8442 | } | 8399 | } |
| 8443 | 8400 |
src/arch/riscv64/Emit.zig+9-8| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | bin_file: *link.File, | 3 | bin_file: *link.File, |
| 4 | lower: Lower, | 4 | lower: Lower, |
| 5 | debug_output: link.File.DebugInfoOutput, | 5 | debug_output: link.File.DebugInfoOutput, |
| 6 | code: *std.ArrayList(u8), | 6 | code: *std.ArrayListUnmanaged(u8), |
| 7 | 7 | ||
| 8 | prev_di_line: u32, | 8 | prev_di_line: u32, |
| 9 | prev_di_column: u32, | 9 | prev_di_column: u32, |
| ... | @@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{ | ... | @@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{ |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | pub fn emitMir(emit: *Emit) Error!void { | 20 | pub fn emitMir(emit: *Emit) Error!void { |
| 21 | const gpa = emit.bin_file.comp.gpa; | ||
| 21 | log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len}); | 22 | log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len}); |
| 22 | for (0..emit.lower.mir.instructions.len) |mir_i| { | 23 | for (0..emit.lower.mir.instructions.len) |mir_i| { |
| 23 | const mir_index: Mir.Inst.Index = @intCast(mir_i); | 24 | const mir_index: Mir.Inst.Index = @intCast(mir_i); |
| ... | @@ -30,7 +31,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -30,7 +31,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 30 | var lowered_relocs = lowered.relocs; | 31 | var lowered_relocs = lowered.relocs; |
| 31 | for (lowered.insts, 0..) |lowered_inst, lowered_index| { | 32 | for (lowered.insts, 0..) |lowered_inst, lowered_index| { |
| 32 | const start_offset: u32 = @intCast(emit.code.items.len); | 33 | const start_offset: u32 = @intCast(emit.code.items.len); |
| 33 | try lowered_inst.encode(emit.code.writer()); | 34 | try lowered_inst.encode(emit.code.writer(gpa)); |
| 34 | 35 | ||
| 35 | while (lowered_relocs.len > 0 and | 36 | while (lowered_relocs.len > 0 and |
| 36 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ | 37 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ |
| ... | @@ -56,13 +57,13 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -56,13 +57,13 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 56 | const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); | 57 | const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); |
| 57 | const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); | 58 | const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); |
| 58 | 59 | ||
| 59 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 60 | try atom_ptr.addReloc(gpa, .{ |
| 60 | .r_offset = start_offset, | 61 | .r_offset = start_offset, |
| 61 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, | 62 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, |
| 62 | .r_addend = 0, | 63 | .r_addend = 0, |
| 63 | }, zo); | 64 | }, zo); |
| 64 | 65 | ||
| 65 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 66 | try atom_ptr.addReloc(gpa, .{ |
| 66 | .r_offset = start_offset + 4, | 67 | .r_offset = start_offset + 4, |
| 67 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, | 68 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, |
| 68 | .r_addend = 0, | 69 | .r_addend = 0, |
| ... | @@ -76,19 +77,19 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -76,19 +77,19 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 76 | 77 | ||
| 77 | const R_RISCV = std.elf.R_RISCV; | 78 | const R_RISCV = std.elf.R_RISCV; |
| 78 | 79 | ||
| 79 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 80 | try atom_ptr.addReloc(gpa, .{ |
| 80 | .r_offset = start_offset, | 81 | .r_offset = start_offset, |
| 81 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20), | 82 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20), |
| 82 | .r_addend = 0, | 83 | .r_addend = 0, |
| 83 | }, zo); | 84 | }, zo); |
| 84 | 85 | ||
| 85 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 86 | try atom_ptr.addReloc(gpa, .{ |
| 86 | .r_offset = start_offset + 4, | 87 | .r_offset = start_offset + 4, |
| 87 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD), | 88 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD), |
| 88 | .r_addend = 0, | 89 | .r_addend = 0, |
| 89 | }, zo); | 90 | }, zo); |
| 90 | 91 | ||
| 91 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 92 | try atom_ptr.addReloc(gpa, .{ |
| 92 | .r_offset = start_offset + 8, | 93 | .r_offset = start_offset + 8, |
| 93 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I), | 94 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I), |
| 94 | .r_addend = 0, | 95 | .r_addend = 0, |
| ... | @@ -101,7 +102,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -101,7 +102,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 101 | 102 | ||
| 102 | const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT); | 103 | const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT); |
| 103 | 104 | ||
| 104 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 105 | try atom_ptr.addReloc(gpa, .{ |
| 105 | .r_offset = start_offset, | 106 | .r_offset = start_offset, |
| 106 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, | 107 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, |
| 107 | .r_addend = 0, | 108 | .r_addend = 0, |
src/arch/sparc64/CodeGen.zig+24-28| ... | @@ -21,7 +21,6 @@ const Emit = @import("Emit.zig"); | ... | @@ -21,7 +21,6 @@ const Emit = @import("Emit.zig"); |
| 21 | const Liveness = @import("../../Liveness.zig"); | 21 | const Liveness = @import("../../Liveness.zig"); |
| 22 | const Type = @import("../../Type.zig"); | 22 | const Type = @import("../../Type.zig"); |
| 23 | const CodeGenError = codegen.CodeGenError; | 23 | const CodeGenError = codegen.CodeGenError; |
| 24 | const Result = @import("../../codegen.zig").Result; | ||
| 25 | const Endian = std.builtin.Endian; | 24 | const Endian = std.builtin.Endian; |
| 26 | const Alignment = InternPool.Alignment; | 25 | const Alignment = InternPool.Alignment; |
| 27 | 26 | ||
| ... | @@ -55,7 +54,7 @@ liveness: Liveness, | ... | @@ -55,7 +54,7 @@ liveness: Liveness, |
| 55 | bin_file: *link.File, | 54 | bin_file: *link.File, |
| 56 | target: *const std.Target, | 55 | target: *const std.Target, |
| 57 | func_index: InternPool.Index, | 56 | func_index: InternPool.Index, |
| 58 | code: *std.ArrayList(u8), | 57 | code: *std.ArrayListUnmanaged(u8), |
| 59 | debug_output: link.File.DebugInfoOutput, | 58 | debug_output: link.File.DebugInfoOutput, |
| 60 | err_msg: ?*ErrorMsg, | 59 | err_msg: ?*ErrorMsg, |
| 61 | args: []MCValue, | 60 | args: []MCValue, |
| ... | @@ -266,9 +265,9 @@ pub fn generate( | ... | @@ -266,9 +265,9 @@ pub fn generate( |
| 266 | func_index: InternPool.Index, | 265 | func_index: InternPool.Index, |
| 267 | air: Air, | 266 | air: Air, |
| 268 | liveness: Liveness, | 267 | liveness: Liveness, |
| 269 | code: *std.ArrayList(u8), | 268 | code: *std.ArrayListUnmanaged(u8), |
| 270 | debug_output: link.File.DebugInfoOutput, | 269 | debug_output: link.File.DebugInfoOutput, |
| 271 | ) CodeGenError!Result { | 270 | ) CodeGenError!void { |
| 272 | const zcu = pt.zcu; | 271 | const zcu = pt.zcu; |
| 273 | const gpa = zcu.gpa; | 272 | const gpa = zcu.gpa; |
| 274 | const func = zcu.funcInfo(func_index); | 273 | const func = zcu.funcInfo(func_index); |
| ... | @@ -284,7 +283,7 @@ pub fn generate( | ... | @@ -284,7 +283,7 @@ pub fn generate( |
| 284 | } | 283 | } |
| 285 | try branch_stack.append(.{}); | 284 | try branch_stack.append(.{}); |
| 286 | 285 | ||
| 287 | var function = Self{ | 286 | var function: Self = .{ |
| 288 | .gpa = gpa, | 287 | .gpa = gpa, |
| 289 | .pt = pt, | 288 | .pt = pt, |
| 290 | .air = air, | 289 | .air = air, |
| ... | @@ -310,10 +309,7 @@ pub fn generate( | ... | @@ -310,10 +309,7 @@ pub fn generate( |
| 310 | defer function.exitlude_jump_relocs.deinit(gpa); | 309 | defer function.exitlude_jump_relocs.deinit(gpa); |
| 311 | 310 | ||
| 312 | var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) { | 311 | var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) { |
| 313 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 312 | error.CodegenFail => return error.CodegenFail, |
| 314 | error.OutOfRegisters => return Result{ | ||
| 315 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 316 | }, | ||
| 317 | else => |e| return e, | 313 | else => |e| return e, |
| 318 | }; | 314 | }; |
| 319 | defer call_info.deinit(&function); | 315 | defer call_info.deinit(&function); |
| ... | @@ -324,10 +320,8 @@ pub fn generate( | ... | @@ -324,10 +320,8 @@ pub fn generate( |
| 324 | function.max_end_stack = call_info.stack_byte_count; | 320 | function.max_end_stack = call_info.stack_byte_count; |
| 325 | 321 | ||
| 326 | function.gen() catch |err| switch (err) { | 322 | function.gen() catch |err| switch (err) { |
| 327 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 323 | error.CodegenFail => return error.CodegenFail, |
| 328 | error.OutOfRegisters => return Result{ | 324 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 329 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 330 | }, | ||
| 331 | else => |e| return e, | 325 | else => |e| return e, |
| 332 | }; | 326 | }; |
| 333 | 327 | ||
| ... | @@ -337,7 +331,7 @@ pub fn generate( | ... | @@ -337,7 +331,7 @@ pub fn generate( |
| 337 | }; | 331 | }; |
| 338 | defer mir.deinit(gpa); | 332 | defer mir.deinit(gpa); |
| 339 | 333 | ||
| 340 | var emit = Emit{ | 334 | var emit: Emit = .{ |
| 341 | .mir = mir, | 335 | .mir = mir, |
| 342 | .bin_file = lf, | 336 | .bin_file = lf, |
| 343 | .debug_output = debug_output, | 337 | .debug_output = debug_output, |
| ... | @@ -351,15 +345,9 @@ pub fn generate( | ... | @@ -351,15 +345,9 @@ pub fn generate( |
| 351 | defer emit.deinit(); | 345 | defer emit.deinit(); |
| 352 | 346 | ||
| 353 | emit.emitMir() catch |err| switch (err) { | 347 | emit.emitMir() catch |err| switch (err) { |
| 354 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | 348 | error.EmitFail => return function.failMsg(emit.err_msg.?), |
| 355 | else => |e| return e, | 349 | else => |e| return e, |
| 356 | }; | 350 | }; |
| 357 | |||
| 358 | if (function.err_msg) |em| { | ||
| 359 | return Result{ .fail = em }; | ||
| 360 | } else { | ||
| 361 | return Result.ok; | ||
| 362 | } | ||
| 363 | } | 351 | } |
| 364 | 352 | ||
| 365 | fn gen(self: *Self) !void { | 353 | fn gen(self: *Self) !void { |
| ... | @@ -1014,7 +1002,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1014,7 +1002,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1014 | return bt.finishAir(result); | 1002 | return bt.finishAir(result); |
| 1015 | } | 1003 | } |
| 1016 | 1004 | ||
| 1017 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { | 1005 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1018 | const pt = self.pt; | 1006 | const pt = self.pt; |
| 1019 | const zcu = pt.zcu; | 1007 | const zcu = pt.zcu; |
| 1020 | const arg_index = self.arg_index; | 1008 | const arg_index = self.arg_index; |
| ... | @@ -1036,7 +1024,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1036,7 +1024,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1036 | } | 1024 | } |
| 1037 | }; | 1025 | }; |
| 1038 | 1026 | ||
| 1039 | try self.genArgDbgInfo(inst, mcv); | 1027 | self.genArgDbgInfo(inst, mcv) catch |err| |
| 1028 | return self.fail("failed to generate debug info for parameter: {s}", .{@errorName(err)}); | ||
| 1040 | 1029 | ||
| 1041 | if (self.liveness.isUnused(inst)) | 1030 | if (self.liveness.isUnused(inst)) |
| 1042 | return self.finishAirBookkeeping(); | 1031 | return self.finishAirBookkeeping(); |
| ... | @@ -3511,12 +3500,19 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) | ... | @@ -3511,12 +3500,19 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 3511 | } | 3500 | } |
| 3512 | } | 3501 | } |
| 3513 | 3502 | ||
| 3514 | fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { | 3503 | fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 3515 | @branchHint(.cold); | 3504 | @branchHint(.cold); |
| 3516 | assert(self.err_msg == null); | 3505 | const zcu = self.pt.zcu; |
| 3517 | const gpa = self.gpa; | 3506 | const func = zcu.funcInfo(self.func_index); |
| 3518 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | 3507 | const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args); |
| 3519 | return error.CodegenFail; | 3508 | return zcu.codegenFailMsg(func.owner_nav, msg); |
| 3509 | } | ||
| 3510 | |||
| 3511 | fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { | ||
| 3512 | @branchHint(.cold); | ||
| 3513 | const zcu = self.pt.zcu; | ||
| 3514 | const func = zcu.funcInfo(self.func_index); | ||
| 3515 | return zcu.codegenFailMsg(func.owner_nav, msg); | ||
| 3520 | } | 3516 | } |
| 3521 | 3517 | ||
| 3522 | /// Called when there are no operands, and the instruction is always unreferenced. | 3518 | /// Called when there are no operands, and the instruction is always unreferenced. |
src/arch/sparc64/Emit.zig+6-3| ... | @@ -22,7 +22,7 @@ debug_output: link.File.DebugInfoOutput, | ... | @@ -22,7 +22,7 @@ debug_output: link.File.DebugInfoOutput, |
| 22 | target: *const std.Target, | 22 | target: *const std.Target, |
| 23 | err_msg: ?*ErrorMsg = null, | 23 | err_msg: ?*ErrorMsg = null, |
| 24 | src_loc: Zcu.LazySrcLoc, | 24 | src_loc: Zcu.LazySrcLoc, |
| 25 | code: *std.ArrayList(u8), | 25 | code: *std.ArrayListUnmanaged(u8), |
| 26 | 26 | ||
| 27 | prev_di_line: u32, | 27 | prev_di_line: u32, |
| 28 | prev_di_column: u32, | 28 | prev_di_column: u32, |
| ... | @@ -678,10 +678,13 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { | ... | @@ -678,10 +678,13 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { |
| 678 | } | 678 | } |
| 679 | 679 | ||
| 680 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { | 680 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 681 | const comp = emit.bin_file.comp; | ||
| 682 | const gpa = comp.gpa; | ||
| 683 | |||
| 681 | // SPARCv9 instructions are always arranged in BE regardless of the | 684 | // SPARCv9 instructions are always arranged in BE regardless of the |
| 682 | // endianness mode the CPU is running in (Section 3.1 of the ISA specification). | 685 | // endianness mode the CPU is running in (Section 3.1 of the ISA specification). |
| 683 | // This is to ease porting in case someone wants to do a LE SPARCv9 backend. | 686 | // This is to ease porting in case someone wants to do a LE SPARCv9 backend. |
| 684 | const endian = Endian.big; | 687 | const endian: Endian = .big; |
| 685 | 688 | ||
| 686 | std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian); | 689 | std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian); |
| 687 | } | 690 | } |
src/arch/wasm/CodeGen.zig+3170-3455| ... | @@ -1,14 +1,13 @@ | ... | @@ -1,14 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const ArrayList = std.ArrayList; | ||
| 5 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 6 | const testing = std.testing; | 5 | const testing = std.testing; |
| 7 | const leb = std.leb; | 6 | const leb = std.leb; |
| 8 | const mem = std.mem; | 7 | const mem = std.mem; |
| 9 | const wasm = std.wasm; | ||
| 10 | const log = std.log.scoped(.codegen); | 8 | const log = std.log.scoped(.codegen); |
| 11 | 9 | ||
| 10 | const CodeGen = @This(); | ||
| 12 | const codegen = @import("../../codegen.zig"); | 11 | const codegen = @import("../../codegen.zig"); |
| 13 | const Zcu = @import("../../Zcu.zig"); | 12 | const Zcu = @import("../../Zcu.zig"); |
| 14 | const InternPool = @import("../../InternPool.zig"); | 13 | const InternPool = @import("../../InternPool.zig"); |
| ... | @@ -19,13 +18,113 @@ const Compilation = @import("../../Compilation.zig"); | ... | @@ -19,13 +18,113 @@ const Compilation = @import("../../Compilation.zig"); |
| 19 | const link = @import("../../link.zig"); | 18 | const link = @import("../../link.zig"); |
| 20 | const Air = @import("../../Air.zig"); | 19 | const Air = @import("../../Air.zig"); |
| 21 | const Liveness = @import("../../Liveness.zig"); | 20 | const Liveness = @import("../../Liveness.zig"); |
| 22 | const target_util = @import("../../target.zig"); | ||
| 23 | const Mir = @import("Mir.zig"); | 21 | const Mir = @import("Mir.zig"); |
| 24 | const Emit = @import("Emit.zig"); | 22 | const Emit = @import("Emit.zig"); |
| 25 | const abi = @import("abi.zig"); | 23 | const abi = @import("abi.zig"); |
| 26 | const Alignment = InternPool.Alignment; | 24 | const Alignment = InternPool.Alignment; |
| 27 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | 25 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 28 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 26 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 27 | const Wasm = link.File.Wasm; | ||
| 28 | |||
| 29 | const target_util = @import("../../target.zig"); | ||
| 30 | const libcFloatPrefix = target_util.libcFloatPrefix; | ||
| 31 | const libcFloatSuffix = target_util.libcFloatSuffix; | ||
| 32 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; | ||
| 33 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; | ||
| 34 | |||
| 35 | /// Reference to the function declaration the code | ||
| 36 | /// section belongs to | ||
| 37 | owner_nav: InternPool.Nav.Index, | ||
| 38 | /// Current block depth. Used to calculate the relative difference between a break | ||
| 39 | /// and block | ||
| 40 | block_depth: u32 = 0, | ||
| 41 | air: Air, | ||
| 42 | liveness: Liveness, | ||
| 43 | gpa: mem.Allocator, | ||
| 44 | func_index: InternPool.Index, | ||
| 45 | /// Contains a list of current branches. | ||
| 46 | /// When we return from a branch, the branch will be popped from this list, | ||
| 47 | /// which means branches can only contain references from within its own branch, | ||
| 48 | /// or a branch higher (lower index) in the tree. | ||
| 49 | branches: std.ArrayListUnmanaged(Branch) = .empty, | ||
| 50 | /// Table to save `WValue`'s generated by an `Air.Inst` | ||
| 51 | // values: ValueTable, | ||
| 52 | /// Mapping from Air.Inst.Index to block ids | ||
| 53 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { | ||
| 54 | label: u32, | ||
| 55 | value: WValue, | ||
| 56 | }) = .{}, | ||
| 57 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. | ||
| 58 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, | ||
| 59 | /// The index the next local generated will have | ||
| 60 | /// NOTE: arguments share the index with locals therefore the first variable | ||
| 61 | /// will have the index that comes after the last argument's index | ||
| 62 | local_index: u32, | ||
| 63 | /// The index of the current argument. | ||
| 64 | /// Used to track which argument is being referenced in `airArg`. | ||
| 65 | arg_index: u32 = 0, | ||
| 66 | /// List of simd128 immediates. Each value is stored as an array of bytes. | ||
| 67 | /// This list will only be populated for 128bit-simd values when the target features | ||
| 68 | /// are enabled also. | ||
| 69 | simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, | ||
| 70 | /// The Target we're emitting (used to call intInfo) | ||
| 71 | target: *const std.Target, | ||
| 72 | ptr_size: enum { wasm32, wasm64 }, | ||
| 73 | wasm: *link.File.Wasm, | ||
| 74 | pt: Zcu.PerThread, | ||
| 75 | /// List of MIR Instructions | ||
| 76 | mir_instructions: *std.MultiArrayList(Mir.Inst), | ||
| 77 | /// Contains extra data for MIR | ||
| 78 | mir_extra: *std.ArrayListUnmanaged(u32), | ||
| 79 | start_mir_extra_off: u32, | ||
| 80 | start_locals_off: u32, | ||
| 81 | /// List of all locals' types generated throughout this declaration | ||
| 82 | /// used to emit locals count at start of 'code' section. | ||
| 83 | locals: *std.ArrayListUnmanaged(std.wasm.Valtype), | ||
| 84 | /// When a function is executing, we store the the current stack pointer's value within this local. | ||
| 85 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | ||
| 86 | initial_stack_value: WValue = .none, | ||
| 87 | /// The current stack pointer subtracted with the stack size. From this value, we will calculate | ||
| 88 | /// all offsets of the stack values. | ||
| 89 | bottom_stack_value: WValue = .none, | ||
| 90 | /// Arguments of this function declaration | ||
| 91 | /// This will be set after `resolveCallingConventionValues` | ||
| 92 | args: []WValue, | ||
| 93 | /// This will only be `.none` if the function returns void, or returns an immediate. | ||
| 94 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated | ||
| 95 | /// before this function returns its execution to the caller. | ||
| 96 | return_value: WValue, | ||
| 97 | /// The size of the stack this function occupies. In the function prologue | ||
| 98 | /// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. | ||
| 99 | stack_size: u32 = 0, | ||
| 100 | /// The stack alignment, which is 16 bytes by default. This is specified by the | ||
| 101 | /// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md | ||
| 102 | /// and also what the llvm backend will emit. | ||
| 103 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. | ||
| 104 | stack_alignment: Alignment = .@"16", | ||
| 105 | |||
| 106 | // For each individual Wasm valtype we store a seperate free list which | ||
| 107 | // allows us to re-use locals that are no longer used. e.g. a temporary local. | ||
| 108 | /// A list of indexes which represents a local of valtype `i32`. | ||
| 109 | /// It is illegal to store a non-i32 valtype in this list. | ||
| 110 | free_locals_i32: std.ArrayListUnmanaged(u32) = .empty, | ||
| 111 | /// A list of indexes which represents a local of valtype `i64`. | ||
| 112 | /// It is illegal to store a non-i64 valtype in this list. | ||
| 113 | free_locals_i64: std.ArrayListUnmanaged(u32) = .empty, | ||
| 114 | /// A list of indexes which represents a local of valtype `f32`. | ||
| 115 | /// It is illegal to store a non-f32 valtype in this list. | ||
| 116 | free_locals_f32: std.ArrayListUnmanaged(u32) = .empty, | ||
| 117 | /// A list of indexes which represents a local of valtype `f64`. | ||
| 118 | /// It is illegal to store a non-f64 valtype in this list. | ||
| 119 | free_locals_f64: std.ArrayListUnmanaged(u32) = .empty, | ||
| 120 | /// A list of indexes which represents a local of valtype `v127`. | ||
| 121 | /// It is illegal to store a non-v128 valtype in this list. | ||
| 122 | free_locals_v128: std.ArrayListUnmanaged(u32) = .empty, | ||
| 123 | |||
| 124 | /// When in debug mode, this tracks if no `finishAir` was missed. | ||
| 125 | /// Forgetting to call `finishAir` will cause the result to not be | ||
| 126 | /// stored in our `values` map and therefore cause bugs. | ||
| 127 | air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init, | ||
| 29 | 128 | ||
| 30 | /// Wasm Value, created when generating an instruction | 129 | /// Wasm Value, created when generating an instruction |
| 31 | const WValue = union(enum) { | 130 | const WValue = union(enum) { |
| ... | @@ -55,22 +154,15 @@ const WValue = union(enum) { | ... | @@ -55,22 +154,15 @@ const WValue = union(enum) { |
| 55 | float32: f32, | 154 | float32: f32, |
| 56 | /// A constant 64bit float value | 155 | /// A constant 64bit float value |
| 57 | float64: f64, | 156 | float64: f64, |
| 58 | /// A value that represents a pointer to the data section | 157 | nav_ref: struct { |
| 59 | /// Note: The value contains the symbol index, rather than the actual address | 158 | nav_index: InternPool.Nav.Index, |
| 60 | /// as we use this to perform the relocation. | 159 | offset: i32 = 0, |
| 61 | memory: u32, | 160 | }, |
| 62 | /// A value that represents a parent pointer and an offset | 161 | uav_ref: struct { |
| 63 | /// from that pointer. i.e. when slicing with constant values. | 162 | ip_index: InternPool.Index, |
| 64 | memory_offset: struct { | 163 | offset: i32 = 0, |
| 65 | /// The symbol of the parent pointer | 164 | orig_ptr_ty: InternPool.Index = .none, |
| 66 | pointer: u32, | ||
| 67 | /// Offset will be set as addend when relocating | ||
| 68 | offset: u32, | ||
| 69 | }, | 165 | }, |
| 70 | /// Represents a function pointer | ||
| 71 | /// In wasm function pointers are indexes into a function table, | ||
| 72 | /// rather than an address in the data section. | ||
| 73 | function_index: u32, | ||
| 74 | /// Offset from the bottom of the virtual stack, with the offset | 166 | /// Offset from the bottom of the virtual stack, with the offset |
| 75 | /// pointing to where the value lives. | 167 | /// pointing to where the value lives. |
| 76 | stack_offset: struct { | 168 | stack_offset: struct { |
| ... | @@ -101,7 +193,7 @@ const WValue = union(enum) { | ... | @@ -101,7 +193,7 @@ const WValue = union(enum) { |
| 101 | switch (value) { | 193 | switch (value) { |
| 102 | .stack => { | 194 | .stack => { |
| 103 | const new_local = try gen.allocLocal(ty); | 195 | const new_local = try gen.allocLocal(ty); |
| 104 | try gen.addLabel(.local_set, new_local.local.value); | 196 | try gen.addLocal(.local_set, new_local.local.value); |
| 105 | return new_local; | 197 | return new_local; |
| 106 | }, | 198 | }, |
| 107 | .local, .stack_offset => return value, | 199 | .local, .stack_offset => return value, |
| ... | @@ -119,7 +211,7 @@ const WValue = union(enum) { | ... | @@ -119,7 +211,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. | 211 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 120 | 212 | ||
| 121 | const index = local_value - reserved; | 213 | const index = local_value - reserved; |
| 122 | const valtype = @as(wasm.Valtype, @enumFromInt(gen.locals.items[index])); | 214 | const valtype = gen.locals.items[gen.start_locals_off + index]; |
| 123 | switch (valtype) { | 215 | 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 instead | 216 | .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, | 217 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| ... | @@ -132,8 +224,6 @@ const WValue = union(enum) { | ... | @@ -132,8 +224,6 @@ const WValue = union(enum) { |
| 132 | } | 224 | } |
| 133 | }; | 225 | }; |
| 134 | 226 | ||
| 135 | /// Wasm ops, but without input/output/signedness information | ||
| 136 | /// Used for `buildOpcode` | ||
| 137 | const Op = enum { | 227 | const Op = enum { |
| 138 | @"unreachable", | 228 | @"unreachable", |
| 139 | nop, | 229 | nop, |
| ... | @@ -147,12 +237,8 @@ const Op = enum { | ... | @@ -147,12 +237,8 @@ const Op = enum { |
| 147 | br_table, | 237 | br_table, |
| 148 | @"return", | 238 | @"return", |
| 149 | call, | 239 | call, |
| 150 | call_indirect, | ||
| 151 | drop, | 240 | drop, |
| 152 | select, | 241 | select, |
| 153 | local_get, | ||
| 154 | local_set, | ||
| 155 | local_tee, | ||
| 156 | global_get, | 242 | global_get, |
| 157 | global_set, | 243 | global_set, |
| 158 | load, | 244 | load, |
| ... | @@ -200,70 +286,38 @@ const Op = enum { | ... | @@ -200,70 +286,38 @@ const Op = enum { |
| 200 | extend, | 286 | extend, |
| 201 | }; | 287 | }; |
| 202 | 288 | ||
| 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 } | ||
| 231 | const OpcodeBuildArguments = struct { | 289 | const OpcodeBuildArguments = struct { |
| 232 | /// First valtype in the opcode (usually represents the type of the output) | 290 | /// First valtype in the opcode (usually represents the type of the output) |
| 233 | valtype1: ?wasm.Valtype = null, | 291 | valtype1: ?std.wasm.Valtype = null, |
| 234 | /// The operation (e.g. call, unreachable, div, min, sqrt, etc.) | 292 | /// The operation (e.g. call, unreachable, div, min, sqrt, etc.) |
| 235 | op: Op, | 293 | op: Op, |
| 236 | /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s) | 294 | /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s) |
| 237 | width: ?u8 = null, | 295 | width: ?u8 = null, |
| 238 | /// Second valtype in the opcode name (usually represents the type of the input) | 296 | /// Second valtype in the opcode name (usually represents the type of the input) |
| 239 | valtype2: ?wasm.Valtype = null, | 297 | valtype2: ?std.wasm.Valtype = null, |
| 240 | /// Signedness of the op | 298 | /// Signedness of the op |
| 241 | signedness: ?std.builtin.Signedness = null, | 299 | signedness: ?std.builtin.Signedness = null, |
| 242 | }; | 300 | }; |
| 243 | 301 | ||
| 244 | /// Helper function that builds an Opcode given the arguments needed | 302 | /// TODO: deprecated, should be split up per tag. |
| 245 | fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | 303 | fn buildOpcode(args: OpcodeBuildArguments) std.wasm.Opcode { |
| 246 | switch (args.op) { | 304 | switch (args.op) { |
| 247 | .@"unreachable" => return .@"unreachable", | 305 | .@"unreachable" => unreachable, |
| 248 | .nop => return .nop, | 306 | .nop => unreachable, |
| 249 | .block => return .block, | 307 | .block => unreachable, |
| 250 | .loop => return .loop, | 308 | .loop => unreachable, |
| 251 | .@"if" => return .@"if", | 309 | .@"if" => unreachable, |
| 252 | .@"else" => return .@"else", | 310 | .@"else" => unreachable, |
| 253 | .end => return .end, | 311 | .end => unreachable, |
| 254 | .br => return .br, | 312 | .br => unreachable, |
| 255 | .br_if => return .br_if, | 313 | .br_if => unreachable, |
| 256 | .br_table => return .br_table, | 314 | .br_table => unreachable, |
| 257 | .@"return" => return .@"return", | 315 | .@"return" => unreachable, |
| 258 | .call => return .call, | 316 | .call => unreachable, |
| 259 | .call_indirect => return .call_indirect, | 317 | .drop => unreachable, |
| 260 | .drop => return .drop, | 318 | .select => unreachable, |
| 261 | .select => return .select, | 319 | .global_get => unreachable, |
| 262 | .local_get => return .local_get, | 320 | .global_set => unreachable, |
| 263 | .local_set => return .local_set, | ||
| 264 | .local_tee => return .local_tee, | ||
| 265 | .global_get => return .global_get, | ||
| 266 | .global_set => return .global_set, | ||
| 267 | 321 | ||
| 268 | .load => if (args.width) |width| switch (width) { | 322 | .load => if (args.width) |width| switch (width) { |
| 269 | 8 => switch (args.valtype1.?) { | 323 | 8 => switch (args.valtype1.?) { |
| ... | @@ -621,121 +675,17 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -621,121 +675,17 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 621 | test "Wasm - buildOpcode" { | 675 | test "Wasm - buildOpcode" { |
| 622 | // Make sure buildOpcode is referenced, and test some examples | 676 | // Make sure buildOpcode is referenced, and test some examples |
| 623 | const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 }); | 677 | const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 }); |
| 624 | const end = buildOpcode(.{ .op = .end }); | ||
| 625 | const local_get = buildOpcode(.{ .op = .local_get }); | ||
| 626 | const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed }); | 678 | 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 }); | 679 | const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 }); |
| 628 | 680 | ||
| 629 | try testing.expectEqual(@as(wasm.Opcode, .i32_const), i32_const); | 681 | try testing.expectEqual(@as(std.wasm.Opcode, .i32_const), i32_const); |
| 630 | try testing.expectEqual(@as(wasm.Opcode, .end), end); | 682 | try testing.expectEqual(@as(std.wasm.Opcode, .i64_extend32_s), i64_extend32_s); |
| 631 | try testing.expectEqual(@as(wasm.Opcode, .local_get), local_get); | 683 | try testing.expectEqual(@as(std.wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64); |
| 632 | try testing.expectEqual(@as(wasm.Opcode, .i64_extend32_s), i64_extend32_s); | ||
| 633 | try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64); | ||
| 634 | } | 684 | } |
| 635 | 685 | ||
| 636 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` | 686 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 637 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); | 687 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); |
| 638 | 688 | ||
| 639 | const CodeGen = @This(); | ||
| 640 | |||
| 641 | /// Reference to the function declaration the code | ||
| 642 | /// section belongs to | ||
| 643 | owner_nav: InternPool.Nav.Index, | ||
| 644 | src_loc: Zcu.LazySrcLoc, | ||
| 645 | /// Current block depth. Used to calculate the relative difference between a break | ||
| 646 | /// and block | ||
| 647 | block_depth: u32 = 0, | ||
| 648 | air: Air, | ||
| 649 | liveness: Liveness, | ||
| 650 | gpa: mem.Allocator, | ||
| 651 | debug_output: link.File.DebugInfoOutput, | ||
| 652 | func_index: InternPool.Index, | ||
| 653 | /// Contains a list of current branches. | ||
| 654 | /// When we return from a branch, the branch will be popped from this list, | ||
| 655 | /// which means branches can only contain references from within its own branch, | ||
| 656 | /// or a branch higher (lower index) in the tree. | ||
| 657 | branches: std.ArrayListUnmanaged(Branch) = .empty, | ||
| 658 | /// Table to save `WValue`'s generated by an `Air.Inst` | ||
| 659 | // values: ValueTable, | ||
| 660 | /// Mapping from Air.Inst.Index to block ids | ||
| 661 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { | ||
| 662 | label: u32, | ||
| 663 | value: WValue, | ||
| 664 | }) = .{}, | ||
| 665 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. | ||
| 666 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, | ||
| 667 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. | ||
| 668 | code: *ArrayList(u8), | ||
| 669 | /// The index the next local generated will have | ||
| 670 | /// NOTE: arguments share the index with locals therefore the first variable | ||
| 671 | /// will have the index that comes after the last argument's index | ||
| 672 | local_index: u32 = 0, | ||
| 673 | /// The index of the current argument. | ||
| 674 | /// Used to track which argument is being referenced in `airArg`. | ||
| 675 | arg_index: u32 = 0, | ||
| 676 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` | ||
| 677 | err_msg: *Zcu.ErrorMsg, | ||
| 678 | /// List of all locals' types generated throughout this declaration | ||
| 679 | /// used to emit locals count at start of 'code' section. | ||
| 680 | locals: std.ArrayListUnmanaged(u8), | ||
| 681 | /// List of simd128 immediates. Each value is stored as an array of bytes. | ||
| 682 | /// This list will only be populated for 128bit-simd values when the target features | ||
| 683 | /// are enabled also. | ||
| 684 | simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, | ||
| 685 | /// The Target we're emitting (used to call intInfo) | ||
| 686 | target: *const std.Target, | ||
| 687 | /// Represents the wasm binary file that is being linked. | ||
| 688 | bin_file: *link.File.Wasm, | ||
| 689 | pt: Zcu.PerThread, | ||
| 690 | /// List of MIR Instructions | ||
| 691 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | ||
| 692 | /// Contains extra data for MIR | ||
| 693 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | ||
| 694 | /// When a function is executing, we store the the current stack pointer's value within this local. | ||
| 695 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | ||
| 696 | initial_stack_value: WValue = .none, | ||
| 697 | /// The current stack pointer subtracted with the stack size. From this value, we will calculate | ||
| 698 | /// all offsets of the stack values. | ||
| 699 | bottom_stack_value: WValue = .none, | ||
| 700 | /// Arguments of this function declaration | ||
| 701 | /// This will be set after `resolveCallingConventionValues` | ||
| 702 | args: []WValue = &.{}, | ||
| 703 | /// This will only be `.none` if the function returns void, or returns an immediate. | ||
| 704 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated | ||
| 705 | /// before this function returns its execution to the caller. | ||
| 706 | return_value: WValue = .none, | ||
| 707 | /// The size of the stack this function occupies. In the function prologue | ||
| 708 | /// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. | ||
| 709 | stack_size: u32 = 0, | ||
| 710 | /// The stack alignment, which is 16 bytes by default. This is specified by the | ||
| 711 | /// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md | ||
| 712 | /// and also what the llvm backend will emit. | ||
| 713 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. | ||
| 714 | stack_alignment: Alignment = .@"16", | ||
| 715 | |||
| 716 | // For each individual Wasm valtype we store a seperate free list which | ||
| 717 | // allows us to re-use locals that are no longer used. e.g. a temporary local. | ||
| 718 | /// A list of indexes which represents a local of valtype `i32`. | ||
| 719 | /// It is illegal to store a non-i32 valtype in this list. | ||
| 720 | free_locals_i32: std.ArrayListUnmanaged(u32) = .empty, | ||
| 721 | /// A list of indexes which represents a local of valtype `i64`. | ||
| 722 | /// It is illegal to store a non-i64 valtype in this list. | ||
| 723 | free_locals_i64: std.ArrayListUnmanaged(u32) = .empty, | ||
| 724 | /// A list of indexes which represents a local of valtype `f32`. | ||
| 725 | /// It is illegal to store a non-f32 valtype in this list. | ||
| 726 | free_locals_f32: std.ArrayListUnmanaged(u32) = .empty, | ||
| 727 | /// A list of indexes which represents a local of valtype `f64`. | ||
| 728 | /// It is illegal to store a non-f64 valtype in this list. | ||
| 729 | free_locals_f64: std.ArrayListUnmanaged(u32) = .empty, | ||
| 730 | /// A list of indexes which represents a local of valtype `v127`. | ||
| 731 | /// It is illegal to store a non-v128 valtype in this list. | ||
| 732 | free_locals_v128: std.ArrayListUnmanaged(u32) = .empty, | ||
| 733 | |||
| 734 | /// When in debug mode, this tracks if no `finishAir` was missed. | ||
| 735 | /// Forgetting to call `finishAir` will cause the result to not be | ||
| 736 | /// stored in our `values` map and therefore cause bugs. | ||
| 737 | air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init, | ||
| 738 | |||
| 739 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | 689 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 740 | 690 | ||
| 741 | const InnerError = error{ | 691 | const InnerError = error{ |
| ... | @@ -746,38 +696,33 @@ const InnerError = error{ | ... | @@ -746,38 +696,33 @@ const InnerError = error{ |
| 746 | Overflow, | 696 | Overflow, |
| 747 | } || link.File.UpdateDebugInfoError; | 697 | } || link.File.UpdateDebugInfoError; |
| 748 | 698 | ||
| 749 | pub fn deinit(func: *CodeGen) void { | 699 | pub fn deinit(cg: *CodeGen) void { |
| 750 | // in case of an error and we still have branches | 700 | const gpa = cg.gpa; |
| 751 | for (func.branches.items) |*branch| { | 701 | for (cg.branches.items) |*branch| branch.deinit(gpa); |
| 752 | branch.deinit(func.gpa); | 702 | cg.branches.deinit(gpa); |
| 753 | } | 703 | cg.blocks.deinit(gpa); |
| 754 | func.branches.deinit(func.gpa); | 704 | cg.loops.deinit(gpa); |
| 755 | func.blocks.deinit(func.gpa); | 705 | cg.simd_immediates.deinit(gpa); |
| 756 | func.loops.deinit(func.gpa); | 706 | cg.free_locals_i32.deinit(gpa); |
| 757 | func.locals.deinit(func.gpa); | 707 | cg.free_locals_i64.deinit(gpa); |
| 758 | func.simd_immediates.deinit(func.gpa); | 708 | cg.free_locals_f32.deinit(gpa); |
| 759 | func.mir_instructions.deinit(func.gpa); | 709 | cg.free_locals_f64.deinit(gpa); |
| 760 | func.mir_extra.deinit(func.gpa); | 710 | cg.free_locals_v128.deinit(gpa); |
| 761 | func.free_locals_i32.deinit(func.gpa); | 711 | cg.* = undefined; |
| 762 | func.free_locals_i64.deinit(func.gpa); | ||
| 763 | func.free_locals_f32.deinit(func.gpa); | ||
| 764 | func.free_locals_f64.deinit(func.gpa); | ||
| 765 | func.free_locals_v128.deinit(func.gpa); | ||
| 766 | func.* = undefined; | ||
| 767 | } | 712 | } |
| 768 | 713 | ||
| 769 | /// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig | 714 | fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 770 | fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError { | 715 | const zcu = cg.pt.zcu; |
| 771 | func.err_msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args); | 716 | const func = zcu.funcInfo(cg.func_index); |
| 772 | return error.CodegenFail; | 717 | return zcu.codegenFail(func.owner_nav, fmt, args); |
| 773 | } | 718 | } |
| 774 | 719 | ||
| 775 | /// Resolves the `WValue` for the given instruction `inst` | 720 | /// Resolves the `WValue` for the given instruction `inst` |
| 776 | /// When the given instruction has a `Value`, it returns a constant instead | 721 | /// When the given instruction has a `Value`, it returns a constant instead |
| 777 | fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | 722 | fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 778 | var branch_index = func.branches.items.len; | 723 | var branch_index = cg.branches.items.len; |
| 779 | while (branch_index > 0) : (branch_index -= 1) { | 724 | while (branch_index > 0) : (branch_index -= 1) { |
| 780 | const branch = func.branches.items[branch_index - 1]; | 725 | const branch = cg.branches.items[branch_index - 1]; |
| 781 | if (branch.values.get(ref)) |value| { | 726 | if (branch.values.get(ref)) |value| { |
| 782 | return value; | 727 | return value; |
| 783 | } | 728 | } |
| ... | @@ -787,16 +732,16 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | ... | @@ -787,16 +732,16 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 787 | // means we must generate it from a constant. | 732 | // means we must generate it from a constant. |
| 788 | // We always store constants in the most outer branch as they must never | 733 | // We always store constants in the most outer branch as they must never |
| 789 | // be removed. The most outer branch is always at index 0. | 734 | // be removed. The most outer branch is always at index 0. |
| 790 | const gop = try func.branches.items[0].values.getOrPut(func.gpa, ref); | 735 | const gop = try cg.branches.items[0].values.getOrPut(cg.gpa, ref); |
| 791 | assert(!gop.found_existing); | 736 | assert(!gop.found_existing); |
| 792 | 737 | ||
| 793 | const pt = func.pt; | 738 | const pt = cg.pt; |
| 794 | const zcu = pt.zcu; | 739 | const zcu = pt.zcu; |
| 795 | const val = (try func.air.value(ref, pt)).?; | 740 | const val = (try cg.air.value(ref, pt)).?; |
| 796 | const ty = func.typeOf(ref); | 741 | const ty = cg.typeOf(ref); |
| 797 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) { | 742 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) { |
| 798 | gop.value_ptr.* = .none; | 743 | gop.value_ptr.* = .none; |
| 799 | return gop.value_ptr.*; | 744 | return .none; |
| 800 | } | 745 | } |
| 801 | 746 | ||
| 802 | // When we need to pass the value by reference (such as a struct), we will | 747 | // When we need to pass the value by reference (such as a struct), we will |
| ... | @@ -805,30 +750,24 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | ... | @@ -805,30 +750,24 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 805 | // | 750 | // |
| 806 | // In the other cases, we will simply lower the constant to a value that fits | 751 | // 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). | 752 | // into a single local (such as a pointer, integer, bool, etc). |
| 808 | const result: WValue = if (isByRef(ty, pt, func.target.*)) | 753 | const result: WValue = if (isByRef(ty, zcu, cg.target)) |
| 809 | switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) { | 754 | .{ .uav_ref = .{ .ip_index = 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 | else | 755 | else |
| 817 | try func.lowerConstant(val, ty); | 756 | try cg.lowerConstant(val, ty); |
| 818 | 757 | ||
| 819 | gop.value_ptr.* = result; | 758 | gop.value_ptr.* = result; |
| 820 | return result; | 759 | return result; |
| 821 | } | 760 | } |
| 822 | 761 | ||
| 823 | /// NOTE: if result == .stack, it will be stored in .local | 762 | /// NOTE: if result == .stack, it will be stored in .local |
| 824 | fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void { | 763 | fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void { |
| 825 | assert(operands.len <= Liveness.bpi - 1); | 764 | assert(operands.len <= Liveness.bpi - 1); |
| 826 | var tomb_bits = func.liveness.getTombBits(inst); | 765 | var tomb_bits = cg.liveness.getTombBits(inst); |
| 827 | for (operands) |operand| { | 766 | for (operands) |operand| { |
| 828 | const dies = @as(u1, @truncate(tomb_bits)) != 0; | 767 | const dies = @as(u1, @truncate(tomb_bits)) != 0; |
| 829 | tomb_bits >>= 1; | 768 | tomb_bits >>= 1; |
| 830 | if (!dies) continue; | 769 | if (!dies) continue; |
| 831 | processDeath(func, operand); | 770 | processDeath(cg, operand); |
| 832 | } | 771 | } |
| 833 | 772 | ||
| 834 | // results of `none` can never be referenced. | 773 | // results of `none` can never be referenced. |
| ... | @@ -836,13 +775,13 @@ fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []c | ... | @@ -836,13 +775,13 @@ fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []c |
| 836 | const trackable_result = if (result != .stack) | 775 | const trackable_result = if (result != .stack) |
| 837 | result | 776 | result |
| 838 | else | 777 | else |
| 839 | try result.toLocal(func, func.typeOfIndex(inst)); | 778 | try result.toLocal(cg, cg.typeOfIndex(inst)); |
| 840 | const branch = func.currentBranch(); | 779 | const branch = cg.currentBranch(); |
| 841 | branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result); | 780 | branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result); |
| 842 | } | 781 | } |
| 843 | 782 | ||
| 844 | if (std.debug.runtime_safety) { | 783 | if (std.debug.runtime_safety) { |
| 845 | func.air_bookkeeping += 1; | 784 | cg.air_bookkeeping += 1; |
| 846 | } | 785 | } |
| 847 | } | 786 | } |
| 848 | 787 | ||
| ... | @@ -855,8 +794,8 @@ const Branch = struct { | ... | @@ -855,8 +794,8 @@ const Branch = struct { |
| 855 | } | 794 | } |
| 856 | }; | 795 | }; |
| 857 | 796 | ||
| 858 | inline fn currentBranch(func: *CodeGen) *Branch { | 797 | inline fn currentBranch(cg: *CodeGen) *Branch { |
| 859 | return &func.branches.items[func.branches.items.len - 1]; | 798 | return &cg.branches.items[cg.branches.items.len - 1]; |
| 860 | } | 799 | } |
| 861 | 800 | ||
| 862 | const BigTomb = struct { | 801 | const BigTomb = struct { |
| ... | @@ -883,131 +822,143 @@ const BigTomb = struct { | ... | @@ -883,131 +822,143 @@ const BigTomb = struct { |
| 883 | } | 822 | } |
| 884 | }; | 823 | }; |
| 885 | 824 | ||
| 886 | fn iterateBigTomb(func: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb { | 825 | fn iterateBigTomb(cg: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb { |
| 887 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, operand_count + 1); | 826 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, operand_count + 1); |
| 888 | return BigTomb{ | 827 | return BigTomb{ |
| 889 | .gen = func, | 828 | .gen = cg, |
| 890 | .inst = inst, | 829 | .inst = inst, |
| 891 | .lbt = func.liveness.iterateBigTomb(inst), | 830 | .lbt = cg.liveness.iterateBigTomb(inst), |
| 892 | }; | 831 | }; |
| 893 | } | 832 | } |
| 894 | 833 | ||
| 895 | fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void { | 834 | fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { |
| 896 | if (ref.toIndex() == null) return; | 835 | if (ref.toIndex() == null) return; |
| 897 | // Branches are currently only allowed to free locals allocated | 836 | // Branches are currently only allowed to free locals allocated |
| 898 | // within their own branch. | 837 | // within their own branch. |
| 899 | // TODO: Upon branch consolidation free any locals if needed. | 838 | // TODO: Upon branch consolidation free any locals if needed. |
| 900 | const value = func.currentBranch().values.getPtr(ref) orelse return; | 839 | const value = cg.currentBranch().values.getPtr(ref) orelse return; |
| 901 | if (value.* != .local) return; | 840 | if (value.* != .local) return; |
| 902 | const reserved_indexes = func.args.len + @intFromBool(func.return_value != .none); | 841 | const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none); |
| 903 | if (value.local.value < reserved_indexes) { | 842 | if (value.local.value < reserved_indexes) { |
| 904 | return; // function arguments can never be re-used | 843 | return; // function arguments can never be re-used |
| 905 | } | 844 | } |
| 906 | log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @intFromEnum(ref.toIndex().?), value.local.value }); | 845 | log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @intFromEnum(ref.toIndex().?), value.local.value }); |
| 907 | value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer | 846 | value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer |
| 908 | if (value.local.references == 0) { | 847 | if (value.local.references == 0) { |
| 909 | value.free(func); | 848 | value.free(cg); |
| 910 | } | 849 | } |
| 911 | } | 850 | } |
| 912 | 851 | ||
| 913 | /// Appends a MIR instruction and returns its index within the list of instructions | 852 | fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { |
| 914 | fn addInst(func: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { | 853 | try cg.mir_instructions.append(cg.gpa, inst); |
| 915 | try func.mir_instructions.append(func.gpa, inst); | 854 | } |
| 855 | |||
| 856 | fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { | ||
| 857 | try cg.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); | ||
| 916 | } | 858 | } |
| 917 | 859 | ||
| 918 | fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { | 860 | fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void { |
| 919 | try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); | 861 | const extra_index = cg.extraLen(); |
| 862 | try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode)); | ||
| 863 | try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } }); | ||
| 920 | } | 864 | } |
| 921 | 865 | ||
| 922 | fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void { | 866 | fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void { |
| 923 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | 867 | try cg.addInst(.{ .tag = tag, .data = .{ .label = label } }); |
| 924 | try func.mir_extra.append(func.gpa, @intFromEnum(opcode)); | ||
| 925 | try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } }); | ||
| 926 | } | 868 | } |
| 927 | 869 | ||
| 928 | fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void { | 870 | fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void { |
| 929 | try func.addInst(.{ .tag = tag, .data = .{ .label = label } }); | 871 | try cg.addInst(.{ .tag = tag, .data = .{ .local = local } }); |
| 872 | } | ||
| 873 | |||
| 874 | fn addFuncTy(cg: *CodeGen, tag: Mir.Inst.Tag, i: Wasm.FunctionType.Index) error{OutOfMemory}!void { | ||
| 875 | try cg.addInst(.{ .tag = tag, .data = .{ .func_ty = i } }); | ||
| 930 | } | 876 | } |
| 931 | 877 | ||
| 932 | /// Accepts an unsigned 32bit integer rather than a signed integer to | 878 | /// Accepts an unsigned 32bit integer rather than a signed integer to |
| 933 | /// prevent us from having to bitcast multiple times as most values | 879 | /// prevent us from having to bitcast multiple times as most values |
| 934 | /// within codegen are represented as unsigned rather than signed. | 880 | /// within codegen are represented as unsigned rather than signed. |
| 935 | fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void { | 881 | fn addImm32(cg: *CodeGen, imm: u32) error{OutOfMemory}!void { |
| 936 | try func.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } }); | 882 | try cg.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } }); |
| 937 | } | 883 | } |
| 938 | 884 | ||
| 939 | /// Accepts an unsigned 64bit integer rather than a signed integer to | 885 | /// Accepts an unsigned 64bit integer rather than a signed integer to |
| 940 | /// prevent us from having to bitcast multiple times as most values | 886 | /// prevent us from having to bitcast multiple times as most values |
| 941 | /// within codegen are represented as unsigned rather than signed. | 887 | /// within codegen are represented as unsigned rather than signed. |
| 942 | fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void { | 888 | fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void { |
| 943 | const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm)); | 889 | const extra_index = try cg.addExtra(Mir.Imm64.init(imm)); |
| 944 | try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); | 890 | try cg.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); |
| 945 | } | 891 | } |
| 946 | 892 | ||
| 947 | /// Accepts the index into the list of 128bit-immediates | 893 | /// Accepts the index into the list of 128bit-immediates |
| 948 | fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void { | 894 | fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void { |
| 949 | const simd_values = func.simd_immediates.items[index]; | 895 | const simd_values = cg.simd_immediates.items[index]; |
| 950 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | 896 | const extra_index = cg.extraLen(); |
| 951 | // tag + 128bit value | 897 | // tag + 128bit value |
| 952 | try func.mir_extra.ensureUnusedCapacity(func.gpa, 5); | 898 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5); |
| 953 | func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const)); | 899 | cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const)); |
| 954 | func.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values))); | 900 | cg.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values))); |
| 955 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 901 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 956 | } | 902 | } |
| 957 | 903 | ||
| 958 | fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void { | 904 | fn addFloat64(cg: *CodeGen, float: f64) error{OutOfMemory}!void { |
| 959 | const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float)); | 905 | const extra_index = try cg.addExtra(Mir.Float64.init(float)); |
| 960 | try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); | 906 | try cg.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); |
| 961 | } | 907 | } |
| 962 | 908 | ||
| 963 | /// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`. | 909 | /// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`. |
| 964 | fn addMemArg(func: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void { | 910 | fn addMemArg(cg: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void { |
| 965 | const extra_index = try func.addExtra(mem_arg); | 911 | const extra_index = try cg.addExtra(mem_arg); |
| 966 | try func.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } }); | 912 | try cg.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } }); |
| 967 | } | 913 | } |
| 968 | 914 | ||
| 969 | /// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the | 915 | /// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the |
| 970 | /// given `tag`. | 916 | /// given `tag`. |
| 971 | fn addAtomicMemArg(func: *CodeGen, tag: wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void { | 917 | fn addAtomicMemArg(cg: *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) })); | 918 | const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) })); |
| 973 | _ = try func.addExtra(mem_arg); | 919 | _ = try cg.addExtra(mem_arg); |
| 974 | try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); | 920 | try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); |
| 975 | } | 921 | } |
| 976 | 922 | ||
| 977 | /// Helper function to emit atomic mir opcodes. | 923 | /// Helper function to emit atomic mir opcodes. |
| 978 | fn addAtomicTag(func: *CodeGen, tag: wasm.AtomicsOpcode) error{OutOfMemory}!void { | 924 | fn addAtomicTag(cg: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void { |
| 979 | const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) })); | 925 | const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) })); |
| 980 | try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); | 926 | try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); |
| 981 | } | 927 | } |
| 982 | 928 | ||
| 983 | /// Appends entries to `mir_extra` based on the type of `extra`. | 929 | /// Appends entries to `mir_extra` based on the type of `extra`. |
| 984 | /// Returns the index into `mir_extra` | 930 | /// Returns the index into `mir_extra` |
| 985 | fn addExtra(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { | 931 | fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { |
| 986 | const fields = std.meta.fields(@TypeOf(extra)); | 932 | const fields = std.meta.fields(@TypeOf(extra)); |
| 987 | try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len); | 933 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, fields.len); |
| 988 | return func.addExtraAssumeCapacity(extra); | 934 | return cg.addExtraAssumeCapacity(extra); |
| 989 | } | 935 | } |
| 990 | 936 | ||
| 991 | /// Appends entries to `mir_extra` based on the type of `extra`. | 937 | /// Appends entries to `mir_extra` based on the type of `extra`. |
| 992 | /// Returns the index into `mir_extra` | 938 | /// Returns the index into `mir_extra` |
| 993 | fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { | 939 | fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { |
| 994 | const fields = std.meta.fields(@TypeOf(extra)); | 940 | const fields = std.meta.fields(@TypeOf(extra)); |
| 995 | const result = @as(u32, @intCast(func.mir_extra.items.len)); | 941 | const result = cg.extraLen(); |
| 996 | inline for (fields) |field| { | 942 | inline for (fields) |field| { |
| 997 | func.mir_extra.appendAssumeCapacity(switch (field.type) { | 943 | cg.mir_extra.appendAssumeCapacity(switch (field.type) { |
| 998 | u32 => @field(extra, field.name), | 944 | u32 => @field(extra, field.name), |
| 945 | i32 => @bitCast(@field(extra, field.name)), | ||
| 946 | InternPool.Index, | ||
| 947 | InternPool.Nav.Index, | ||
| 948 | Wasm.UavsObjIndex, | ||
| 949 | Wasm.UavsExeIndex, | ||
| 950 | => @intFromEnum(@field(extra, field.name)), | ||
| 999 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), | 951 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 1000 | }); | 952 | }); |
| 1001 | } | 953 | } |
| 1002 | return result; | 954 | return result; |
| 1003 | } | 955 | } |
| 1004 | 956 | ||
| 1005 | /// Using a given `Type`, returns the corresponding valtype for .auto callconv | 957 | /// For `std.builtin.CallingConvention.auto`. |
| 1006 | fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { | 958 | pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.Valtype { |
| 1007 | const zcu = pt.zcu; | ||
| 1008 | const ip = &zcu.intern_pool; | 959 | const ip = &zcu.intern_pool; |
| 1009 | return switch (ty.zigTypeTag(zcu)) { | 960 | return switch (ty.zigTypeTag(zcu)) { |
| 1010 | .float => switch (ty.floatBits(target)) { | 961 | .float => switch (ty.floatBits(target.*)) { |
| 1011 | 16 => .i32, // stored/loaded as u16 | 962 | 16 => .i32, // stored/loaded as u16 |
| 1012 | 32 => .f32, | 963 | 32 => .f32, |
| 1013 | 64 => .f64, | 964 | 64 => .f64, |
| ... | @@ -1022,19 +973,20 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { | ... | @@ -1022,19 +973,20 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { |
| 1022 | .@"struct" => blk: { | 973 | .@"struct" => blk: { |
| 1023 | if (zcu.typeToPackedStruct(ty)) |packed_struct| { | 974 | if (zcu.typeToPackedStruct(ty)) |packed_struct| { |
| 1024 | const backing_int_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); | 975 | const backing_int_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); |
| 1025 | break :blk typeToValtype(backing_int_ty, pt, target); | 976 | break :blk typeToValtype(backing_int_ty, zcu, target); |
| 1026 | } else { | 977 | } else { |
| 1027 | break :blk .i32; | 978 | break :blk .i32; |
| 1028 | } | 979 | } |
| 1029 | }, | 980 | }, |
| 1030 | .vector => switch (determineSimdStoreStrategy(ty, zcu, target)) { | 981 | .vector => switch (CodeGen.determineSimdStoreStrategy(ty, zcu, target)) { |
| 1031 | .direct => .v128, | 982 | .direct => .v128, |
| 1032 | .unrolled => .i32, | 983 | .unrolled => .i32, |
| 1033 | }, | 984 | }, |
| 1034 | .@"union" => switch (ty.containerLayout(zcu)) { | 985 | .@"union" => switch (ty.containerLayout(zcu)) { |
| 1035 | .@"packed" => blk: { | 986 | .@"packed" => switch (ty.bitSize(zcu)) { |
| 1036 | const int_ty = pt.intType(.unsigned, @as(u16, @intCast(ty.bitSize(zcu)))) catch @panic("out of memory"); | 987 | 0...32 => .i32, |
| 1037 | break :blk typeToValtype(int_ty, pt, target); | 988 | 33...64 => .i64, |
| 989 | else => .i32, | ||
| 1038 | }, | 990 | }, |
| 1039 | else => .i32, | 991 | else => .i32, |
| 1040 | }, | 992 | }, |
| ... | @@ -1042,42 +994,94 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { | ... | @@ -1042,42 +994,94 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { |
| 1042 | }; | 994 | }; |
| 1043 | } | 995 | } |
| 1044 | 996 | ||
| 1045 | /// Using a given `Type`, returns the byte representation of its wasm value type | ||
| 1046 | fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 { | ||
| 1047 | return wasm.valtype(typeToValtype(ty, pt, target)); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | /// Using a given `Type`, returns the corresponding wasm value type | 997 | /// Using a given `Type`, returns the corresponding wasm value type |
| 1051 | /// Differently from `genValtype` this also allows `void` to create a block | 998 | /// Differently from `typeToValtype` this also allows `void` to create a block |
| 1052 | /// with no return type | 999 | /// with no return type |
| 1053 | fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 { | 1000 | fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.BlockType { |
| 1054 | return switch (ty.ip_index) { | 1001 | return switch (ty.ip_index) { |
| 1055 | .void_type, .noreturn_type => wasm.block_empty, | 1002 | .void_type, .noreturn_type => .empty, |
| 1056 | else => genValtype(ty, pt, target), | 1003 | else => .fromValtype(typeToValtype(ty, zcu, target)), |
| 1057 | }; | 1004 | }; |
| 1058 | } | 1005 | } |
| 1059 | 1006 | ||
| 1060 | /// Writes the bytecode depending on the given `WValue` in `val` | 1007 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 1061 | fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { | 1008 | fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 1062 | switch (value) { | 1009 | switch (value) { |
| 1063 | .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?) | 1010 | .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?) |
| 1064 | .none, .stack => {}, // no-op | 1011 | .none, .stack => {}, // no-op |
| 1065 | .local => |idx| try func.addLabel(.local_get, idx.value), | 1012 | .local => |idx| try cg.addLocal(.local_get, idx.value), |
| 1066 | .imm32 => |val| try func.addImm32(val), | 1013 | .imm32 => |val| try cg.addImm32(val), |
| 1067 | .imm64 => |val| try func.addImm64(val), | 1014 | .imm64 => |val| try cg.addImm64(val), |
| 1068 | .imm128 => |val| try func.addImm128(val), | 1015 | .imm128 => |val| try cg.addImm128(val), |
| 1069 | .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | 1016 | .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 1070 | .float64 => |val| try func.addFloat64(val), | 1017 | .float64 => |val| try cg.addFloat64(val), |
| 1071 | .memory => |ptr| { | 1018 | .nav_ref => |nav_ref| { |
| 1072 | const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 }); | 1019 | const wasm = cg.wasm; |
| 1073 | try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); | 1020 | const comp = wasm.base.comp; |
| 1021 | const zcu = comp.zcu.?; | ||
| 1022 | const ip = &zcu.intern_pool; | ||
| 1023 | if (ip.getNav(nav_ref.nav_index).isFn(ip)) { | ||
| 1024 | assert(nav_ref.offset == 0); | ||
| 1025 | const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index); | ||
| 1026 | if (!gop.found_existing) gop.value_ptr.* = {}; | ||
| 1027 | try cg.addInst(.{ | ||
| 1028 | .tag = .func_ref, | ||
| 1029 | .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, | ||
| 1030 | }); | ||
| 1031 | } else if (nav_ref.offset == 0) { | ||
| 1032 | try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } }); | ||
| 1033 | } else { | ||
| 1034 | try cg.addInst(.{ | ||
| 1035 | .tag = .nav_ref_off, | ||
| 1036 | .data = .{ | ||
| 1037 | .payload = try cg.addExtra(Mir.NavRefOff{ | ||
| 1038 | .nav_index = nav_ref.nav_index, | ||
| 1039 | .offset = nav_ref.offset, | ||
| 1040 | }), | ||
| 1041 | }, | ||
| 1042 | }); | ||
| 1043 | } | ||
| 1074 | }, | 1044 | }, |
| 1075 | .memory_offset => |mem_off| { | 1045 | .uav_ref => |uav| { |
| 1076 | const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset }); | 1046 | const wasm = cg.wasm; |
| 1077 | try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); | 1047 | const comp = wasm.base.comp; |
| 1048 | const is_obj = comp.config.output_mode == .Obj; | ||
| 1049 | const zcu = comp.zcu.?; | ||
| 1050 | const ip = &zcu.intern_pool; | ||
| 1051 | if (ip.isFunctionType(ip.typeOf(uav.ip_index))) { | ||
| 1052 | assert(uav.offset == 0); | ||
| 1053 | const owner_nav = ip.toFunc(uav.ip_index).owner_nav; | ||
| 1054 | const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav); | ||
| 1055 | if (!gop.found_existing) gop.value_ptr.* = {}; | ||
| 1056 | try cg.addInst(.{ | ||
| 1057 | .tag = .func_ref, | ||
| 1058 | .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, | ||
| 1059 | }); | ||
| 1060 | } else if (uav.offset == 0) { | ||
| 1061 | try cg.addInst(.{ | ||
| 1062 | .tag = .uav_ref, | ||
| 1063 | .data = if (is_obj) .{ | ||
| 1064 | .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), | ||
| 1065 | } else .{ | ||
| 1066 | .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty), | ||
| 1067 | }, | ||
| 1068 | }); | ||
| 1069 | } else { | ||
| 1070 | try cg.addInst(.{ | ||
| 1071 | .tag = .uav_ref_off, | ||
| 1072 | .data = .{ | ||
| 1073 | .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{ | ||
| 1074 | .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), | ||
| 1075 | .offset = uav.offset, | ||
| 1076 | }) else try cg.addExtra(Mir.UavRefOffExe{ | ||
| 1077 | .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty), | ||
| 1078 | .offset = uav.offset, | ||
| 1079 | }), | ||
| 1080 | }, | ||
| 1081 | }); | ||
| 1082 | } | ||
| 1078 | }, | 1083 | }, |
| 1079 | .function_index => |index| try func.addLabel(.function_index, index), // write function index and generate relocation | 1084 | .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value), // caller must ensure to address the offset |
| 1080 | .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset | ||
| 1081 | } | 1085 | } |
| 1082 | } | 1086 | } |
| 1083 | 1087 | ||
| ... | @@ -1085,7 +1089,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { | ... | @@ -1085,7 +1089,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { |
| 1085 | /// The old `WValue` found at instruction `ref` is then replaced by the | 1089 | /// The old `WValue` found at instruction `ref` is then replaced by the |
| 1086 | /// modified `WValue` and returned. When given a non-local or non-stack-offset, | 1090 | /// modified `WValue` and returned. When given a non-local or non-stack-offset, |
| 1087 | /// returns the given `operand` itfunc instead. | 1091 | /// returns the given `operand` itfunc instead. |
| 1088 | fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue { | 1092 | fn reuseOperand(cg: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue { |
| 1089 | if (operand != .local and operand != .stack_offset) return operand; | 1093 | if (operand != .local and operand != .stack_offset) return operand; |
| 1090 | var new_value = operand; | 1094 | var new_value = operand; |
| 1091 | switch (new_value) { | 1095 | switch (new_value) { |
| ... | @@ -1093,17 +1097,17 @@ fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue { | ... | @@ -1093,17 +1097,17 @@ fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue { |
| 1093 | .stack_offset => |*stack_offset| stack_offset.references += 1, | 1097 | .stack_offset => |*stack_offset| stack_offset.references += 1, |
| 1094 | else => unreachable, | 1098 | else => unreachable, |
| 1095 | } | 1099 | } |
| 1096 | const old_value = func.getResolvedInst(ref); | 1100 | const old_value = cg.getResolvedInst(ref); |
| 1097 | old_value.* = new_value; | 1101 | old_value.* = new_value; |
| 1098 | return new_value; | 1102 | return new_value; |
| 1099 | } | 1103 | } |
| 1100 | 1104 | ||
| 1101 | /// From a reference, returns its resolved `WValue`. | 1105 | /// From a reference, returns its resolved `WValue`. |
| 1102 | /// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet. | 1106 | /// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet. |
| 1103 | fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { | 1107 | fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 1104 | var index = func.branches.items.len; | 1108 | var index = cg.branches.items.len; |
| 1105 | while (index > 0) : (index -= 1) { | 1109 | while (index > 0) : (index -= 1) { |
| 1106 | const branch = func.branches.items[index - 1]; | 1110 | const branch = cg.branches.items[index - 1]; |
| 1107 | if (branch.values.getPtr(ref)) |value| { | 1111 | if (branch.values.getPtr(ref)) |value| { |
| 1108 | return value; | 1112 | return value; |
| 1109 | } | 1113 | } |
| ... | @@ -1113,243 +1117,238 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { | ... | @@ -1113,243 +1117,238 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 1113 | 1117 | ||
| 1114 | /// Creates one locals for a given `Type`. | 1118 | /// Creates one locals for a given `Type`. |
| 1115 | /// Returns a corresponding `Wvalue` with `local` as active tag | 1119 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 1116 | fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | 1120 | fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1117 | const pt = func.pt; | 1121 | const zcu = cg.pt.zcu; |
| 1118 | const valtype = typeToValtype(ty, pt, func.target.*); | 1122 | const valtype = typeToValtype(ty, zcu, cg.target); |
| 1119 | const index_or_null = switch (valtype) { | 1123 | const index_or_null = switch (valtype) { |
| 1120 | .i32 => func.free_locals_i32.popOrNull(), | 1124 | .i32 => cg.free_locals_i32.popOrNull(), |
| 1121 | .i64 => func.free_locals_i64.popOrNull(), | 1125 | .i64 => cg.free_locals_i64.popOrNull(), |
| 1122 | .f32 => func.free_locals_f32.popOrNull(), | 1126 | .f32 => cg.free_locals_f32.popOrNull(), |
| 1123 | .f64 => func.free_locals_f64.popOrNull(), | 1127 | .f64 => cg.free_locals_f64.popOrNull(), |
| 1124 | .v128 => func.free_locals_v128.popOrNull(), | 1128 | .v128 => cg.free_locals_v128.popOrNull(), |
| 1125 | }; | 1129 | }; |
| 1126 | if (index_or_null) |index| { | 1130 | if (index_or_null) |index| { |
| 1127 | log.debug("reusing local ({d}) of type {}", .{ index, valtype }); | 1131 | log.debug("reusing local ({d}) of type {}", .{ index, valtype }); |
| 1128 | return .{ .local = .{ .value = index, .references = 1 } }; | 1132 | return .{ .local = .{ .value = index, .references = 1 } }; |
| 1129 | } | 1133 | } |
| 1130 | log.debug("new local of type {}", .{valtype}); | 1134 | log.debug("new local of type {}", .{valtype}); |
| 1131 | return func.ensureAllocLocal(ty); | 1135 | return cg.ensureAllocLocal(ty); |
| 1132 | } | 1136 | } |
| 1133 | 1137 | ||
| 1134 | /// Ensures a new local will be created. This is useful when it's useful | 1138 | /// Ensures a new local will be created. This is useful when it's useful |
| 1135 | /// to use a zero-initialized local. | 1139 | /// to use a zero-initialized local. |
| 1136 | fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | 1140 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1137 | const pt = func.pt; | 1141 | const zcu = cg.pt.zcu; |
| 1138 | try func.locals.append(func.gpa, genValtype(ty, pt, func.target.*)); | 1142 | try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); |
| 1139 | const initial_index = func.local_index; | 1143 | const initial_index = cg.local_index; |
| 1140 | func.local_index += 1; | 1144 | cg.local_index += 1; |
| 1141 | return .{ .local = .{ .value = initial_index, .references = 1 } }; | 1145 | return .{ .local = .{ .value = initial_index, .references = 1 } }; |
| 1142 | } | 1146 | } |
| 1143 | 1147 | ||
| 1144 | /// Generates a `wasm.Type` from a given function type. | 1148 | pub const Function = extern struct { |
| 1145 | /// Memory is owned by the caller. | 1149 | /// Index into `Wasm.mir_instructions`. |
| 1146 | fn genFunctype( | 1150 | mir_off: u32, |
| 1147 | gpa: Allocator, | 1151 | /// This is unused except for as a safety slice bound and could be removed. |
| 1148 | cc: std.builtin.CallingConvention, | 1152 | mir_len: u32, |
| 1149 | params: []const InternPool.Index, | 1153 | /// Index into `Wasm.mir_extra`. |
| 1150 | return_type: Type, | 1154 | mir_extra_off: u32, |
| 1151 | pt: Zcu.PerThread, | 1155 | /// This is unused except for as a safety slice bound and could be removed. |
| 1152 | target: std.Target, | 1156 | mir_extra_len: u32, |
| 1153 | ) !wasm.Type { | 1157 | locals_off: u32, |
| 1154 | const zcu = pt.zcu; | 1158 | locals_len: u32, |
| 1155 | var temp_params = std.ArrayList(wasm.Valtype).init(gpa); | 1159 | prologue: Prologue, |
| 1156 | defer temp_params.deinit(); | 1160 | |
| 1157 | var returns = std.ArrayList(wasm.Valtype).init(gpa); | 1161 | pub const Prologue = extern struct { |
| 1158 | defer returns.deinit(); | 1162 | flags: Flags, |
| 1159 | 1163 | sp_local: u32, | |
| 1160 | if (firstParamSRet(cc, return_type, pt, target)) { | 1164 | stack_size: u32, |
| 1161 | try temp_params.append(.i32); // memory address is always a 32-bit handle | 1165 | bottom_stack_local: u32, |
| 1162 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { | 1166 | |
| 1163 | if (cc == .wasm_watc) { | 1167 | pub const Flags = packed struct(u32) { |
| 1164 | const res_classes = abi.classifyType(return_type, zcu); | 1168 | stack_alignment: Alignment, |
| 1165 | assert(res_classes[0] == .direct and res_classes[1] == .none); | 1169 | padding: u26 = 0, |
| 1166 | const scalar_type = abi.scalarType(return_type, zcu); | 1170 | }; |
| 1167 | try returns.append(typeToValtype(scalar_type, pt, target)); | 1171 | |
| 1168 | } else { | 1172 | pub const none: Prologue = .{ |
| 1169 | try returns.append(typeToValtype(return_type, pt, target)); | 1173 | .sp_local = 0, |
| 1174 | .flags = .{ .stack_alignment = .none }, | ||
| 1175 | .stack_size = 0, | ||
| 1176 | .bottom_stack_local = 0, | ||
| 1177 | }; | ||
| 1178 | |||
| 1179 | pub fn isNone(p: *const Prologue) bool { | ||
| 1180 | return p.flags.stack_alignment != .none; | ||
| 1170 | } | 1181 | } |
| 1171 | } else if (return_type.isError(zcu)) { | 1182 | }; |
| 1172 | try returns.append(.i32); | 1183 | |
| 1173 | } | 1184 | pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { |
| 1174 | 1185 | const gpa = wasm.base.comp.gpa; | |
| 1175 | // param types | 1186 | |
| 1176 | for (params) |param_type_ip| { | 1187 | // Write the locals in the prologue of the function body. |
| 1177 | const param_type = Type.fromInterned(param_type_ip); | 1188 | const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len]; |
| 1178 | if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; | 1189 | try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38); |
| 1179 | 1190 | ||
| 1180 | switch (cc) { | 1191 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable; |
| 1181 | .wasm_watc => { | 1192 | for (locals) |local| { |
| 1182 | const param_classes = abi.classifyType(param_type, zcu); | 1193 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; |
| 1183 | if (param_classes[1] == .none) { | 1194 | code.appendAssumeCapacity(@intFromEnum(local)); |
| 1184 | if (param_classes[0] == .direct) { | 1195 | } |
| 1185 | const scalar_type = abi.scalarType(param_type, zcu); | 1196 | |
| 1186 | try temp_params.append(typeToValtype(scalar_type, pt, target)); | 1197 | // Stack management section of function prologue. |
| 1187 | } else { | 1198 | const stack_alignment = f.prologue.flags.stack_alignment; |
| 1188 | try temp_params.append(typeToValtype(param_type, pt, target)); | 1199 | if (stack_alignment.toByteUnits()) |align_bytes| { |
| 1189 | } | 1200 | const sp_global: Wasm.GlobalIndex = .stack_pointer; |
| 1190 | } else { | 1201 | // load stack pointer |
| 1191 | // i128/f128 | 1202 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); |
| 1192 | try temp_params.append(.i64); | 1203 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; |
| 1193 | try temp_params.append(.i64); | 1204 | // store stack pointer so we can restore it when we return from the function |
| 1194 | } | 1205 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); |
| 1195 | }, | 1206 | leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable; |
| 1196 | else => try temp_params.append(typeToValtype(param_type, pt, target)), | 1207 | // get the total stack size |
| 1208 | const aligned_stack: i32 = @intCast(stack_alignment.forward(f.prologue.stack_size)); | ||
| 1209 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1210 | leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable; | ||
| 1211 | // subtract it from the current stack pointer | ||
| 1212 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub)); | ||
| 1213 | // Get negative stack alignment | ||
| 1214 | const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; | ||
| 1215 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1216 | leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable; | ||
| 1217 | // Bitwise-and the value to get the new stack pointer to ensure the | ||
| 1218 | // pointers are aligned with the abi alignment. | ||
| 1219 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and)); | ||
| 1220 | // The bottom will be used to calculate all stack pointer offsets. | ||
| 1221 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); | ||
| 1222 | leb.writeUleb128(code.fixedWriter(), f.prologue.bottom_stack_local) catch unreachable; | ||
| 1223 | // Store the current stack pointer value into the global stack pointer so other function calls will | ||
| 1224 | // start from this value instead and not overwrite the current stack. | ||
| 1225 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); | ||
| 1226 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | ||
| 1197 | } | 1227 | } |
| 1228 | |||
| 1229 | var emit: Emit = .{ | ||
| 1230 | .mir = .{ | ||
| 1231 | .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len], | ||
| 1232 | .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len], | ||
| 1233 | .extra = wasm.mir_extra.items[f.mir_extra_off..][0..f.mir_extra_len], | ||
| 1234 | }, | ||
| 1235 | .wasm = wasm, | ||
| 1236 | .code = code, | ||
| 1237 | }; | ||
| 1238 | try emit.lowerToCode(); | ||
| 1198 | } | 1239 | } |
| 1240 | }; | ||
| 1199 | 1241 | ||
| 1200 | return wasm.Type{ | 1242 | pub const Error = error{ |
| 1201 | .params = try temp_params.toOwnedSlice(), | 1243 | OutOfMemory, |
| 1202 | .returns = try returns.toOwnedSlice(), | 1244 | /// Compiler was asked to operate on a number larger than supported. |
| 1203 | }; | 1245 | Overflow, |
| 1204 | } | 1246 | /// Indicates the error is already stored in Zcu `failed_codegen`. |
| 1247 | CodegenFail, | ||
| 1248 | }; | ||
| 1205 | 1249 | ||
| 1206 | pub fn generate( | 1250 | pub fn function( |
| 1207 | bin_file: *link.File, | 1251 | wasm: *Wasm, |
| 1208 | pt: Zcu.PerThread, | 1252 | pt: Zcu.PerThread, |
| 1209 | src_loc: Zcu.LazySrcLoc, | ||
| 1210 | func_index: InternPool.Index, | 1253 | func_index: InternPool.Index, |
| 1211 | air: Air, | 1254 | air: Air, |
| 1212 | liveness: Liveness, | 1255 | liveness: Liveness, |
| 1213 | code: *std.ArrayList(u8), | 1256 | ) Error!Function { |
| 1214 | debug_output: link.File.DebugInfoOutput, | ||
| 1215 | ) codegen.CodeGenError!codegen.Result { | ||
| 1216 | const zcu = pt.zcu; | 1257 | const zcu = pt.zcu; |
| 1217 | const gpa = zcu.gpa; | 1258 | const gpa = zcu.gpa; |
| 1218 | const func = zcu.funcInfo(func_index); | 1259 | const cg = zcu.funcInfo(func_index); |
| 1219 | const file_scope = zcu.navFileScope(func.owner_nav); | 1260 | const file_scope = zcu.navFileScope(cg.owner_nav); |
| 1220 | const target = &file_scope.mod.resolved_target.result; | 1261 | const target = &file_scope.mod.resolved_target.result; |
| 1262 | const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu); | ||
| 1263 | const fn_info = zcu.typeToFunc(fn_ty).?; | ||
| 1264 | const ip = &zcu.intern_pool; | ||
| 1265 | const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); | ||
| 1266 | const returns = fn_ty_index.ptr(wasm).returns.slice(wasm); | ||
| 1267 | const any_returns = returns.len != 0; | ||
| 1268 | |||
| 1269 | var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target); | ||
| 1270 | defer cc_result.deinit(gpa); | ||
| 1271 | |||
| 1221 | var code_gen: CodeGen = .{ | 1272 | var code_gen: CodeGen = .{ |
| 1222 | .gpa = gpa, | 1273 | .gpa = gpa, |
| 1223 | .pt = pt, | 1274 | .pt = pt, |
| 1224 | .air = air, | 1275 | .air = air, |
| 1225 | .liveness = liveness, | 1276 | .liveness = liveness, |
| 1226 | .code = code, | 1277 | .owner_nav = cg.owner_nav, |
| 1227 | .owner_nav = func.owner_nav, | ||
| 1228 | .src_loc = src_loc, | ||
| 1229 | .err_msg = undefined, | ||
| 1230 | .locals = .{}, | ||
| 1231 | .target = target, | 1278 | .target = target, |
| 1232 | .bin_file = bin_file.cast(.wasm).?, | 1279 | .ptr_size = switch (target.cpu.arch) { |
| 1233 | .debug_output = debug_output, | 1280 | .wasm32 => .wasm32, |
| 1281 | .wasm64 => .wasm64, | ||
| 1282 | else => unreachable, | ||
| 1283 | }, | ||
| 1284 | .wasm = wasm, | ||
| 1234 | .func_index = func_index, | 1285 | .func_index = func_index, |
| 1286 | .args = cc_result.args, | ||
| 1287 | .return_value = cc_result.return_value, | ||
| 1288 | .local_index = cc_result.local_index, | ||
| 1289 | .mir_instructions = &wasm.mir_instructions, | ||
| 1290 | .mir_extra = &wasm.mir_extra, | ||
| 1291 | .locals = &wasm.all_zcu_locals, | ||
| 1292 | .start_mir_extra_off = @intCast(wasm.mir_extra.items.len), | ||
| 1293 | .start_locals_off = @intCast(wasm.all_zcu_locals.items.len), | ||
| 1235 | }; | 1294 | }; |
| 1236 | defer code_gen.deinit(); | 1295 | defer code_gen.deinit(); |
| 1237 | 1296 | ||
| 1238 | genFunc(&code_gen) catch |err| switch (err) { | 1297 | return functionInner(&code_gen, any_returns) catch |err| switch (err) { |
| 1239 | error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg }, | 1298 | error.CodegenFail => return error.CodegenFail, |
| 1240 | else => |e| return e, | 1299 | else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), |
| 1241 | }; | 1300 | }; |
| 1242 | |||
| 1243 | return codegen.Result.ok; | ||
| 1244 | } | 1301 | } |
| 1245 | 1302 | ||
| 1246 | fn genFunc(func: *CodeGen) InnerError!void { | 1303 | fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { |
| 1247 | const pt = func.pt; | 1304 | const wasm = cg.wasm; |
| 1248 | const zcu = pt.zcu; | 1305 | const zcu = cg.pt.zcu; |
| 1249 | const ip = &zcu.intern_pool; | ||
| 1250 | const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu); | ||
| 1251 | 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.*); | ||
| 1253 | defer func_type.deinit(func.gpa); | ||
| 1254 | _ = try func.bin_file.storeNavType(func.owner_nav, func_type); | ||
| 1255 | |||
| 1256 | var cc_result = try func.resolveCallingConventionValues(fn_ty); | ||
| 1257 | defer cc_result.deinit(func.gpa); | ||
| 1258 | |||
| 1259 | func.args = cc_result.args; | ||
| 1260 | func.return_value = cc_result.return_value; | ||
| 1261 | 1306 | ||
| 1262 | try func.addTag(.dbg_prologue_end); | 1307 | const start_mir_off: u32 = @intCast(wasm.mir_instructions.len); |
| 1263 | 1308 | ||
| 1264 | try func.branches.append(func.gpa, .{}); | 1309 | try cg.branches.append(cg.gpa, .{}); |
| 1265 | // clean up outer branch | 1310 | // clean up outer branch |
| 1266 | defer { | 1311 | defer { |
| 1267 | var outer_branch = func.branches.pop(); | 1312 | var outer_branch = cg.branches.pop(); |
| 1268 | outer_branch.deinit(func.gpa); | 1313 | outer_branch.deinit(cg.gpa); |
| 1269 | assert(func.branches.items.len == 0); // missing branch merge | 1314 | assert(cg.branches.items.len == 0); // missing branch merge |
| 1270 | } | 1315 | } |
| 1271 | // Generate MIR for function body | 1316 | // Generate MIR for function body |
| 1272 | try func.genBody(func.air.getMainBody()); | 1317 | try cg.genBody(cg.air.getMainBody()); |
| 1273 | 1318 | ||
| 1274 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) | 1319 | // 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. | 1320 | // 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) { | 1321 | if (any_returns and cg.air.instructions.len > 0) { |
| 1277 | const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1); | 1322 | const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1); |
| 1278 | const last_inst_ty = func.typeOfIndex(inst); | 1323 | const last_inst_ty = cg.typeOfIndex(inst); |
| 1279 | if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) { | 1324 | if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) { |
| 1280 | try func.addTag(.@"unreachable"); | 1325 | try cg.addTag(.@"unreachable"); |
| 1281 | } | 1326 | } |
| 1282 | } | 1327 | } |
| 1283 | // End of function body | 1328 | // End of function body |
| 1284 | try func.addTag(.end); | 1329 | try cg.addTag(.end); |
| 1285 | 1330 | try cg.addTag(.dbg_epilogue_begin); | |
| 1286 | try func.addTag(.dbg_epilogue_begin); | 1331 | |
| 1287 | 1332 | return .{ | |
| 1288 | // check if we have to initialize and allocate anything into the stack frame. | 1333 | .mir_off = start_mir_off, |
| 1289 | // If so, create enough stack space and insert the instructions at the front of the list. | 1334 | .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off), |
| 1290 | if (func.initial_stack_value != .none) { | 1335 | .mir_extra_off = cg.start_mir_extra_off, |
| 1291 | var prologue = std.ArrayList(Mir.Inst).init(func.gpa); | 1336 | .mir_extra_len = cg.extraLen(), |
| 1292 | defer prologue.deinit(); | 1337 | .locals_off = cg.start_locals_off, |
| 1293 | 1338 | .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off), | |
| 1294 | const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym); | 1339 | .prologue = if (cg.initial_stack_value == .none) .none else .{ |
| 1295 | // load stack pointer | 1340 | .sp_local = cg.initial_stack_value.local.value, |
| 1296 | try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } }); | 1341 | .flags = .{ .stack_alignment = cg.stack_alignment }, |
| 1297 | // store stack pointer so we can restore it when we return from the function | 1342 | .stack_size = cg.stack_size, |
| 1298 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } }); | 1343 | .bottom_stack_local = cg.bottom_stack_value.local.value, |
| 1299 | // get the total stack size | ||
| 1300 | const aligned_stack = func.stack_alignment.forward(func.stack_size); | ||
| 1301 | try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(aligned_stack) } }); | ||
| 1302 | // subtract it from the current stack pointer | ||
| 1303 | try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } }); | ||
| 1304 | // Get negative stack alignment | ||
| 1305 | try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @as(i32, @intCast(func.stack_alignment.toByteUnits().?)) * -1 } }); | ||
| 1306 | // Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment | ||
| 1307 | try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } }); | ||
| 1308 | // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets | ||
| 1309 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } }); | ||
| 1310 | // Store the current stack pointer value into the global stack pointer so other function calls will | ||
| 1311 | // start from this value instead and not overwrite the current stack. | ||
| 1312 | try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } }); | ||
| 1313 | |||
| 1314 | // reserve space and insert all prologue instructions at the front of the instruction list | ||
| 1315 | // We insert them in reserve order as there is no insertSlice in multiArrayList. | ||
| 1316 | try func.mir_instructions.ensureUnusedCapacity(func.gpa, prologue.items.len); | ||
| 1317 | for (prologue.items, 0..) |_, index| { | ||
| 1318 | const inst = prologue.items[prologue.items.len - 1 - index]; | ||
| 1319 | func.mir_instructions.insertAssumeCapacity(0, inst); | ||
| 1320 | } | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | var mir: Mir = .{ | ||
| 1324 | .instructions = func.mir_instructions.toOwnedSlice(), | ||
| 1325 | .extra = try func.mir_extra.toOwnedSlice(func.gpa), | ||
| 1326 | }; | ||
| 1327 | defer mir.deinit(func.gpa); | ||
| 1328 | |||
| 1329 | var emit: Emit = .{ | ||
| 1330 | .mir = mir, | ||
| 1331 | .bin_file = func.bin_file, | ||
| 1332 | .code = func.code, | ||
| 1333 | .locals = func.locals.items, | ||
| 1334 | .owner_nav = func.owner_nav, | ||
| 1335 | .dbg_output = func.debug_output, | ||
| 1336 | .prev_di_line = 0, | ||
| 1337 | .prev_di_column = 0, | ||
| 1338 | .prev_di_offset = 0, | ||
| 1339 | }; | ||
| 1340 | |||
| 1341 | emit.emitMir() catch |err| switch (err) { | ||
| 1342 | error.EmitFail => { | ||
| 1343 | func.err_msg = emit.error_msg.?; | ||
| 1344 | return error.CodegenFail; | ||
| 1345 | }, | 1344 | }, |
| 1346 | else => |e| return e, | ||
| 1347 | }; | 1345 | }; |
| 1348 | } | 1346 | } |
| 1349 | 1347 | ||
| 1350 | const CallWValues = struct { | 1348 | const CallWValues = struct { |
| 1351 | args: []WValue, | 1349 | args: []WValue, |
| 1352 | return_value: WValue, | 1350 | return_value: WValue, |
| 1351 | local_index: u32, | ||
| 1353 | 1352 | ||
| 1354 | fn deinit(values: *CallWValues, gpa: Allocator) void { | 1353 | fn deinit(values: *CallWValues, gpa: Allocator) void { |
| 1355 | gpa.free(values.args); | 1354 | gpa.free(values.args); |
| ... | @@ -1357,28 +1356,33 @@ const CallWValues = struct { | ... | @@ -1357,28 +1356,33 @@ const CallWValues = struct { |
| 1357 | } | 1356 | } |
| 1358 | }; | 1357 | }; |
| 1359 | 1358 | ||
| 1360 | fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues { | 1359 | fn resolveCallingConventionValues( |
| 1361 | const pt = func.pt; | 1360 | zcu: *const Zcu, |
| 1362 | const zcu = pt.zcu; | 1361 | fn_ty: Type, |
| 1362 | target: *const std.Target, | ||
| 1363 | ) Allocator.Error!CallWValues { | ||
| 1364 | const gpa = zcu.gpa; | ||
| 1363 | const ip = &zcu.intern_pool; | 1365 | const ip = &zcu.intern_pool; |
| 1364 | const fn_info = zcu.typeToFunc(fn_ty).?; | 1366 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 1365 | const cc = fn_info.cc; | 1367 | const cc = fn_info.cc; |
| 1368 | |||
| 1366 | var result: CallWValues = .{ | 1369 | var result: CallWValues = .{ |
| 1367 | .args = &.{}, | 1370 | .args = &.{}, |
| 1368 | .return_value = .none, | 1371 | .return_value = .none, |
| 1372 | .local_index = 0, | ||
| 1369 | }; | 1373 | }; |
| 1370 | if (cc == .naked) return result; | 1374 | if (cc == .naked) return result; |
| 1371 | 1375 | ||
| 1372 | var args = std.ArrayList(WValue).init(func.gpa); | 1376 | var args = std.ArrayList(WValue).init(gpa); |
| 1373 | defer args.deinit(); | 1377 | defer args.deinit(); |
| 1374 | 1378 | ||
| 1375 | // Check if we store the result as a pointer to the stack rather than | 1379 | // Check if we store the result as a pointer to the stack rather than |
| 1376 | // by value | 1380 | // by value |
| 1377 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | 1381 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) { |
| 1378 | // the sret arg will be passed as first argument, therefore we | 1382 | // the sret arg will be passed as first argument, therefore we |
| 1379 | // set the `return_value` before allocating locals for regular args. | 1383 | // set the `return_value` before allocating locals for regular args. |
| 1380 | result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } }; | 1384 | result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } }; |
| 1381 | func.local_index += 1; | 1385 | result.local_index += 1; |
| 1382 | } | 1386 | } |
| 1383 | 1387 | ||
| 1384 | switch (cc) { | 1388 | switch (cc) { |
| ... | @@ -1388,8 +1392,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV | ... | @@ -1388,8 +1392,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV |
| 1388 | continue; | 1392 | continue; |
| 1389 | } | 1393 | } |
| 1390 | 1394 | ||
| 1391 | try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } }); | 1395 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1392 | func.local_index += 1; | 1396 | result.local_index += 1; |
| 1393 | } | 1397 | } |
| 1394 | }, | 1398 | }, |
| 1395 | .wasm_watc => { | 1399 | .wasm_watc => { |
| ... | @@ -1397,23 +1401,28 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV | ... | @@ -1397,23 +1401,28 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV |
| 1397 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); | 1401 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); |
| 1398 | for (ty_classes) |class| { | 1402 | for (ty_classes) |class| { |
| 1399 | if (class == .none) continue; | 1403 | if (class == .none) continue; |
| 1400 | try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } }); | 1404 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1401 | func.local_index += 1; | 1405 | result.local_index += 1; |
| 1402 | } | 1406 | } |
| 1403 | } | 1407 | } |
| 1404 | }, | 1408 | }, |
| 1405 | else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}), | 1409 | else => unreachable, // Frontend is responsible for emitting an error earlier. |
| 1406 | } | 1410 | } |
| 1407 | result.args = try args.toOwnedSlice(); | 1411 | result.args = try args.toOwnedSlice(); |
| 1408 | return result; | 1412 | return result; |
| 1409 | } | 1413 | } |
| 1410 | 1414 | ||
| 1411 | fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool { | 1415 | pub fn firstParamSRet( |
| 1416 | cc: std.builtin.CallingConvention, | ||
| 1417 | return_type: Type, | ||
| 1418 | zcu: *const Zcu, | ||
| 1419 | target: *const std.Target, | ||
| 1420 | ) bool { | ||
| 1412 | switch (cc) { | 1421 | switch (cc) { |
| 1413 | .@"inline" => unreachable, | 1422 | .@"inline" => unreachable, |
| 1414 | .auto => return isByRef(return_type, pt, target), | 1423 | .auto => return isByRef(return_type, zcu, target), |
| 1415 | .wasm_watc => { | 1424 | .wasm_watc => { |
| 1416 | const ty_classes = abi.classifyType(return_type, pt.zcu); | 1425 | const ty_classes = abi.classifyType(return_type, zcu); |
| 1417 | if (ty_classes[0] == .indirect) return true; | 1426 | if (ty_classes[0] == .indirect) return true; |
| 1418 | if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; | 1427 | if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; |
| 1419 | return false; | 1428 | return false; |
| ... | @@ -1424,94 +1433,88 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu. | ... | @@ -1424,94 +1433,88 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu. |
| 1424 | 1433 | ||
| 1425 | /// Lowers a Zig type and its value based on a given calling convention to ensure | 1434 | /// Lowers a Zig type and its value based on a given calling convention to ensure |
| 1426 | /// it matches the ABI. | 1435 | /// it matches the ABI. |
| 1427 | fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void { | 1436 | fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void { |
| 1428 | if (cc != .wasm_watc) { | 1437 | if (cc != .wasm_watc) { |
| 1429 | return func.lowerToStack(value); | 1438 | return cg.lowerToStack(value); |
| 1430 | } | 1439 | } |
| 1431 | 1440 | ||
| 1432 | const pt = func.pt; | 1441 | const zcu = cg.pt.zcu; |
| 1433 | const zcu = pt.zcu; | ||
| 1434 | const ty_classes = abi.classifyType(ty, zcu); | 1442 | const ty_classes = abi.classifyType(ty, zcu); |
| 1435 | assert(ty_classes[0] != .none); | 1443 | assert(ty_classes[0] != .none); |
| 1436 | switch (ty.zigTypeTag(zcu)) { | 1444 | switch (ty.zigTypeTag(zcu)) { |
| 1437 | .@"struct", .@"union" => { | 1445 | .@"struct", .@"union" => { |
| 1438 | if (ty_classes[0] == .indirect) { | 1446 | if (ty_classes[0] == .indirect) { |
| 1439 | return func.lowerToStack(value); | 1447 | return cg.lowerToStack(value); |
| 1440 | } | 1448 | } |
| 1441 | assert(ty_classes[0] == .direct); | 1449 | assert(ty_classes[0] == .direct); |
| 1442 | const scalar_type = abi.scalarType(ty, zcu); | 1450 | const scalar_type = abi.scalarType(ty, zcu); |
| 1443 | switch (value) { | 1451 | switch (value) { |
| 1444 | .memory, | 1452 | .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0), |
| 1445 | .memory_offset, | ||
| 1446 | .stack_offset, | ||
| 1447 | => _ = try func.load(value, scalar_type, 0), | ||
| 1448 | .dead => unreachable, | 1453 | .dead => unreachable, |
| 1449 | else => try func.emitWValue(value), | 1454 | else => try cg.emitWValue(value), |
| 1450 | } | 1455 | } |
| 1451 | }, | 1456 | }, |
| 1452 | .int, .float => { | 1457 | .int, .float => { |
| 1453 | if (ty_classes[1] == .none) { | 1458 | if (ty_classes[1] == .none) { |
| 1454 | return func.lowerToStack(value); | 1459 | return cg.lowerToStack(value); |
| 1455 | } | 1460 | } |
| 1456 | assert(ty_classes[0] == .direct and ty_classes[1] == .direct); | 1461 | assert(ty_classes[0] == .direct and ty_classes[1] == .direct); |
| 1457 | assert(ty.abiSize(zcu) == 16); | 1462 | assert(ty.abiSize(zcu) == 16); |
| 1458 | // in this case we have an integer or float that must be lowered as 2 i64's. | 1463 | // in this case we have an integer or float that must be lowered as 2 i64's. |
| 1459 | try func.emitWValue(value); | 1464 | try cg.emitWValue(value); |
| 1460 | try func.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 }); | 1465 | try cg.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 }); |
| 1461 | try func.emitWValue(value); | 1466 | try cg.emitWValue(value); |
| 1462 | try func.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); | 1467 | try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); |
| 1463 | }, | 1468 | }, |
| 1464 | else => return func.lowerToStack(value), | 1469 | else => return cg.lowerToStack(value), |
| 1465 | } | 1470 | } |
| 1466 | } | 1471 | } |
| 1467 | 1472 | ||
| 1468 | /// Lowers a `WValue` to the stack. This means when the `value` results in | 1473 | /// Lowers a `WValue` to the stack. This means when the `value` results in |
| 1469 | /// `.stack_offset` we calculate the pointer of this offset and use that. | 1474 | /// `.stack_offset` we calculate the pointer of this offset and use that. |
| 1470 | /// The value is left on the stack, and not stored in any temporary. | 1475 | /// The value is left on the stack, and not stored in any temporary. |
| 1471 | fn lowerToStack(func: *CodeGen, value: WValue) !void { | 1476 | fn lowerToStack(cg: *CodeGen, value: WValue) !void { |
| 1472 | switch (value) { | 1477 | switch (value) { |
| 1473 | .stack_offset => |offset| { | 1478 | .stack_offset => |offset| { |
| 1474 | try func.emitWValue(value); | 1479 | try cg.emitWValue(value); |
| 1475 | if (offset.value > 0) { | 1480 | if (offset.value > 0) { |
| 1476 | switch (func.arch()) { | 1481 | switch (cg.ptr_size) { |
| 1477 | .wasm32 => { | 1482 | .wasm32 => { |
| 1478 | try func.addImm32(offset.value); | 1483 | try cg.addImm32(offset.value); |
| 1479 | try func.addTag(.i32_add); | 1484 | try cg.addTag(.i32_add); |
| 1480 | }, | 1485 | }, |
| 1481 | .wasm64 => { | 1486 | .wasm64 => { |
| 1482 | try func.addImm64(offset.value); | 1487 | try cg.addImm64(offset.value); |
| 1483 | try func.addTag(.i64_add); | 1488 | try cg.addTag(.i64_add); |
| 1484 | }, | 1489 | }, |
| 1485 | else => unreachable, | ||
| 1486 | } | 1490 | } |
| 1487 | } | 1491 | } |
| 1488 | }, | 1492 | }, |
| 1489 | else => try func.emitWValue(value), | 1493 | else => try cg.emitWValue(value), |
| 1490 | } | 1494 | } |
| 1491 | } | 1495 | } |
| 1492 | 1496 | ||
| 1493 | /// Creates a local for the initial stack value | 1497 | /// Creates a local for the initial stack value |
| 1494 | /// Asserts `initial_stack_value` is `.none` | 1498 | /// Asserts `initial_stack_value` is `.none` |
| 1495 | fn initializeStack(func: *CodeGen) !void { | 1499 | fn initializeStack(cg: *CodeGen) !void { |
| 1496 | assert(func.initial_stack_value == .none); | 1500 | assert(cg.initial_stack_value == .none); |
| 1497 | // Reserve a local to store the current stack pointer | 1501 | // Reserve a local to store the current stack pointer |
| 1498 | // We can later use this local to set the stack pointer back to the value | 1502 | // We can later use this local to set the stack pointer back to the value |
| 1499 | // we have stored here. | 1503 | // we have stored here. |
| 1500 | func.initial_stack_value = try func.ensureAllocLocal(Type.usize); | 1504 | cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 1501 | // Also reserve a local to store the bottom stack value | 1505 | // Also reserve a local to store the bottom stack value |
| 1502 | func.bottom_stack_value = try func.ensureAllocLocal(Type.usize); | 1506 | cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 1503 | } | 1507 | } |
| 1504 | 1508 | ||
| 1505 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it | 1509 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| 1506 | /// to the global stack pointer variable | 1510 | /// to the global stack pointer variable |
| 1507 | fn restoreStackPointer(func: *CodeGen) !void { | 1511 | fn restoreStackPointer(cg: *CodeGen) !void { |
| 1508 | // only restore the pointer if it was initialized | 1512 | // only restore the pointer if it was initialized |
| 1509 | if (func.initial_stack_value == .none) return; | 1513 | if (cg.initial_stack_value == .none) return; |
| 1510 | // Get the original stack pointer's value | 1514 | // Get the original stack pointer's value |
| 1511 | try func.emitWValue(func.initial_stack_value); | 1515 | try cg.emitWValue(cg.initial_stack_value); |
| 1512 | 1516 | ||
| 1513 | // save its value in the global stack pointer | 1517 | try cg.addTag(.global_set_sp); |
| 1514 | try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym)); | ||
| 1515 | } | 1518 | } |
| 1516 | 1519 | ||
| 1517 | /// From a given type, will create space on the virtual stack to store the value of such type. | 1520 | /// From a given type, will create space on the virtual stack to store the value of such type. |
| ... | @@ -1520,24 +1523,25 @@ fn restoreStackPointer(func: *CodeGen) !void { | ... | @@ -1520,24 +1523,25 @@ fn restoreStackPointer(func: *CodeGen) !void { |
| 1520 | /// moveStack unless a local was already created to store the pointer. | 1523 | /// moveStack unless a local was already created to store the pointer. |
| 1521 | /// | 1524 | /// |
| 1522 | /// Asserts Type has codegenbits | 1525 | /// Asserts Type has codegenbits |
| 1523 | fn allocStack(func: *CodeGen, ty: Type) !WValue { | 1526 | fn allocStack(cg: *CodeGen, ty: Type) !WValue { |
| 1524 | const zcu = func.pt.zcu; | 1527 | const pt = cg.pt; |
| 1528 | const zcu = pt.zcu; | ||
| 1525 | assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); | 1529 | assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); |
| 1526 | if (func.initial_stack_value == .none) { | 1530 | if (cg.initial_stack_value == .none) { |
| 1527 | try func.initializeStack(); | 1531 | try cg.initializeStack(); |
| 1528 | } | 1532 | } |
| 1529 | 1533 | ||
| 1530 | const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse { | 1534 | const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse { |
| 1531 | return func.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ | 1535 | return cg.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ |
| 1532 | ty.fmt(func.pt), ty.abiSize(zcu), | 1536 | ty.fmt(pt), ty.abiSize(zcu), |
| 1533 | }); | 1537 | }); |
| 1534 | }; | 1538 | }; |
| 1535 | const abi_align = ty.abiAlignment(zcu); | 1539 | const abi_align = ty.abiAlignment(zcu); |
| 1536 | 1540 | ||
| 1537 | func.stack_alignment = func.stack_alignment.max(abi_align); | 1541 | cg.stack_alignment = cg.stack_alignment.max(abi_align); |
| 1538 | 1542 | ||
| 1539 | const offset: u32 = @intCast(abi_align.forward(func.stack_size)); | 1543 | const offset: u32 = @intCast(abi_align.forward(cg.stack_size)); |
| 1540 | defer func.stack_size = offset + abi_size; | 1544 | defer cg.stack_size = offset + abi_size; |
| 1541 | 1545 | ||
| 1542 | return .{ .stack_offset = .{ .value = offset, .references = 1 } }; | 1546 | return .{ .stack_offset = .{ .value = offset, .references = 1 } }; |
| 1543 | } | 1547 | } |
| ... | @@ -1546,30 +1550,30 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue { | ... | @@ -1546,30 +1550,30 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue { |
| 1546 | /// the value of its type will live. | 1550 | /// the value of its type will live. |
| 1547 | /// This is different from allocStack where this will use the pointer's alignment | 1551 | /// This is different from allocStack where this will use the pointer's alignment |
| 1548 | /// if it is set, to ensure the stack alignment will be set correctly. | 1552 | /// if it is set, to ensure the stack alignment will be set correctly. |
| 1549 | fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue { | 1553 | fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue { |
| 1550 | const pt = func.pt; | 1554 | const pt = cg.pt; |
| 1551 | const zcu = pt.zcu; | 1555 | const zcu = pt.zcu; |
| 1552 | const ptr_ty = func.typeOfIndex(inst); | 1556 | const ptr_ty = cg.typeOfIndex(inst); |
| 1553 | const pointee_ty = ptr_ty.childType(zcu); | 1557 | const pointee_ty = ptr_ty.childType(zcu); |
| 1554 | 1558 | ||
| 1555 | if (func.initial_stack_value == .none) { | 1559 | if (cg.initial_stack_value == .none) { |
| 1556 | try func.initializeStack(); | 1560 | try cg.initializeStack(); |
| 1557 | } | 1561 | } |
| 1558 | 1562 | ||
| 1559 | if (!pointee_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 1563 | if (!pointee_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 1560 | return func.allocStack(Type.usize); // create a value containing just the stack pointer. | 1564 | return cg.allocStack(Type.usize); // create a value containing just the stack pointer. |
| 1561 | } | 1565 | } |
| 1562 | 1566 | ||
| 1563 | const abi_alignment = ptr_ty.ptrAlignment(zcu); | 1567 | const abi_alignment = ptr_ty.ptrAlignment(zcu); |
| 1564 | const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse { | 1568 | const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse { |
| 1565 | return func.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ | 1569 | return cg.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ |
| 1566 | pointee_ty.fmt(pt), pointee_ty.abiSize(zcu), | 1570 | pointee_ty.fmt(pt), pointee_ty.abiSize(zcu), |
| 1567 | }); | 1571 | }); |
| 1568 | }; | 1572 | }; |
| 1569 | func.stack_alignment = func.stack_alignment.max(abi_alignment); | 1573 | cg.stack_alignment = cg.stack_alignment.max(abi_alignment); |
| 1570 | 1574 | ||
| 1571 | const offset: u32 = @intCast(abi_alignment.forward(func.stack_size)); | 1575 | const offset: u32 = @intCast(abi_alignment.forward(cg.stack_size)); |
| 1572 | defer func.stack_size = offset + abi_size; | 1576 | defer cg.stack_size = offset + abi_size; |
| 1573 | 1577 | ||
| 1574 | return .{ .stack_offset = .{ .value = offset, .references = 1 } }; | 1578 | return .{ .stack_offset = .{ .value = offset, .references = 1 } }; |
| 1575 | } | 1579 | } |
| ... | @@ -1583,14 +1587,14 @@ fn toWasmBits(bits: u16) ?u16 { | ... | @@ -1583,14 +1587,14 @@ fn toWasmBits(bits: u16) ?u16 { |
| 1583 | 1587 | ||
| 1584 | /// Performs a copy of bytes for a given type. Copying all bytes | 1588 | /// Performs a copy of bytes for a given type. Copying all bytes |
| 1585 | /// from rhs to lhs. | 1589 | /// from rhs to lhs. |
| 1586 | fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | 1590 | fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1587 | // When bulk_memory is enabled, we lower it to wasm's memcpy instruction. | 1591 | // When bulk_memory is enabled, we lower it to wasm's memcpy instruction. |
| 1588 | // If not, we lower it ourselves manually | 1592 | // If not, we lower it ourselves manually |
| 1589 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) { | 1593 | if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) { |
| 1590 | try func.lowerToStack(dst); | 1594 | try cg.lowerToStack(dst); |
| 1591 | try func.lowerToStack(src); | 1595 | try cg.lowerToStack(src); |
| 1592 | try func.emitWValue(len); | 1596 | try cg.emitWValue(len); |
| 1593 | try func.addExtended(.memory_copy); | 1597 | try cg.addExtended(.memory_copy); |
| 1594 | return; | 1598 | return; |
| 1595 | } | 1599 | } |
| 1596 | 1600 | ||
| ... | @@ -1611,19 +1615,18 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | ... | @@ -1611,19 +1615,18 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1611 | const rhs_base = src.offset(); | 1615 | const rhs_base = src.offset(); |
| 1612 | while (offset < length) : (offset += 1) { | 1616 | while (offset < length) : (offset += 1) { |
| 1613 | // get dst's address to store the result | 1617 | // get dst's address to store the result |
| 1614 | try func.emitWValue(dst); | 1618 | try cg.emitWValue(dst); |
| 1615 | // load byte from src's address | 1619 | // load byte from src's address |
| 1616 | try func.emitWValue(src); | 1620 | try cg.emitWValue(src); |
| 1617 | switch (func.arch()) { | 1621 | switch (cg.ptr_size) { |
| 1618 | .wasm32 => { | 1622 | .wasm32 => { |
| 1619 | try func.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); | 1623 | try cg.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); |
| 1620 | try func.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); | 1624 | try cg.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); |
| 1621 | }, | 1625 | }, |
| 1622 | .wasm64 => { | 1626 | .wasm64 => { |
| 1623 | try func.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); | 1627 | try cg.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); |
| 1624 | try func.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); | 1628 | try cg.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); |
| 1625 | }, | 1629 | }, |
| 1626 | else => unreachable, | ||
| 1627 | } | 1630 | } |
| 1628 | } | 1631 | } |
| 1629 | return; | 1632 | return; |
| ... | @@ -1633,94 +1636,84 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | ... | @@ -1633,94 +1636,84 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1633 | 1636 | ||
| 1634 | // allocate a local for the offset, and set it to 0. | 1637 | // allocate a local for the offset, and set it to 0. |
| 1635 | // This to ensure that inside loops we correctly re-set the counter. | 1638 | // This to ensure that inside loops we correctly re-set the counter. |
| 1636 | var offset = try func.allocLocal(Type.usize); // local for counter | 1639 | var offset = try cg.allocLocal(Type.usize); // local for counter |
| 1637 | defer offset.free(func); | 1640 | defer offset.free(cg); |
| 1638 | switch (func.arch()) { | 1641 | switch (cg.ptr_size) { |
| 1639 | .wasm32 => try func.addImm32(0), | 1642 | .wasm32 => try cg.addImm32(0), |
| 1640 | .wasm64 => try func.addImm64(0), | 1643 | .wasm64 => try cg.addImm64(0), |
| 1641 | else => unreachable, | ||
| 1642 | } | 1644 | } |
| 1643 | try func.addLabel(.local_set, offset.local.value); | 1645 | try cg.addLocal(.local_set, offset.local.value); |
| 1644 | 1646 | ||
| 1645 | // outer block to jump to when loop is done | 1647 | // outer block to jump to when loop is done |
| 1646 | try func.startBlock(.block, wasm.block_empty); | 1648 | try cg.startBlock(.block, .empty); |
| 1647 | try func.startBlock(.loop, wasm.block_empty); | 1649 | try cg.startBlock(.loop, .empty); |
| 1648 | 1650 | ||
| 1649 | // loop condition (offset == length -> break) | 1651 | // loop condition (offset == length -> break) |
| 1650 | { | 1652 | { |
| 1651 | try func.emitWValue(offset); | 1653 | try cg.emitWValue(offset); |
| 1652 | try func.emitWValue(len); | 1654 | try cg.emitWValue(len); |
| 1653 | switch (func.arch()) { | 1655 | switch (cg.ptr_size) { |
| 1654 | .wasm32 => try func.addTag(.i32_eq), | 1656 | .wasm32 => try cg.addTag(.i32_eq), |
| 1655 | .wasm64 => try func.addTag(.i64_eq), | 1657 | .wasm64 => try cg.addTag(.i64_eq), |
| 1656 | else => unreachable, | ||
| 1657 | } | 1658 | } |
| 1658 | try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished) | 1659 | try cg.addLabel(.br_if, 1); // jump out of loop into outer block (finished) |
| 1659 | } | 1660 | } |
| 1660 | 1661 | ||
| 1661 | // get dst ptr | 1662 | // get dst ptr |
| 1662 | { | 1663 | { |
| 1663 | try func.emitWValue(dst); | 1664 | try cg.emitWValue(dst); |
| 1664 | try func.emitWValue(offset); | 1665 | try cg.emitWValue(offset); |
| 1665 | switch (func.arch()) { | 1666 | switch (cg.ptr_size) { |
| 1666 | .wasm32 => try func.addTag(.i32_add), | 1667 | .wasm32 => try cg.addTag(.i32_add), |
| 1667 | .wasm64 => try func.addTag(.i64_add), | 1668 | .wasm64 => try cg.addTag(.i64_add), |
| 1668 | else => unreachable, | ||
| 1669 | } | 1669 | } |
| 1670 | } | 1670 | } |
| 1671 | 1671 | ||
| 1672 | // get src value and also store in dst | 1672 | // get src value and also store in dst |
| 1673 | { | 1673 | { |
| 1674 | try func.emitWValue(src); | 1674 | try cg.emitWValue(src); |
| 1675 | try func.emitWValue(offset); | 1675 | try cg.emitWValue(offset); |
| 1676 | switch (func.arch()) { | 1676 | switch (cg.ptr_size) { |
| 1677 | .wasm32 => { | 1677 | .wasm32 => { |
| 1678 | try func.addTag(.i32_add); | 1678 | try cg.addTag(.i32_add); |
| 1679 | try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 }); | 1679 | try cg.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 }); |
| 1680 | try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 }); | 1680 | try cg.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 }); |
| 1681 | }, | 1681 | }, |
| 1682 | .wasm64 => { | 1682 | .wasm64 => { |
| 1683 | try func.addTag(.i64_add); | 1683 | try cg.addTag(.i64_add); |
| 1684 | try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 }); | 1684 | try cg.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 }); |
| 1685 | try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 }); | 1685 | try cg.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 }); |
| 1686 | }, | 1686 | }, |
| 1687 | else => unreachable, | ||
| 1688 | } | 1687 | } |
| 1689 | } | 1688 | } |
| 1690 | 1689 | ||
| 1691 | // increment loop counter | 1690 | // increment loop counter |
| 1692 | { | 1691 | { |
| 1693 | try func.emitWValue(offset); | 1692 | try cg.emitWValue(offset); |
| 1694 | switch (func.arch()) { | 1693 | switch (cg.ptr_size) { |
| 1695 | .wasm32 => { | 1694 | .wasm32 => { |
| 1696 | try func.addImm32(1); | 1695 | try cg.addImm32(1); |
| 1697 | try func.addTag(.i32_add); | 1696 | try cg.addTag(.i32_add); |
| 1698 | }, | 1697 | }, |
| 1699 | .wasm64 => { | 1698 | .wasm64 => { |
| 1700 | try func.addImm64(1); | 1699 | try cg.addImm64(1); |
| 1701 | try func.addTag(.i64_add); | 1700 | try cg.addTag(.i64_add); |
| 1702 | }, | 1701 | }, |
| 1703 | else => unreachable, | ||
| 1704 | } | 1702 | } |
| 1705 | try func.addLabel(.local_set, offset.local.value); | 1703 | try cg.addLocal(.local_set, offset.local.value); |
| 1706 | try func.addLabel(.br, 0); // jump to start of loop | 1704 | try cg.addLabel(.br, 0); // jump to start of loop |
| 1707 | } | 1705 | } |
| 1708 | try func.endBlock(); // close off loop block | 1706 | try cg.endBlock(); // close off loop block |
| 1709 | try func.endBlock(); // close off outer block | 1707 | try cg.endBlock(); // close off outer block |
| 1710 | } | 1708 | } |
| 1711 | 1709 | ||
| 1712 | fn ptrSize(func: *const CodeGen) u16 { | 1710 | fn ptrSize(cg: *const CodeGen) u16 { |
| 1713 | return @divExact(func.target.ptrBitWidth(), 8); | 1711 | return @divExact(cg.target.ptrBitWidth(), 8); |
| 1714 | } | ||
| 1715 | |||
| 1716 | fn arch(func: *const CodeGen) std.Target.Cpu.Arch { | ||
| 1717 | return func.target.cpu.arch; | ||
| 1718 | } | 1712 | } |
| 1719 | 1713 | ||
| 1720 | /// For a given `Type`, will return true when the type will be passed | 1714 | /// For a given `Type`, will return true when the type will be passed |
| 1721 | /// by reference, rather than by value | 1715 | /// by reference, rather than by value |
| 1722 | fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { | 1716 | fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { |
| 1723 | const zcu = pt.zcu; | ||
| 1724 | const ip = &zcu.intern_pool; | 1717 | const ip = &zcu.intern_pool; |
| 1725 | switch (ty.zigTypeTag(zcu)) { | 1718 | switch (ty.zigTypeTag(zcu)) { |
| 1726 | .type, | 1719 | .type, |
| ... | @@ -1753,14 +1746,14 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { | ... | @@ -1753,14 +1746,14 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { |
| 1753 | }, | 1746 | }, |
| 1754 | .@"struct" => { | 1747 | .@"struct" => { |
| 1755 | if (zcu.typeToPackedStruct(ty)) |packed_struct| { | 1748 | if (zcu.typeToPackedStruct(ty)) |packed_struct| { |
| 1756 | return isByRef(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), pt, target); | 1749 | return isByRef(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), zcu, target); |
| 1757 | } | 1750 | } |
| 1758 | return ty.hasRuntimeBitsIgnoreComptime(zcu); | 1751 | return ty.hasRuntimeBitsIgnoreComptime(zcu); |
| 1759 | }, | 1752 | }, |
| 1760 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, | 1753 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, |
| 1761 | .int => return ty.intInfo(zcu).bits > 64, | 1754 | .int => return ty.intInfo(zcu).bits > 64, |
| 1762 | .@"enum" => return ty.intInfo(zcu).bits > 64, | 1755 | .@"enum" => return ty.intInfo(zcu).bits > 64, |
| 1763 | .float => return ty.floatBits(target) > 64, | 1756 | .float => return ty.floatBits(target.*) > 64, |
| 1764 | .error_union => { | 1757 | .error_union => { |
| 1765 | const pl_ty = ty.errorUnionPayload(zcu); | 1758 | const pl_ty = ty.errorUnionPayload(zcu); |
| 1766 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 1759 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| ... | @@ -1791,8 +1784,8 @@ const SimdStoreStrategy = enum { | ... | @@ -1791,8 +1784,8 @@ const SimdStoreStrategy = enum { |
| 1791 | /// This means when a given type is 128 bits and either the simd128 or relaxed-simd | 1784 | /// This means when a given type is 128 bits and either the simd128 or relaxed-simd |
| 1792 | /// features are enabled, the function will return `.direct`. This would allow to store | 1785 | /// features are enabled, the function will return `.direct`. This would allow to store |
| 1793 | /// it using a instruction, rather than an unrolled version. | 1786 | /// it using a instruction, rather than an unrolled version. |
| 1794 | fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy { | 1787 | pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy { |
| 1795 | std.debug.assert(ty.zigTypeTag(zcu) == .vector); | 1788 | assert(ty.zigTypeTag(zcu) == .vector); |
| 1796 | if (ty.bitSize(zcu) != 128) return .unrolled; | 1789 | if (ty.bitSize(zcu) != 128) return .unrolled; |
| 1797 | const hasFeature = std.Target.wasm.featureSetHas; | 1790 | const hasFeature = std.Target.wasm.featureSetHas; |
| 1798 | const features = target.cpu.features; | 1791 | const features = target.cpu.features; |
| ... | @@ -1806,215 +1799,214 @@ fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStore | ... | @@ -1806,215 +1799,214 @@ fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStore |
| 1806 | /// This can be used to get a pointer to a struct field, error payload, etc. | 1799 | /// This can be used to get a pointer to a struct field, error payload, etc. |
| 1807 | /// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new | 1800 | /// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new |
| 1808 | /// local value to store the pointer. This allows for local re-use and improves binary size. | 1801 | /// local value to store the pointer. This allows for local re-use and improves binary size. |
| 1809 | fn buildPointerOffset(func: *CodeGen, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue { | 1802 | fn buildPointerOffset(cg: *CodeGen, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue { |
| 1810 | // do not perform arithmetic when offset is 0. | 1803 | // do not perform arithmetic when offset is 0. |
| 1811 | if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value; | 1804 | if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value; |
| 1812 | const result_ptr: WValue = switch (action) { | 1805 | const result_ptr: WValue = switch (action) { |
| 1813 | .new => try func.ensureAllocLocal(Type.usize), | 1806 | .new => try cg.ensureAllocLocal(Type.usize), |
| 1814 | .modify => ptr_value, | 1807 | .modify => ptr_value, |
| 1815 | }; | 1808 | }; |
| 1816 | try func.emitWValue(ptr_value); | 1809 | try cg.emitWValue(ptr_value); |
| 1817 | if (offset + ptr_value.offset() > 0) { | 1810 | if (offset + ptr_value.offset() > 0) { |
| 1818 | switch (func.arch()) { | 1811 | switch (cg.ptr_size) { |
| 1819 | .wasm32 => { | 1812 | .wasm32 => { |
| 1820 | try func.addImm32(@intCast(offset + ptr_value.offset())); | 1813 | try cg.addImm32(@intCast(offset + ptr_value.offset())); |
| 1821 | try func.addTag(.i32_add); | 1814 | try cg.addTag(.i32_add); |
| 1822 | }, | 1815 | }, |
| 1823 | .wasm64 => { | 1816 | .wasm64 => { |
| 1824 | try func.addImm64(offset + ptr_value.offset()); | 1817 | try cg.addImm64(offset + ptr_value.offset()); |
| 1825 | try func.addTag(.i64_add); | 1818 | try cg.addTag(.i64_add); |
| 1826 | }, | 1819 | }, |
| 1827 | else => unreachable, | ||
| 1828 | } | 1820 | } |
| 1829 | } | 1821 | } |
| 1830 | try func.addLabel(.local_set, result_ptr.local.value); | 1822 | try cg.addLocal(.local_set, result_ptr.local.value); |
| 1831 | return result_ptr; | 1823 | return result_ptr; |
| 1832 | } | 1824 | } |
| 1833 | 1825 | ||
| 1834 | fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 1826 | fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1835 | const air_tags = func.air.instructions.items(.tag); | 1827 | const air_tags = cg.air.instructions.items(.tag); |
| 1836 | return switch (air_tags[@intFromEnum(inst)]) { | 1828 | return switch (air_tags[@intFromEnum(inst)]) { |
| 1837 | .inferred_alloc, .inferred_alloc_comptime => unreachable, | 1829 | .inferred_alloc, .inferred_alloc_comptime => unreachable, |
| 1838 | 1830 | ||
| 1839 | .add => func.airBinOp(inst, .add), | 1831 | .add => cg.airBinOp(inst, .add), |
| 1840 | .add_sat => func.airSatBinOp(inst, .add), | 1832 | .add_sat => cg.airSatBinOp(inst, .add), |
| 1841 | .add_wrap => func.airWrapBinOp(inst, .add), | 1833 | .add_wrap => cg.airWrapBinOp(inst, .add), |
| 1842 | .sub => func.airBinOp(inst, .sub), | 1834 | .sub => cg.airBinOp(inst, .sub), |
| 1843 | .sub_sat => func.airSatBinOp(inst, .sub), | 1835 | .sub_sat => cg.airSatBinOp(inst, .sub), |
| 1844 | .sub_wrap => func.airWrapBinOp(inst, .sub), | 1836 | .sub_wrap => cg.airWrapBinOp(inst, .sub), |
| 1845 | .mul => func.airBinOp(inst, .mul), | 1837 | .mul => cg.airBinOp(inst, .mul), |
| 1846 | .mul_sat => func.airSatMul(inst), | 1838 | .mul_sat => cg.airSatMul(inst), |
| 1847 | .mul_wrap => func.airWrapBinOp(inst, .mul), | 1839 | .mul_wrap => cg.airWrapBinOp(inst, .mul), |
| 1848 | .div_float, .div_exact => func.airDiv(inst), | 1840 | .div_float, .div_exact => cg.airDiv(inst), |
| 1849 | .div_trunc => func.airDivTrunc(inst), | 1841 | .div_trunc => cg.airDivTrunc(inst), |
| 1850 | .div_floor => func.airDivFloor(inst), | 1842 | .div_floor => cg.airDivFloor(inst), |
| 1851 | .bit_and => func.airBinOp(inst, .@"and"), | 1843 | .bit_and => cg.airBinOp(inst, .@"and"), |
| 1852 | .bit_or => func.airBinOp(inst, .@"or"), | 1844 | .bit_or => cg.airBinOp(inst, .@"or"), |
| 1853 | .bool_and => func.airBinOp(inst, .@"and"), | 1845 | .bool_and => cg.airBinOp(inst, .@"and"), |
| 1854 | .bool_or => func.airBinOp(inst, .@"or"), | 1846 | .bool_or => cg.airBinOp(inst, .@"or"), |
| 1855 | .rem => func.airRem(inst), | 1847 | .rem => cg.airRem(inst), |
| 1856 | .mod => func.airMod(inst), | 1848 | .mod => cg.airMod(inst), |
| 1857 | .shl => func.airWrapBinOp(inst, .shl), | 1849 | .shl => cg.airWrapBinOp(inst, .shl), |
| 1858 | .shl_exact => func.airBinOp(inst, .shl), | 1850 | .shl_exact => cg.airBinOp(inst, .shl), |
| 1859 | .shl_sat => func.airShlSat(inst), | 1851 | .shl_sat => cg.airShlSat(inst), |
| 1860 | .shr, .shr_exact => func.airBinOp(inst, .shr), | 1852 | .shr, .shr_exact => cg.airBinOp(inst, .shr), |
| 1861 | .xor => func.airBinOp(inst, .xor), | 1853 | .xor => cg.airBinOp(inst, .xor), |
| 1862 | .max => func.airMaxMin(inst, .max), | 1854 | .max => cg.airMaxMin(inst, .fmax, .gt), |
| 1863 | .min => func.airMaxMin(inst, .min), | 1855 | .min => cg.airMaxMin(inst, .fmin, .lt), |
| 1864 | .mul_add => func.airMulAdd(inst), | 1856 | .mul_add => cg.airMulAdd(inst), |
| 1865 | 1857 | ||
| 1866 | .sqrt => func.airUnaryFloatOp(inst, .sqrt), | 1858 | .sqrt => cg.airUnaryFloatOp(inst, .sqrt), |
| 1867 | .sin => func.airUnaryFloatOp(inst, .sin), | 1859 | .sin => cg.airUnaryFloatOp(inst, .sin), |
| 1868 | .cos => func.airUnaryFloatOp(inst, .cos), | 1860 | .cos => cg.airUnaryFloatOp(inst, .cos), |
| 1869 | .tan => func.airUnaryFloatOp(inst, .tan), | 1861 | .tan => cg.airUnaryFloatOp(inst, .tan), |
| 1870 | .exp => func.airUnaryFloatOp(inst, .exp), | 1862 | .exp => cg.airUnaryFloatOp(inst, .exp), |
| 1871 | .exp2 => func.airUnaryFloatOp(inst, .exp2), | 1863 | .exp2 => cg.airUnaryFloatOp(inst, .exp2), |
| 1872 | .log => func.airUnaryFloatOp(inst, .log), | 1864 | .log => cg.airUnaryFloatOp(inst, .log), |
| 1873 | .log2 => func.airUnaryFloatOp(inst, .log2), | 1865 | .log2 => cg.airUnaryFloatOp(inst, .log2), |
| 1874 | .log10 => func.airUnaryFloatOp(inst, .log10), | 1866 | .log10 => cg.airUnaryFloatOp(inst, .log10), |
| 1875 | .floor => func.airUnaryFloatOp(inst, .floor), | 1867 | .floor => cg.airUnaryFloatOp(inst, .floor), |
| 1876 | .ceil => func.airUnaryFloatOp(inst, .ceil), | 1868 | .ceil => cg.airUnaryFloatOp(inst, .ceil), |
| 1877 | .round => func.airUnaryFloatOp(inst, .round), | 1869 | .round => cg.airUnaryFloatOp(inst, .round), |
| 1878 | .trunc_float => func.airUnaryFloatOp(inst, .trunc), | 1870 | .trunc_float => cg.airUnaryFloatOp(inst, .trunc), |
| 1879 | .neg => func.airUnaryFloatOp(inst, .neg), | 1871 | .neg => cg.airUnaryFloatOp(inst, .neg), |
| 1880 | 1872 | ||
| 1881 | .abs => func.airAbs(inst), | 1873 | .abs => cg.airAbs(inst), |
| 1882 | 1874 | ||
| 1883 | .add_with_overflow => func.airAddSubWithOverflow(inst, .add), | 1875 | .add_with_overflow => cg.airAddSubWithOverflow(inst, .add), |
| 1884 | .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub), | 1876 | .sub_with_overflow => cg.airAddSubWithOverflow(inst, .sub), |
| 1885 | .shl_with_overflow => func.airShlWithOverflow(inst), | 1877 | .shl_with_overflow => cg.airShlWithOverflow(inst), |
| 1886 | .mul_with_overflow => func.airMulWithOverflow(inst), | 1878 | .mul_with_overflow => cg.airMulWithOverflow(inst), |
| 1887 | 1879 | ||
| 1888 | .clz => func.airClz(inst), | 1880 | .clz => cg.airClz(inst), |
| 1889 | .ctz => func.airCtz(inst), | 1881 | .ctz => cg.airCtz(inst), |
| 1890 | 1882 | ||
| 1891 | .cmp_eq => func.airCmp(inst, .eq), | 1883 | .cmp_eq => cg.airCmp(inst, .eq), |
| 1892 | .cmp_gte => func.airCmp(inst, .gte), | 1884 | .cmp_gte => cg.airCmp(inst, .gte), |
| 1893 | .cmp_gt => func.airCmp(inst, .gt), | 1885 | .cmp_gt => cg.airCmp(inst, .gt), |
| 1894 | .cmp_lte => func.airCmp(inst, .lte), | 1886 | .cmp_lte => cg.airCmp(inst, .lte), |
| 1895 | .cmp_lt => func.airCmp(inst, .lt), | 1887 | .cmp_lt => cg.airCmp(inst, .lt), |
| 1896 | .cmp_neq => func.airCmp(inst, .neq), | 1888 | .cmp_neq => cg.airCmp(inst, .neq), |
| 1897 | 1889 | ||
| 1898 | .cmp_vector => func.airCmpVector(inst), | 1890 | .cmp_vector => cg.airCmpVector(inst), |
| 1899 | .cmp_lt_errors_len => func.airCmpLtErrorsLen(inst), | 1891 | .cmp_lt_errors_len => cg.airCmpLtErrorsLen(inst), |
| 1900 | 1892 | ||
| 1901 | .array_elem_val => func.airArrayElemVal(inst), | 1893 | .array_elem_val => cg.airArrayElemVal(inst), |
| 1902 | .array_to_slice => func.airArrayToSlice(inst), | 1894 | .array_to_slice => cg.airArrayToSlice(inst), |
| 1903 | .alloc => func.airAlloc(inst), | 1895 | .alloc => cg.airAlloc(inst), |
| 1904 | .arg => func.airArg(inst), | 1896 | .arg => cg.airArg(inst), |
| 1905 | .bitcast => func.airBitcast(inst), | 1897 | .bitcast => cg.airBitcast(inst), |
| 1906 | .block => func.airBlock(inst), | 1898 | .block => cg.airBlock(inst), |
| 1907 | .trap => func.airTrap(inst), | 1899 | .trap => cg.airTrap(inst), |
| 1908 | .breakpoint => func.airBreakpoint(inst), | 1900 | .breakpoint => cg.airBreakpoint(inst), |
| 1909 | .br => func.airBr(inst), | 1901 | .br => cg.airBr(inst), |
| 1910 | .repeat => func.airRepeat(inst), | 1902 | .repeat => cg.airRepeat(inst), |
| 1911 | .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}), | 1903 | .switch_dispatch => return cg.fail("TODO implement `switch_dispatch`", .{}), |
| 1912 | .int_from_bool => func.airIntFromBool(inst), | 1904 | .int_from_bool => cg.airIntFromBool(inst), |
| 1913 | .cond_br => func.airCondBr(inst), | 1905 | .cond_br => cg.airCondBr(inst), |
| 1914 | .intcast => func.airIntcast(inst), | 1906 | .intcast => cg.airIntcast(inst), |
| 1915 | .fptrunc => func.airFptrunc(inst), | 1907 | .fptrunc => cg.airFptrunc(inst), |
| 1916 | .fpext => func.airFpext(inst), | 1908 | .fpext => cg.airFpext(inst), |
| 1917 | .int_from_float => func.airIntFromFloat(inst), | 1909 | .int_from_float => cg.airIntFromFloat(inst), |
| 1918 | .float_from_int => func.airFloatFromInt(inst), | 1910 | .float_from_int => cg.airFloatFromInt(inst), |
| 1919 | .get_union_tag => func.airGetUnionTag(inst), | 1911 | .get_union_tag => cg.airGetUnionTag(inst), |
| 1920 | 1912 | ||
| 1921 | .@"try" => func.airTry(inst), | 1913 | .@"try" => cg.airTry(inst), |
| 1922 | .try_cold => func.airTry(inst), | 1914 | .try_cold => cg.airTry(inst), |
| 1923 | .try_ptr => func.airTryPtr(inst), | 1915 | .try_ptr => cg.airTryPtr(inst), |
| 1924 | .try_ptr_cold => func.airTryPtr(inst), | 1916 | .try_ptr_cold => cg.airTryPtr(inst), |
| 1925 | 1917 | ||
| 1926 | .dbg_stmt => func.airDbgStmt(inst), | 1918 | .dbg_stmt => cg.airDbgStmt(inst), |
| 1927 | .dbg_empty_stmt => try func.finishAir(inst, .none, &.{}), | 1919 | .dbg_empty_stmt => try cg.finishAir(inst, .none, &.{}), |
| 1928 | .dbg_inline_block => func.airDbgInlineBlock(inst), | 1920 | .dbg_inline_block => cg.airDbgInlineBlock(inst), |
| 1929 | .dbg_var_ptr => func.airDbgVar(inst, .local_var, true), | 1921 | .dbg_var_ptr => cg.airDbgVar(inst, .local_var, true), |
| 1930 | .dbg_var_val => func.airDbgVar(inst, .local_var, false), | 1922 | .dbg_var_val => cg.airDbgVar(inst, .local_var, false), |
| 1931 | .dbg_arg_inline => func.airDbgVar(inst, .local_arg, false), | 1923 | .dbg_arg_inline => cg.airDbgVar(inst, .local_arg, false), |
| 1932 | 1924 | ||
| 1933 | .call => func.airCall(inst, .auto), | 1925 | .call => cg.airCall(inst, .auto), |
| 1934 | .call_always_tail => func.airCall(inst, .always_tail), | 1926 | .call_always_tail => cg.airCall(inst, .always_tail), |
| 1935 | .call_never_tail => func.airCall(inst, .never_tail), | 1927 | .call_never_tail => cg.airCall(inst, .never_tail), |
| 1936 | .call_never_inline => func.airCall(inst, .never_inline), | 1928 | .call_never_inline => cg.airCall(inst, .never_inline), |
| 1937 | 1929 | ||
| 1938 | .is_err => func.airIsErr(inst, .i32_ne), | 1930 | .is_err => cg.airIsErr(inst, .i32_ne), |
| 1939 | .is_non_err => func.airIsErr(inst, .i32_eq), | 1931 | .is_non_err => cg.airIsErr(inst, .i32_eq), |
| 1940 | 1932 | ||
| 1941 | .is_null => func.airIsNull(inst, .i32_eq, .value), | 1933 | .is_null => cg.airIsNull(inst, .i32_eq, .value), |
| 1942 | .is_non_null => func.airIsNull(inst, .i32_ne, .value), | 1934 | .is_non_null => cg.airIsNull(inst, .i32_ne, .value), |
| 1943 | .is_null_ptr => func.airIsNull(inst, .i32_eq, .ptr), | 1935 | .is_null_ptr => cg.airIsNull(inst, .i32_eq, .ptr), |
| 1944 | .is_non_null_ptr => func.airIsNull(inst, .i32_ne, .ptr), | 1936 | .is_non_null_ptr => cg.airIsNull(inst, .i32_ne, .ptr), |
| 1945 | 1937 | ||
| 1946 | .load => func.airLoad(inst), | 1938 | .load => cg.airLoad(inst), |
| 1947 | .loop => func.airLoop(inst), | 1939 | .loop => cg.airLoop(inst), |
| 1948 | .memset => func.airMemset(inst, false), | 1940 | .memset => cg.airMemset(inst, false), |
| 1949 | .memset_safe => func.airMemset(inst, true), | 1941 | .memset_safe => cg.airMemset(inst, true), |
| 1950 | .not => func.airNot(inst), | 1942 | .not => cg.airNot(inst), |
| 1951 | .optional_payload => func.airOptionalPayload(inst), | 1943 | .optional_payload => cg.airOptionalPayload(inst), |
| 1952 | .optional_payload_ptr => func.airOptionalPayloadPtr(inst), | 1944 | .optional_payload_ptr => cg.airOptionalPayloadPtr(inst), |
| 1953 | .optional_payload_ptr_set => func.airOptionalPayloadPtrSet(inst), | 1945 | .optional_payload_ptr_set => cg.airOptionalPayloadPtrSet(inst), |
| 1954 | .ptr_add => func.airPtrBinOp(inst, .add), | 1946 | .ptr_add => cg.airPtrBinOp(inst, .add), |
| 1955 | .ptr_sub => func.airPtrBinOp(inst, .sub), | 1947 | .ptr_sub => cg.airPtrBinOp(inst, .sub), |
| 1956 | .ptr_elem_ptr => func.airPtrElemPtr(inst), | 1948 | .ptr_elem_ptr => cg.airPtrElemPtr(inst), |
| 1957 | .ptr_elem_val => func.airPtrElemVal(inst), | 1949 | .ptr_elem_val => cg.airPtrElemVal(inst), |
| 1958 | .int_from_ptr => func.airIntFromPtr(inst), | 1950 | .int_from_ptr => cg.airIntFromPtr(inst), |
| 1959 | .ret => func.airRet(inst), | 1951 | .ret => cg.airRet(inst), |
| 1960 | .ret_safe => func.airRet(inst), // TODO | 1952 | .ret_safe => cg.airRet(inst), // TODO |
| 1961 | .ret_ptr => func.airRetPtr(inst), | 1953 | .ret_ptr => cg.airRetPtr(inst), |
| 1962 | .ret_load => func.airRetLoad(inst), | 1954 | .ret_load => cg.airRetLoad(inst), |
| 1963 | .splat => func.airSplat(inst), | 1955 | .splat => cg.airSplat(inst), |
| 1964 | .select => func.airSelect(inst), | 1956 | .select => cg.airSelect(inst), |
| 1965 | .shuffle => func.airShuffle(inst), | 1957 | .shuffle => cg.airShuffle(inst), |
| 1966 | .reduce => func.airReduce(inst), | 1958 | .reduce => cg.airReduce(inst), |
| 1967 | .aggregate_init => func.airAggregateInit(inst), | 1959 | .aggregate_init => cg.airAggregateInit(inst), |
| 1968 | .union_init => func.airUnionInit(inst), | 1960 | .union_init => cg.airUnionInit(inst), |
| 1969 | .prefetch => func.airPrefetch(inst), | 1961 | .prefetch => cg.airPrefetch(inst), |
| 1970 | .popcount => func.airPopcount(inst), | 1962 | .popcount => cg.airPopcount(inst), |
| 1971 | .byte_swap => func.airByteSwap(inst), | 1963 | .byte_swap => cg.airByteSwap(inst), |
| 1972 | .bit_reverse => func.airBitReverse(inst), | 1964 | .bit_reverse => cg.airBitReverse(inst), |
| 1973 | 1965 | ||
| 1974 | .slice => func.airSlice(inst), | 1966 | .slice => cg.airSlice(inst), |
| 1975 | .slice_len => func.airSliceLen(inst), | 1967 | .slice_len => cg.airSliceLen(inst), |
| 1976 | .slice_elem_val => func.airSliceElemVal(inst), | 1968 | .slice_elem_val => cg.airSliceElemVal(inst), |
| 1977 | .slice_elem_ptr => func.airSliceElemPtr(inst), | 1969 | .slice_elem_ptr => cg.airSliceElemPtr(inst), |
| 1978 | .slice_ptr => func.airSlicePtr(inst), | 1970 | .slice_ptr => cg.airSlicePtr(inst), |
| 1979 | .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()), | 1971 | .ptr_slice_len_ptr => cg.airPtrSliceFieldPtr(inst, cg.ptrSize()), |
| 1980 | .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0), | 1972 | .ptr_slice_ptr_ptr => cg.airPtrSliceFieldPtr(inst, 0), |
| 1981 | .store => func.airStore(inst, false), | 1973 | .store => cg.airStore(inst, false), |
| 1982 | .store_safe => func.airStore(inst, true), | 1974 | .store_safe => cg.airStore(inst, true), |
| 1983 | 1975 | ||
| 1984 | .set_union_tag => func.airSetUnionTag(inst), | 1976 | .set_union_tag => cg.airSetUnionTag(inst), |
| 1985 | .struct_field_ptr => func.airStructFieldPtr(inst), | 1977 | .struct_field_ptr => cg.airStructFieldPtr(inst), |
| 1986 | .struct_field_ptr_index_0 => func.airStructFieldPtrIndex(inst, 0), | 1978 | .struct_field_ptr_index_0 => cg.airStructFieldPtrIndex(inst, 0), |
| 1987 | .struct_field_ptr_index_1 => func.airStructFieldPtrIndex(inst, 1), | 1979 | .struct_field_ptr_index_1 => cg.airStructFieldPtrIndex(inst, 1), |
| 1988 | .struct_field_ptr_index_2 => func.airStructFieldPtrIndex(inst, 2), | 1980 | .struct_field_ptr_index_2 => cg.airStructFieldPtrIndex(inst, 2), |
| 1989 | .struct_field_ptr_index_3 => func.airStructFieldPtrIndex(inst, 3), | 1981 | .struct_field_ptr_index_3 => cg.airStructFieldPtrIndex(inst, 3), |
| 1990 | .struct_field_val => func.airStructFieldVal(inst), | 1982 | .struct_field_val => cg.airStructFieldVal(inst), |
| 1991 | .field_parent_ptr => func.airFieldParentPtr(inst), | 1983 | .field_parent_ptr => cg.airFieldParentPtr(inst), |
| 1992 | 1984 | ||
| 1993 | .switch_br => func.airSwitchBr(inst), | 1985 | .switch_br => cg.airSwitchBr(inst), |
| 1994 | .loop_switch_br => return func.fail("TODO implement `loop_switch_br`", .{}), | 1986 | .loop_switch_br => return cg.fail("TODO implement `loop_switch_br`", .{}), |
| 1995 | .trunc => func.airTrunc(inst), | 1987 | .trunc => cg.airTrunc(inst), |
| 1996 | .unreach => func.airUnreachable(inst), | 1988 | .unreach => cg.airUnreachable(inst), |
| 1997 | 1989 | ||
| 1998 | .wrap_optional => func.airWrapOptional(inst), | 1990 | .wrap_optional => cg.airWrapOptional(inst), |
| 1999 | .unwrap_errunion_payload => func.airUnwrapErrUnionPayload(inst, false), | 1991 | .unwrap_errunion_payload => cg.airUnwrapErrUnionPayload(inst, false), |
| 2000 | .unwrap_errunion_payload_ptr => func.airUnwrapErrUnionPayload(inst, true), | 1992 | .unwrap_errunion_payload_ptr => cg.airUnwrapErrUnionPayload(inst, true), |
| 2001 | .unwrap_errunion_err => func.airUnwrapErrUnionError(inst, false), | 1993 | .unwrap_errunion_err => cg.airUnwrapErrUnionError(inst, false), |
| 2002 | .unwrap_errunion_err_ptr => func.airUnwrapErrUnionError(inst, true), | 1994 | .unwrap_errunion_err_ptr => cg.airUnwrapErrUnionError(inst, true), |
| 2003 | .wrap_errunion_payload => func.airWrapErrUnionPayload(inst), | 1995 | .wrap_errunion_payload => cg.airWrapErrUnionPayload(inst), |
| 2004 | .wrap_errunion_err => func.airWrapErrUnionErr(inst), | 1996 | .wrap_errunion_err => cg.airWrapErrUnionErr(inst), |
| 2005 | .errunion_payload_ptr_set => func.airErrUnionPayloadPtrSet(inst), | 1997 | .errunion_payload_ptr_set => cg.airErrUnionPayloadPtrSet(inst), |
| 2006 | .error_name => func.airErrorName(inst), | 1998 | .error_name => cg.airErrorName(inst), |
| 2007 | 1999 | ||
| 2008 | .wasm_memory_size => func.airWasmMemorySize(inst), | 2000 | .wasm_memory_size => cg.airWasmMemorySize(inst), |
| 2009 | .wasm_memory_grow => func.airWasmMemoryGrow(inst), | 2001 | .wasm_memory_grow => cg.airWasmMemoryGrow(inst), |
| 2010 | 2002 | ||
| 2011 | .memcpy => func.airMemcpy(inst), | 2003 | .memcpy => cg.airMemcpy(inst), |
| 2012 | 2004 | ||
| 2013 | .ret_addr => func.airRetAddr(inst), | 2005 | .ret_addr => cg.airRetAddr(inst), |
| 2014 | .tag_name => func.airTagName(inst), | 2006 | .tag_name => cg.airTagName(inst), |
| 2015 | 2007 | ||
| 2016 | .error_set_has_value => func.airErrorSetHasValue(inst), | 2008 | .error_set_has_value => cg.airErrorSetHasValue(inst), |
| 2017 | .frame_addr => func.airFrameAddress(inst), | 2009 | .frame_addr => cg.airFrameAddress(inst), |
| 2018 | 2010 | ||
| 2019 | .assembly, | 2011 | .assembly, |
| 2020 | .is_err_ptr, | 2012 | .is_err_ptr, |
| ... | @@ -2030,18 +2022,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2030,18 +2022,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2030 | .c_va_copy, | 2022 | .c_va_copy, |
| 2031 | .c_va_end, | 2023 | .c_va_end, |
| 2032 | .c_va_start, | 2024 | .c_va_start, |
| 2033 | => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), | 2025 | => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 2034 | 2026 | ||
| 2035 | .atomic_load => func.airAtomicLoad(inst), | 2027 | .atomic_load => cg.airAtomicLoad(inst), |
| 2036 | .atomic_store_unordered, | 2028 | .atomic_store_unordered, |
| 2037 | .atomic_store_monotonic, | 2029 | .atomic_store_monotonic, |
| 2038 | .atomic_store_release, | 2030 | .atomic_store_release, |
| 2039 | .atomic_store_seq_cst, | 2031 | .atomic_store_seq_cst, |
| 2040 | // in WebAssembly, all atomic instructions are sequentially ordered. | 2032 | // in WebAssembly, all atomic instructions are sequentially ordered. |
| 2041 | => func.airAtomicStore(inst), | 2033 | => cg.airAtomicStore(inst), |
| 2042 | .atomic_rmw => func.airAtomicRmw(inst), | 2034 | .atomic_rmw => cg.airAtomicRmw(inst), |
| 2043 | .cmpxchg_weak => func.airCmpxchg(inst), | 2035 | .cmpxchg_weak => cg.airCmpxchg(inst), |
| 2044 | .cmpxchg_strong => func.airCmpxchg(inst), | 2036 | .cmpxchg_strong => cg.airCmpxchg(inst), |
| 2045 | 2037 | ||
| 2046 | .add_optimized, | 2038 | .add_optimized, |
| 2047 | .sub_optimized, | 2039 | .sub_optimized, |
| ... | @@ -2062,12 +2054,12 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2062,12 +2054,12 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2062 | .cmp_vector_optimized, | 2054 | .cmp_vector_optimized, |
| 2063 | .reduce_optimized, | 2055 | .reduce_optimized, |
| 2064 | .int_from_float_optimized, | 2056 | .int_from_float_optimized, |
| 2065 | => return func.fail("TODO implement optimized float mode", .{}), | 2057 | => return cg.fail("TODO implement optimized float mode", .{}), |
| 2066 | 2058 | ||
| 2067 | .add_safe, | 2059 | .add_safe, |
| 2068 | .sub_safe, | 2060 | .sub_safe, |
| 2069 | .mul_safe, | 2061 | .mul_safe, |
| 2070 | => return func.fail("TODO implement safety_checked_instructions", .{}), | 2062 | => return cg.fail("TODO implement safety_checked_instructions", .{}), |
| 2071 | 2063 | ||
| 2072 | .work_item_id, | 2064 | .work_item_id, |
| 2073 | .work_group_size, | 2065 | .work_group_size, |
| ... | @@ -2076,123 +2068,120 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2076,123 +2068,120 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2076 | }; | 2068 | }; |
| 2077 | } | 2069 | } |
| 2078 | 2070 | ||
| 2079 | fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void { | 2071 | fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 2080 | const pt = func.pt; | 2072 | const zcu = cg.pt.zcu; |
| 2081 | const zcu = pt.zcu; | ||
| 2082 | const ip = &zcu.intern_pool; | 2073 | const ip = &zcu.intern_pool; |
| 2083 | 2074 | ||
| 2084 | for (body) |inst| { | 2075 | for (body) |inst| { |
| 2085 | if (func.liveness.isUnused(inst) and !func.air.mustLower(inst, ip)) { | 2076 | if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) { |
| 2086 | continue; | 2077 | continue; |
| 2087 | } | 2078 | } |
| 2088 | const old_bookkeeping_value = func.air_bookkeeping; | 2079 | const old_bookkeeping_value = cg.air_bookkeeping; |
| 2089 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi); | 2080 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, Liveness.bpi); |
| 2090 | try func.genInst(inst); | 2081 | try cg.genInst(inst); |
| 2091 | 2082 | ||
| 2092 | if (std.debug.runtime_safety and func.air_bookkeeping < old_bookkeeping_value + 1) { | 2083 | if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) { |
| 2093 | std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{ | 2084 | std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{ |
| 2094 | inst, | 2085 | inst, |
| 2095 | func.air.instructions.items(.tag)[@intFromEnum(inst)], | 2086 | cg.air.instructions.items(.tag)[@intFromEnum(inst)], |
| 2096 | }); | 2087 | }); |
| 2097 | } | 2088 | } |
| 2098 | } | 2089 | } |
| 2099 | } | 2090 | } |
| 2100 | 2091 | ||
| 2101 | fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2092 | fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2102 | const pt = func.pt; | 2093 | const zcu = cg.pt.zcu; |
| 2103 | const zcu = pt.zcu; | 2094 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 2104 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 2095 | const operand = try cg.resolveInst(un_op); |
| 2105 | const operand = try func.resolveInst(un_op); | 2096 | const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; |
| 2106 | const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?; | ||
| 2107 | const ret_ty = Type.fromInterned(fn_info.return_type); | 2097 | const ret_ty = Type.fromInterned(fn_info.return_type); |
| 2108 | 2098 | ||
| 2109 | // result must be stored in the stack and we return a pointer | 2099 | // result must be stored in the stack and we return a pointer |
| 2110 | // to the stack instead | 2100 | // to the stack instead |
| 2111 | if (func.return_value != .none) { | 2101 | if (cg.return_value != .none) { |
| 2112 | try func.store(func.return_value, operand, ret_ty, 0); | 2102 | try cg.store(cg.return_value, operand, ret_ty, 0); |
| 2113 | } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 2103 | } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2114 | switch (ret_ty.zigTypeTag(zcu)) { | 2104 | switch (ret_ty.zigTypeTag(zcu)) { |
| 2115 | // Aggregate types can be lowered as a singular value | 2105 | // Aggregate types can be lowered as a singular value |
| 2116 | .@"struct", .@"union" => { | 2106 | .@"struct", .@"union" => { |
| 2117 | const scalar_type = abi.scalarType(ret_ty, zcu); | 2107 | const scalar_type = abi.scalarType(ret_ty, zcu); |
| 2118 | try func.emitWValue(operand); | 2108 | try cg.emitWValue(operand); |
| 2119 | const opcode = buildOpcode(.{ | 2109 | const opcode = buildOpcode(.{ |
| 2120 | .op = .load, | 2110 | .op = .load, |
| 2121 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), | 2111 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), |
| 2122 | .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned, | 2112 | .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned, |
| 2123 | .valtype1 = typeToValtype(scalar_type, pt, func.target.*), | 2113 | .valtype1 = typeToValtype(scalar_type, zcu, cg.target), |
| 2124 | }); | 2114 | }); |
| 2125 | try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ | 2115 | try cg.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ |
| 2126 | .offset = operand.offset(), | 2116 | .offset = operand.offset(), |
| 2127 | .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?), | 2117 | .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?), |
| 2128 | }); | 2118 | }); |
| 2129 | }, | 2119 | }, |
| 2130 | else => try func.emitWValue(operand), | 2120 | else => try cg.emitWValue(operand), |
| 2131 | } | 2121 | } |
| 2132 | } else { | 2122 | } else { |
| 2133 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) { | 2123 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) { |
| 2134 | try func.addImm32(0); | 2124 | try cg.addImm32(0); |
| 2135 | } else { | 2125 | } else { |
| 2136 | try func.emitWValue(operand); | 2126 | try cg.emitWValue(operand); |
| 2137 | } | 2127 | } |
| 2138 | } | 2128 | } |
| 2139 | try func.restoreStackPointer(); | 2129 | try cg.restoreStackPointer(); |
| 2140 | try func.addTag(.@"return"); | 2130 | try cg.addTag(.@"return"); |
| 2141 | 2131 | ||
| 2142 | return func.finishAir(inst, .none, &.{un_op}); | 2132 | return cg.finishAir(inst, .none, &.{un_op}); |
| 2143 | } | 2133 | } |
| 2144 | 2134 | ||
| 2145 | fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2135 | fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2146 | const pt = func.pt; | 2136 | const zcu = cg.pt.zcu; |
| 2147 | const zcu = pt.zcu; | 2137 | const child_type = cg.typeOfIndex(inst).childType(zcu); |
| 2148 | const child_type = func.typeOfIndex(inst).childType(zcu); | ||
| 2149 | 2138 | ||
| 2150 | const result = result: { | 2139 | const result = result: { |
| 2151 | if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { | 2140 | if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { |
| 2152 | break :result try func.allocStack(Type.usize); // create pointer to void | 2141 | break :result try cg.allocStack(Type.usize); // create pointer to void |
| 2153 | } | 2142 | } |
| 2154 | 2143 | ||
| 2155 | const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?; | 2144 | const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; |
| 2156 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | 2145 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) { |
| 2157 | break :result func.return_value; | 2146 | break :result cg.return_value; |
| 2158 | } | 2147 | } |
| 2159 | 2148 | ||
| 2160 | break :result try func.allocStackPtr(inst); | 2149 | break :result try cg.allocStackPtr(inst); |
| 2161 | }; | 2150 | }; |
| 2162 | 2151 | ||
| 2163 | return func.finishAir(inst, result, &.{}); | 2152 | return cg.finishAir(inst, result, &.{}); |
| 2164 | } | 2153 | } |
| 2165 | 2154 | ||
| 2166 | fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2155 | fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2167 | const pt = func.pt; | 2156 | const zcu = cg.pt.zcu; |
| 2168 | const zcu = pt.zcu; | 2157 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 2169 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 2158 | const operand = try cg.resolveInst(un_op); |
| 2170 | const operand = try func.resolveInst(un_op); | 2159 | const ret_ty = cg.typeOf(un_op).childType(zcu); |
| 2171 | const ret_ty = func.typeOf(un_op).childType(zcu); | ||
| 2172 | 2160 | ||
| 2173 | const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?; | 2161 | const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; |
| 2174 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 2162 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2175 | if (ret_ty.isError(zcu)) { | 2163 | if (ret_ty.isError(zcu)) { |
| 2176 | try func.addImm32(0); | 2164 | try cg.addImm32(0); |
| 2177 | } | 2165 | } |
| 2178 | } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | 2166 | } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) { |
| 2179 | // leave on the stack | 2167 | // leave on the stack |
| 2180 | _ = try func.load(operand, ret_ty, 0); | 2168 | _ = try cg.load(operand, ret_ty, 0); |
| 2181 | } | 2169 | } |
| 2182 | 2170 | ||
| 2183 | try func.restoreStackPointer(); | 2171 | try cg.restoreStackPointer(); |
| 2184 | try func.addTag(.@"return"); | 2172 | try cg.addTag(.@"return"); |
| 2185 | return func.finishAir(inst, .none, &.{un_op}); | 2173 | return cg.finishAir(inst, .none, &.{un_op}); |
| 2186 | } | 2174 | } |
| 2187 | 2175 | ||
| 2188 | fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { | 2176 | fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { |
| 2189 | if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{}); | 2177 | const wasm = cg.wasm; |
| 2190 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 2178 | if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); |
| 2191 | const extra = func.air.extraData(Air.Call, pl_op.payload); | 2179 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2192 | const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len])); | 2180 | const extra = cg.air.extraData(Air.Call, pl_op.payload); |
| 2193 | const ty = func.typeOf(pl_op.operand); | 2181 | const args: []const Air.Inst.Ref = @ptrCast(cg.air.extra[extra.end..][0..extra.data.args_len]); |
| 2182 | const ty = cg.typeOf(pl_op.operand); | ||
| 2194 | 2183 | ||
| 2195 | const pt = func.pt; | 2184 | const pt = cg.pt; |
| 2196 | const zcu = pt.zcu; | 2185 | const zcu = pt.zcu; |
| 2197 | const ip = &zcu.intern_pool; | 2186 | const ip = &zcu.intern_pool; |
| 2198 | const fn_ty = switch (ty.zigTypeTag(zcu)) { | 2187 | const fn_ty = switch (ty.zigTypeTag(zcu)) { |
| ... | @@ -2202,142 +2191,109 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif | ... | @@ -2202,142 +2191,109 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2202 | }; | 2191 | }; |
| 2203 | const ret_ty = fn_ty.fnReturnType(zcu); | 2192 | const ret_ty = fn_ty.fnReturnType(zcu); |
| 2204 | const fn_info = zcu.typeToFunc(fn_ty).?; | 2193 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 2205 | const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*); | 2194 | const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target); |
| 2206 | 2195 | ||
| 2207 | const callee: ?InternPool.Nav.Index = blk: { | 2196 | const callee: ?InternPool.Nav.Index = blk: { |
| 2208 | const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null; | 2197 | const func_val = (try cg.air.value(pl_op.operand, pt)) orelse break :blk null; |
| 2209 | 2198 | ||
| 2210 | switch (ip.indexToKey(func_val.toIntern())) { | 2199 | switch (ip.indexToKey(func_val.toIntern())) { |
| 2211 | .func => |function| { | 2200 | 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) { | 2201 | .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { |
| 2239 | .nav => |nav| { | 2202 | .nav => |nav| break :blk nav, |
| 2240 | _ = try func.bin_file.getOrCreateAtomForNav(pt, nav); | ||
| 2241 | break :blk nav; | ||
| 2242 | }, | ||
| 2243 | else => {}, | 2203 | else => {}, |
| 2244 | }, | 2204 | }, |
| 2245 | else => {}, | 2205 | else => {}, |
| 2246 | } | 2206 | } |
| 2247 | return func.fail("Expected a function, but instead found '{s}'", .{@tagName(ip.indexToKey(func_val.toIntern()))}); | 2207 | return cg.fail("unable to lower callee to a function index", .{}); |
| 2248 | }; | 2208 | }; |
| 2249 | 2209 | ||
| 2250 | const sret: WValue = if (first_param_sret) blk: { | 2210 | const sret: WValue = if (first_param_sret) blk: { |
| 2251 | const sret_local = try func.allocStack(ret_ty); | 2211 | const sret_local = try cg.allocStack(ret_ty); |
| 2252 | try func.lowerToStack(sret_local); | 2212 | try cg.lowerToStack(sret_local); |
| 2253 | break :blk sret_local; | 2213 | break :blk sret_local; |
| 2254 | } else .none; | 2214 | } else .none; |
| 2255 | 2215 | ||
| 2256 | for (args) |arg| { | 2216 | for (args) |arg| { |
| 2257 | const arg_val = try func.resolveInst(arg); | 2217 | const arg_val = try cg.resolveInst(arg); |
| 2258 | 2218 | ||
| 2259 | const arg_ty = func.typeOf(arg); | 2219 | const arg_ty = cg.typeOf(arg); |
| 2260 | if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; | 2220 | if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; |
| 2261 | 2221 | ||
| 2262 | try func.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val); | 2222 | try cg.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val); |
| 2263 | } | 2223 | } |
| 2264 | 2224 | ||
| 2265 | if (callee) |direct| { | 2225 | if (callee) |nav_index| { |
| 2266 | const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom; | 2226 | try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } }); |
| 2267 | try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index)); | ||
| 2268 | } else { | 2227 | } else { |
| 2269 | // in this case we call a function pointer | 2228 | // in this case we call a function pointer |
| 2270 | // so load its value onto the stack | 2229 | // so load its value onto the stack |
| 2271 | std.debug.assert(ty.zigTypeTag(zcu) == .pointer); | 2230 | assert(ty.zigTypeTag(zcu) == .pointer); |
| 2272 | const operand = try func.resolveInst(pl_op.operand); | 2231 | const operand = try cg.resolveInst(pl_op.operand); |
| 2273 | try func.emitWValue(operand); | 2232 | try cg.emitWValue(operand); |
| 2274 | |||
| 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.*); | ||
| 2276 | defer fn_type.deinit(func.gpa); | ||
| 2277 | 2233 | ||
| 2278 | const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type); | 2234 | const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target); |
| 2279 | try func.addLabel(.call_indirect, fn_type_index); | 2235 | try cg.addFuncTy(.call_indirect, fn_type_index); |
| 2280 | } | 2236 | } |
| 2281 | 2237 | ||
| 2282 | const result_value = result_value: { | 2238 | const result_value = result_value: { |
| 2283 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { | 2239 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { |
| 2284 | break :result_value .none; | 2240 | break :result_value .none; |
| 2285 | } else if (ret_ty.isNoReturn(zcu)) { | 2241 | } else if (ret_ty.isNoReturn(zcu)) { |
| 2286 | try func.addTag(.@"unreachable"); | 2242 | try cg.addTag(.@"unreachable"); |
| 2287 | break :result_value .none; | 2243 | break :result_value .none; |
| 2288 | } else if (first_param_sret) { | 2244 | } else if (first_param_sret) { |
| 2289 | break :result_value sret; | 2245 | break :result_value sret; |
| 2290 | // TODO: Make this less fragile and optimize | 2246 | // TODO: Make this less fragile and optimize |
| 2291 | } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_watc and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") { | 2247 | } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_watc and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") { |
| 2292 | const result_local = try func.allocLocal(ret_ty); | 2248 | const result_local = try cg.allocLocal(ret_ty); |
| 2293 | try func.addLabel(.local_set, result_local.local.value); | 2249 | try cg.addLocal(.local_set, result_local.local.value); |
| 2294 | const scalar_type = abi.scalarType(ret_ty, zcu); | 2250 | const scalar_type = abi.scalarType(ret_ty, zcu); |
| 2295 | const result = try func.allocStack(scalar_type); | 2251 | const result = try cg.allocStack(scalar_type); |
| 2296 | try func.store(result, result_local, scalar_type, 0); | 2252 | try cg.store(result, result_local, scalar_type, 0); |
| 2297 | break :result_value result; | 2253 | break :result_value result; |
| 2298 | } else { | 2254 | } else { |
| 2299 | const result_local = try func.allocLocal(ret_ty); | 2255 | const result_local = try cg.allocLocal(ret_ty); |
| 2300 | try func.addLabel(.local_set, result_local.local.value); | 2256 | try cg.addLocal(.local_set, result_local.local.value); |
| 2301 | break :result_value result_local; | 2257 | break :result_value result_local; |
| 2302 | } | 2258 | } |
| 2303 | }; | 2259 | }; |
| 2304 | 2260 | ||
| 2305 | var bt = try func.iterateBigTomb(inst, 1 + args.len); | 2261 | var bt = try cg.iterateBigTomb(inst, 1 + args.len); |
| 2306 | bt.feed(pl_op.operand); | 2262 | bt.feed(pl_op.operand); |
| 2307 | for (args) |arg| bt.feed(arg); | 2263 | for (args) |arg| bt.feed(arg); |
| 2308 | return bt.finishAir(result_value); | 2264 | return bt.finishAir(result_value); |
| 2309 | } | 2265 | } |
| 2310 | 2266 | ||
| 2311 | fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2267 | fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2312 | const value = try func.allocStackPtr(inst); | 2268 | const value = try cg.allocStackPtr(inst); |
| 2313 | return func.finishAir(inst, value, &.{}); | 2269 | return cg.finishAir(inst, value, &.{}); |
| 2314 | } | 2270 | } |
| 2315 | 2271 | ||
| 2316 | fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { | 2272 | fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { |
| 2317 | const pt = func.pt; | 2273 | const pt = cg.pt; |
| 2318 | const zcu = pt.zcu; | 2274 | const zcu = pt.zcu; |
| 2319 | if (safety) { | 2275 | if (safety) { |
| 2320 | // TODO if the value is undef, write 0xaa bytes to dest | 2276 | // TODO if the value is undef, write 0xaa bytes to dest |
| 2321 | } else { | 2277 | } else { |
| 2322 | // TODO if the value is undef, don't lower this instruction | 2278 | // TODO if the value is undef, don't lower this instruction |
| 2323 | } | 2279 | } |
| 2324 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 2280 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2325 | 2281 | ||
| 2326 | const lhs = try func.resolveInst(bin_op.lhs); | 2282 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 2327 | const rhs = try func.resolveInst(bin_op.rhs); | 2283 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 2328 | const ptr_ty = func.typeOf(bin_op.lhs); | 2284 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 2329 | const ptr_info = ptr_ty.ptrInfo(zcu); | 2285 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 2330 | const ty = ptr_ty.childType(zcu); | 2286 | const ty = ptr_ty.childType(zcu); |
| 2331 | 2287 | ||
| 2332 | if (ptr_info.packed_offset.host_size == 0) { | 2288 | if (ptr_info.packed_offset.host_size == 0) { |
| 2333 | try func.store(lhs, rhs, ty, 0); | 2289 | try cg.store(lhs, rhs, ty, 0); |
| 2334 | } else { | 2290 | } else { |
| 2335 | // at this point we have a non-natural alignment, we must | 2291 | // at this point we have a non-natural alignment, we must |
| 2336 | // load the value, and then shift+or the rhs into the result location. | 2292 | // load the value, and then shift+or the rhs into the result location. |
| 2337 | const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8); | 2293 | const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8); |
| 2338 | 2294 | ||
| 2339 | if (isByRef(int_elem_ty, pt, func.target.*)) { | 2295 | if (isByRef(int_elem_ty, zcu, cg.target)) { |
| 2340 | return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{}); | 2296 | return cg.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{}); |
| 2341 | } | 2297 | } |
| 2342 | 2298 | ||
| 2343 | var mask = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(ty.bitSize(zcu)))) - 1)); | 2299 | var mask = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(ty.bitSize(zcu)))) - 1)); |
| ... | @@ -2356,115 +2312,115 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void | ... | @@ -2356,115 +2312,115 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 2356 | else | 2312 | else |
| 2357 | .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) }; | 2313 | .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) }; |
| 2358 | 2314 | ||
| 2359 | try func.emitWValue(lhs); | 2315 | try cg.emitWValue(lhs); |
| 2360 | const loaded = try func.load(lhs, int_elem_ty, 0); | 2316 | const loaded = try cg.load(lhs, int_elem_ty, 0); |
| 2361 | const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and"); | 2317 | const anded = try cg.binOp(loaded, mask_val, int_elem_ty, .@"and"); |
| 2362 | const extended_value = try func.intcast(rhs, ty, int_elem_ty); | 2318 | const extended_value = try cg.intcast(rhs, ty, int_elem_ty); |
| 2363 | const masked_value = try func.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and"); | 2319 | const masked_value = try cg.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and"); |
| 2364 | const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: { | 2320 | const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: { |
| 2365 | break :shifted try func.binOp(masked_value, shift_val, int_elem_ty, .shl); | 2321 | break :shifted try cg.binOp(masked_value, shift_val, int_elem_ty, .shl); |
| 2366 | } else masked_value; | 2322 | } else masked_value; |
| 2367 | const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or"); | 2323 | const result = try cg.binOp(anded, shifted_value, int_elem_ty, .@"or"); |
| 2368 | // lhs is still on the stack | 2324 | // lhs is still on the stack |
| 2369 | try func.store(.stack, result, int_elem_ty, lhs.offset()); | 2325 | try cg.store(.stack, result, int_elem_ty, lhs.offset()); |
| 2370 | } | 2326 | } |
| 2371 | 2327 | ||
| 2372 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 2328 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 2373 | } | 2329 | } |
| 2374 | 2330 | ||
| 2375 | fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { | 2331 | fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { |
| 2376 | assert(!(lhs != .stack and rhs == .stack)); | 2332 | assert(!(lhs != .stack and rhs == .stack)); |
| 2377 | const pt = func.pt; | 2333 | const pt = cg.pt; |
| 2378 | const zcu = pt.zcu; | 2334 | const zcu = pt.zcu; |
| 2379 | const abi_size = ty.abiSize(zcu); | 2335 | const abi_size = ty.abiSize(zcu); |
| 2380 | switch (ty.zigTypeTag(zcu)) { | 2336 | switch (ty.zigTypeTag(zcu)) { |
| 2381 | .error_union => { | 2337 | .error_union => { |
| 2382 | const pl_ty = ty.errorUnionPayload(zcu); | 2338 | const pl_ty = ty.errorUnionPayload(zcu); |
| 2383 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 2339 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2384 | return func.store(lhs, rhs, Type.anyerror, 0); | 2340 | return cg.store(lhs, rhs, Type.anyerror, 0); |
| 2385 | } | 2341 | } |
| 2386 | 2342 | ||
| 2387 | const len = @as(u32, @intCast(abi_size)); | 2343 | const len = @as(u32, @intCast(abi_size)); |
| 2388 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2344 | return cg.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2389 | }, | 2345 | }, |
| 2390 | .optional => { | 2346 | .optional => { |
| 2391 | if (ty.isPtrLikeOptional(zcu)) { | 2347 | if (ty.isPtrLikeOptional(zcu)) { |
| 2392 | return func.store(lhs, rhs, Type.usize, 0); | 2348 | return cg.store(lhs, rhs, Type.usize, 0); |
| 2393 | } | 2349 | } |
| 2394 | const pl_ty = ty.optionalChild(zcu); | 2350 | const pl_ty = ty.optionalChild(zcu); |
| 2395 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 2351 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2396 | return func.store(lhs, rhs, Type.u8, 0); | 2352 | return cg.store(lhs, rhs, Type.u8, 0); |
| 2397 | } | 2353 | } |
| 2398 | if (pl_ty.zigTypeTag(zcu) == .error_set) { | 2354 | if (pl_ty.zigTypeTag(zcu) == .error_set) { |
| 2399 | return func.store(lhs, rhs, Type.anyerror, 0); | 2355 | return cg.store(lhs, rhs, Type.anyerror, 0); |
| 2400 | } | 2356 | } |
| 2401 | 2357 | ||
| 2402 | const len = @as(u32, @intCast(abi_size)); | 2358 | const len = @as(u32, @intCast(abi_size)); |
| 2403 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2359 | return cg.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2404 | }, | 2360 | }, |
| 2405 | .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target.*)) { | 2361 | .@"struct", .array, .@"union" => if (isByRef(ty, zcu, cg.target)) { |
| 2406 | const len = @as(u32, @intCast(abi_size)); | 2362 | const len = @as(u32, @intCast(abi_size)); |
| 2407 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2363 | return cg.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2408 | }, | 2364 | }, |
| 2409 | .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target.*)) { | 2365 | .vector => switch (determineSimdStoreStrategy(ty, zcu, cg.target)) { |
| 2410 | .unrolled => { | 2366 | .unrolled => { |
| 2411 | const len: u32 = @intCast(abi_size); | 2367 | const len: u32 = @intCast(abi_size); |
| 2412 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2368 | return cg.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2413 | }, | 2369 | }, |
| 2414 | .direct => { | 2370 | .direct => { |
| 2415 | try func.emitWValue(lhs); | 2371 | try cg.emitWValue(lhs); |
| 2416 | try func.lowerToStack(rhs); | 2372 | try cg.lowerToStack(rhs); |
| 2417 | // TODO: Add helper functions for simd opcodes | 2373 | // TODO: Add helper functions for simd opcodes |
| 2418 | const extra_index: u32 = @intCast(func.mir_extra.items.len); | 2374 | const extra_index = cg.extraLen(); |
| 2419 | // stores as := opcode, offset, alignment (opcode::memarg) | 2375 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2420 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ | 2376 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 2421 | std.wasm.simdOpcode(.v128_store), | 2377 | @intFromEnum(std.wasm.SimdOpcode.v128_store), |
| 2422 | offset + lhs.offset(), | 2378 | offset + lhs.offset(), |
| 2423 | @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0), | 2379 | @intCast(ty.abiAlignment(zcu).toByteUnits() orelse 0), |
| 2424 | }); | 2380 | }); |
| 2425 | return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 2381 | return cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 2426 | }, | 2382 | }, |
| 2427 | }, | 2383 | }, |
| 2428 | .pointer => { | 2384 | .pointer => { |
| 2429 | if (ty.isSlice(zcu)) { | 2385 | if (ty.isSlice(zcu)) { |
| 2430 | // store pointer first | 2386 | // store pointer first |
| 2431 | // lower it to the stack so we do not have to store rhs into a local first | 2387 | // lower it to the stack so we do not have to store rhs into a local first |
| 2432 | try func.emitWValue(lhs); | 2388 | try cg.emitWValue(lhs); |
| 2433 | const ptr_local = try func.load(rhs, Type.usize, 0); | 2389 | const ptr_local = try cg.load(rhs, Type.usize, 0); |
| 2434 | try func.store(.stack, ptr_local, Type.usize, 0 + lhs.offset()); | 2390 | try cg.store(.stack, ptr_local, Type.usize, 0 + lhs.offset()); |
| 2435 | 2391 | ||
| 2436 | // retrieve length from rhs, and store that alongside lhs as well | 2392 | // retrieve length from rhs, and store that alongside lhs as well |
| 2437 | try func.emitWValue(lhs); | 2393 | try cg.emitWValue(lhs); |
| 2438 | const len_local = try func.load(rhs, Type.usize, func.ptrSize()); | 2394 | const len_local = try cg.load(rhs, Type.usize, cg.ptrSize()); |
| 2439 | try func.store(.stack, len_local, Type.usize, func.ptrSize() + lhs.offset()); | 2395 | try cg.store(.stack, len_local, Type.usize, cg.ptrSize() + lhs.offset()); |
| 2440 | return; | 2396 | return; |
| 2441 | } | 2397 | } |
| 2442 | }, | 2398 | }, |
| 2443 | .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) { | 2399 | .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) { |
| 2444 | try func.emitWValue(lhs); | 2400 | try cg.emitWValue(lhs); |
| 2445 | const lsb = try func.load(rhs, Type.u64, 0); | 2401 | const lsb = try cg.load(rhs, Type.u64, 0); |
| 2446 | try func.store(.stack, lsb, Type.u64, 0 + lhs.offset()); | 2402 | try cg.store(.stack, lsb, Type.u64, 0 + lhs.offset()); |
| 2447 | 2403 | ||
| 2448 | try func.emitWValue(lhs); | 2404 | try cg.emitWValue(lhs); |
| 2449 | const msb = try func.load(rhs, Type.u64, 8); | 2405 | const msb = try cg.load(rhs, Type.u64, 8); |
| 2450 | try func.store(.stack, msb, Type.u64, 8 + lhs.offset()); | 2406 | try cg.store(.stack, msb, Type.u64, 8 + lhs.offset()); |
| 2451 | return; | 2407 | return; |
| 2452 | } else if (abi_size > 16) { | 2408 | } else if (abi_size > 16) { |
| 2453 | try func.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) }); | 2409 | try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) }); |
| 2454 | }, | 2410 | }, |
| 2455 | else => if (abi_size > 8) { | 2411 | else => if (abi_size > 8) { |
| 2456 | return func.fail("TODO: `store` for type `{}` with abisize `{d}`", .{ | 2412 | return cg.fail("TODO: `store` for type `{}` with abisize `{d}`", .{ |
| 2457 | ty.fmt(pt), | 2413 | ty.fmt(pt), |
| 2458 | abi_size, | 2414 | abi_size, |
| 2459 | }); | 2415 | }); |
| 2460 | }, | 2416 | }, |
| 2461 | } | 2417 | } |
| 2462 | try func.emitWValue(lhs); | 2418 | try cg.emitWValue(lhs); |
| 2463 | // In this case we're actually interested in storing the stack position | 2419 | // In this case we're actually interested in storing the stack position |
| 2464 | // into lhs, so we calculate that and emit that instead | 2420 | // into lhs, so we calculate that and emit that instead |
| 2465 | try func.lowerToStack(rhs); | 2421 | try cg.lowerToStack(rhs); |
| 2466 | 2422 | ||
| 2467 | const valtype = typeToValtype(ty, pt, func.target.*); | 2423 | const valtype = typeToValtype(ty, zcu, cg.target); |
| 2468 | const opcode = buildOpcode(.{ | 2424 | const opcode = buildOpcode(.{ |
| 2469 | .valtype1 = valtype, | 2425 | .valtype1 = valtype, |
| 2470 | .width = @as(u8, @intCast(abi_size * 8)), | 2426 | .width = @as(u8, @intCast(abi_size * 8)), |
| ... | @@ -2472,7 +2428,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE | ... | @@ -2472,7 +2428,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE |
| 2472 | }); | 2428 | }); |
| 2473 | 2429 | ||
| 2474 | // store rhs value at stack pointer's location in memory | 2430 | // store rhs value at stack pointer's location in memory |
| 2475 | try func.addMemArg( | 2431 | try cg.addMemArg( |
| 2476 | Mir.Inst.Tag.fromOpcode(opcode), | 2432 | Mir.Inst.Tag.fromOpcode(opcode), |
| 2477 | .{ | 2433 | .{ |
| 2478 | .offset = offset + lhs.offset(), | 2434 | .offset = offset + lhs.offset(), |
| ... | @@ -2481,26 +2437,26 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE | ... | @@ -2481,26 +2437,26 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE |
| 2481 | ); | 2437 | ); |
| 2482 | } | 2438 | } |
| 2483 | 2439 | ||
| 2484 | fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2440 | fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2485 | const pt = func.pt; | 2441 | const pt = cg.pt; |
| 2486 | const zcu = pt.zcu; | 2442 | const zcu = pt.zcu; |
| 2487 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 2443 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 2488 | const operand = try func.resolveInst(ty_op.operand); | 2444 | const operand = try cg.resolveInst(ty_op.operand); |
| 2489 | const ty = ty_op.ty.toType(); | 2445 | const ty = ty_op.ty.toType(); |
| 2490 | const ptr_ty = func.typeOf(ty_op.operand); | 2446 | const ptr_ty = cg.typeOf(ty_op.operand); |
| 2491 | const ptr_info = ptr_ty.ptrInfo(zcu); | 2447 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 2492 | 2448 | ||
| 2493 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{ty_op.operand}); | 2449 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return cg.finishAir(inst, .none, &.{ty_op.operand}); |
| 2494 | 2450 | ||
| 2495 | const result = result: { | 2451 | const result = result: { |
| 2496 | if (isByRef(ty, pt, func.target.*)) { | 2452 | if (isByRef(ty, zcu, cg.target)) { |
| 2497 | const new_local = try func.allocStack(ty); | 2453 | const new_local = try cg.allocStack(ty); |
| 2498 | try func.store(new_local, operand, ty, 0); | 2454 | try cg.store(new_local, operand, ty, 0); |
| 2499 | break :result new_local; | 2455 | break :result new_local; |
| 2500 | } | 2456 | } |
| 2501 | 2457 | ||
| 2502 | if (ptr_info.packed_offset.host_size == 0) { | 2458 | if (ptr_info.packed_offset.host_size == 0) { |
| 2503 | break :result try func.load(operand, ty, 0); | 2459 | break :result try cg.load(operand, ty, 0); |
| 2504 | } | 2460 | } |
| 2505 | 2461 | ||
| 2506 | // at this point we have a non-natural alignment, we must | 2462 | // at this point we have a non-natural alignment, we must |
| ... | @@ -2511,45 +2467,44 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2511,45 +2467,44 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2511 | else if (ptr_info.packed_offset.host_size <= 8) | 2467 | else if (ptr_info.packed_offset.host_size <= 8) |
| 2512 | .{ .imm64 = ptr_info.packed_offset.bit_offset } | 2468 | .{ .imm64 = ptr_info.packed_offset.bit_offset } |
| 2513 | else | 2469 | else |
| 2514 | return func.fail("TODO: airLoad where ptr to bitfield exceeds 64 bits", .{}); | 2470 | return cg.fail("TODO: airLoad where ptr to bitfield exceeds 64 bits", .{}); |
| 2515 | 2471 | ||
| 2516 | const stack_loaded = try func.load(operand, int_elem_ty, 0); | 2472 | const stack_loaded = try cg.load(operand, int_elem_ty, 0); |
| 2517 | const shifted = try func.binOp(stack_loaded, shift_val, int_elem_ty, .shr); | 2473 | const shifted = try cg.binOp(stack_loaded, shift_val, int_elem_ty, .shr); |
| 2518 | break :result try func.trunc(shifted, ty, int_elem_ty); | 2474 | break :result try cg.trunc(shifted, ty, int_elem_ty); |
| 2519 | }; | 2475 | }; |
| 2520 | return func.finishAir(inst, result, &.{ty_op.operand}); | 2476 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 2521 | } | 2477 | } |
| 2522 | 2478 | ||
| 2523 | /// Loads an operand from the linear memory section. | 2479 | /// Loads an operand from the linear memory section. |
| 2524 | /// NOTE: Leaves the value on the stack. | 2480 | /// NOTE: Leaves the value on the stack. |
| 2525 | fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | 2481 | fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 2526 | const pt = func.pt; | 2482 | const zcu = cg.pt.zcu; |
| 2527 | const zcu = pt.zcu; | ||
| 2528 | // load local's value from memory by its stack position | 2483 | // load local's value from memory by its stack position |
| 2529 | try func.emitWValue(operand); | 2484 | try cg.emitWValue(operand); |
| 2530 | 2485 | ||
| 2531 | if (ty.zigTypeTag(zcu) == .vector) { | 2486 | if (ty.zigTypeTag(zcu) == .vector) { |
| 2532 | // TODO: Add helper functions for simd opcodes | 2487 | // TODO: Add helper functions for simd opcodes |
| 2533 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | 2488 | const extra_index = cg.extraLen(); |
| 2534 | // stores as := opcode, offset, alignment (opcode::memarg) | 2489 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2535 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ | 2490 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 2536 | std.wasm.simdOpcode(.v128_load), | 2491 | @intFromEnum(std.wasm.SimdOpcode.v128_load), |
| 2537 | offset + operand.offset(), | 2492 | offset + operand.offset(), |
| 2538 | @intCast(ty.abiAlignment(zcu).toByteUnits().?), | 2493 | @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 2539 | }); | 2494 | }); |
| 2540 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 2495 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 2541 | return .stack; | 2496 | return .stack; |
| 2542 | } | 2497 | } |
| 2543 | 2498 | ||
| 2544 | const abi_size: u8 = @intCast(ty.abiSize(zcu)); | 2499 | const abi_size: u8 = @intCast(ty.abiSize(zcu)); |
| 2545 | const opcode = buildOpcode(.{ | 2500 | const opcode = buildOpcode(.{ |
| 2546 | .valtype1 = typeToValtype(ty, pt, func.target.*), | 2501 | .valtype1 = typeToValtype(ty, zcu, cg.target), |
| 2547 | .width = abi_size * 8, | 2502 | .width = abi_size * 8, |
| 2548 | .op = .load, | 2503 | .op = .load, |
| 2549 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, | 2504 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, |
| 2550 | }); | 2505 | }); |
| 2551 | 2506 | ||
| 2552 | try func.addMemArg( | 2507 | try cg.addMemArg( |
| 2553 | Mir.Inst.Tag.fromOpcode(opcode), | 2508 | Mir.Inst.Tag.fromOpcode(opcode), |
| 2554 | .{ | 2509 | .{ |
| 2555 | .offset = offset + operand.offset(), | 2510 | .offset = offset + operand.offset(), |
| ... | @@ -2560,18 +2515,18 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu | ... | @@ -2560,18 +2515,18 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu |
| 2560 | return .stack; | 2515 | return .stack; |
| 2561 | } | 2516 | } |
| 2562 | 2517 | ||
| 2563 | fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2518 | fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2564 | const pt = func.pt; | 2519 | const pt = cg.pt; |
| 2565 | const zcu = pt.zcu; | 2520 | const zcu = pt.zcu; |
| 2566 | const arg_index = func.arg_index; | 2521 | const arg_index = cg.arg_index; |
| 2567 | const arg = func.args[arg_index]; | 2522 | const arg = cg.args[arg_index]; |
| 2568 | const cc = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?.cc; | 2523 | const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc; |
| 2569 | const arg_ty = func.typeOfIndex(inst); | 2524 | const arg_ty = cg.typeOfIndex(inst); |
| 2570 | if (cc == .wasm_watc) { | 2525 | if (cc == .wasm_watc) { |
| 2571 | const arg_classes = abi.classifyType(arg_ty, zcu); | 2526 | const arg_classes = abi.classifyType(arg_ty, zcu); |
| 2572 | for (arg_classes) |class| { | 2527 | for (arg_classes) |class| { |
| 2573 | if (class != .none) { | 2528 | if (class != .none) { |
| 2574 | func.arg_index += 1; | 2529 | cg.arg_index += 1; |
| 2575 | } | 2530 | } |
| 2576 | } | 2531 | } |
| 2577 | 2532 | ||
| ... | @@ -2579,44 +2534,30 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2579,44 +2534,30 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2579 | // we combine them into a single stack value | 2534 | // we combine them into a single stack value |
| 2580 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { | 2535 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { |
| 2581 | if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) { | 2536 | if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) { |
| 2582 | return func.fail( | 2537 | return cg.fail( |
| 2583 | "TODO: Implement C-ABI argument for type '{}'", | 2538 | "TODO: Implement C-ABI argument for type '{}'", |
| 2584 | .{arg_ty.fmt(pt)}, | 2539 | .{arg_ty.fmt(pt)}, |
| 2585 | ); | 2540 | ); |
| 2586 | } | 2541 | } |
| 2587 | const result = try func.allocStack(arg_ty); | 2542 | const result = try cg.allocStack(arg_ty); |
| 2588 | try func.store(result, arg, Type.u64, 0); | 2543 | try cg.store(result, arg, Type.u64, 0); |
| 2589 | try func.store(result, func.args[arg_index + 1], Type.u64, 8); | 2544 | try cg.store(result, cg.args[arg_index + 1], Type.u64, 8); |
| 2590 | return func.finishAir(inst, result, &.{}); | 2545 | return cg.finishAir(inst, result, &.{}); |
| 2591 | } | 2546 | } |
| 2592 | } else { | 2547 | } else { |
| 2593 | func.arg_index += 1; | 2548 | cg.arg_index += 1; |
| 2594 | } | ||
| 2595 | |||
| 2596 | switch (func.debug_output) { | ||
| 2597 | .dwarf => |dwarf| { | ||
| 2598 | const name = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; | ||
| 2599 | if (name != .none) try dwarf.genLocalDebugInfo( | ||
| 2600 | .local_arg, | ||
| 2601 | name.toSlice(func.air), | ||
| 2602 | arg_ty, | ||
| 2603 | .{ .wasm_ext = .{ .local = arg.local.value } }, | ||
| 2604 | ); | ||
| 2605 | }, | ||
| 2606 | else => {}, | ||
| 2607 | } | 2549 | } |
| 2608 | 2550 | ||
| 2609 | return func.finishAir(inst, arg, &.{}); | 2551 | return cg.finishAir(inst, arg, &.{}); |
| 2610 | } | 2552 | } |
| 2611 | 2553 | ||
| 2612 | fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 2554 | fn airBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 2613 | const pt = func.pt; | 2555 | const zcu = cg.pt.zcu; |
| 2614 | const zcu = pt.zcu; | 2556 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2615 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 2557 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 2616 | const lhs = try func.resolveInst(bin_op.lhs); | 2558 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 2617 | const rhs = try func.resolveInst(bin_op.rhs); | 2559 | const lhs_ty = cg.typeOf(bin_op.lhs); |
| 2618 | const lhs_ty = func.typeOf(bin_op.lhs); | 2560 | const rhs_ty = cg.typeOf(bin_op.rhs); |
| 2619 | const rhs_ty = func.typeOf(bin_op.rhs); | ||
| 2620 | 2561 | ||
| 2621 | // For certain operations, such as shifting, the types are different. | 2562 | // For certain operations, such as shifting, the types are different. |
| 2622 | // When converting this to a WebAssembly type, they *must* match to perform | 2563 | // When converting this to a WebAssembly type, they *must* match to perform |
| ... | @@ -2626,122 +2567,121 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | ... | @@ -2626,122 +2567,121 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 2626 | const result = switch (op) { | 2567 | const result = switch (op) { |
| 2627 | .shr, .shl => result: { | 2568 | .shr, .shl => result: { |
| 2628 | const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse { | 2569 | const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse { |
| 2629 | return func.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)}); | 2570 | return cg.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)}); |
| 2630 | }; | 2571 | }; |
| 2631 | const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?; | 2572 | const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?; |
| 2632 | const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128) | 2573 | const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128) |
| 2633 | try (try func.intcast(rhs, rhs_ty, lhs_ty)).toLocal(func, lhs_ty) | 2574 | try (try cg.intcast(rhs, rhs_ty, lhs_ty)).toLocal(cg, lhs_ty) |
| 2634 | else | 2575 | else |
| 2635 | rhs; | 2576 | rhs; |
| 2636 | break :result try func.binOp(lhs, new_rhs, lhs_ty, op); | 2577 | break :result try cg.binOp(lhs, new_rhs, lhs_ty, op); |
| 2637 | }, | 2578 | }, |
| 2638 | else => try func.binOp(lhs, rhs, lhs_ty, op), | 2579 | else => try cg.binOp(lhs, rhs, lhs_ty, op), |
| 2639 | }; | 2580 | }; |
| 2640 | 2581 | ||
| 2641 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 2582 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 2642 | } | 2583 | } |
| 2643 | 2584 | ||
| 2644 | /// Performs a binary operation on the given `WValue`'s | 2585 | /// Performs a binary operation on the given `WValue`'s |
| 2645 | /// NOTE: THis leaves the value on top of the stack. | 2586 | /// NOTE: THis leaves the value on top of the stack. |
| 2646 | fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 2587 | fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2647 | const pt = func.pt; | 2588 | const pt = cg.pt; |
| 2648 | const zcu = pt.zcu; | 2589 | const zcu = pt.zcu; |
| 2649 | assert(!(lhs != .stack and rhs == .stack)); | 2590 | assert(!(lhs != .stack and rhs == .stack)); |
| 2650 | 2591 | ||
| 2651 | if (ty.isAnyFloat()) { | 2592 | if (ty.isAnyFloat()) { |
| 2652 | const float_op = FloatOp.fromOp(op); | 2593 | const float_op = FloatOp.fromOp(op); |
| 2653 | return func.floatOp(float_op, ty, &.{ lhs, rhs }); | 2594 | return cg.floatOp(float_op, ty, &.{ lhs, rhs }); |
| 2654 | } | 2595 | } |
| 2655 | 2596 | ||
| 2656 | if (isByRef(ty, pt, func.target.*)) { | 2597 | if (isByRef(ty, zcu, cg.target)) { |
| 2657 | if (ty.zigTypeTag(zcu) == .int) { | 2598 | if (ty.zigTypeTag(zcu) == .int) { |
| 2658 | return func.binOpBigInt(lhs, rhs, ty, op); | 2599 | return cg.binOpBigInt(lhs, rhs, ty, op); |
| 2659 | } else { | 2600 | } else { |
| 2660 | return func.fail( | 2601 | return cg.fail( |
| 2661 | "TODO: Implement binary operation for type: {}", | 2602 | "TODO: Implement binary operation for type: {}", |
| 2662 | .{ty.fmt(pt)}, | 2603 | .{ty.fmt(pt)}, |
| 2663 | ); | 2604 | ); |
| 2664 | } | 2605 | } |
| 2665 | } | 2606 | } |
| 2666 | 2607 | ||
| 2667 | const opcode: wasm.Opcode = buildOpcode(.{ | 2608 | const opcode: std.wasm.Opcode = buildOpcode(.{ |
| 2668 | .op = op, | 2609 | .op = op, |
| 2669 | .valtype1 = typeToValtype(ty, pt, func.target.*), | 2610 | .valtype1 = typeToValtype(ty, zcu, cg.target), |
| 2670 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, | 2611 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, |
| 2671 | }); | 2612 | }); |
| 2672 | try func.emitWValue(lhs); | 2613 | try cg.emitWValue(lhs); |
| 2673 | try func.emitWValue(rhs); | 2614 | try cg.emitWValue(rhs); |
| 2674 | 2615 | ||
| 2675 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2616 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2676 | 2617 | ||
| 2677 | return .stack; | 2618 | return .stack; |
| 2678 | } | 2619 | } |
| 2679 | 2620 | ||
| 2680 | fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 2621 | fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2681 | const pt = func.pt; | 2622 | const zcu = cg.pt.zcu; |
| 2682 | const zcu = pt.zcu; | ||
| 2683 | const int_info = ty.intInfo(zcu); | 2623 | const int_info = ty.intInfo(zcu); |
| 2684 | if (int_info.bits > 128) { | 2624 | if (int_info.bits > 128) { |
| 2685 | return func.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{}); | 2625 | return cg.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{}); |
| 2686 | } | 2626 | } |
| 2687 | 2627 | ||
| 2688 | switch (op) { | 2628 | switch (op) { |
| 2689 | .mul => return func.callIntrinsic("__multi3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | 2629 | .mul => return cg.callIntrinsic(.__multi3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2690 | .div => switch (int_info.signedness) { | 2630 | .div => switch (int_info.signedness) { |
| 2691 | .signed => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | 2631 | .signed => return cg.callIntrinsic(.__divti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2692 | .unsigned => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | 2632 | .unsigned => return cg.callIntrinsic(.__udivti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2693 | }, | 2633 | }, |
| 2694 | .rem => switch (int_info.signedness) { | 2634 | .rem => switch (int_info.signedness) { |
| 2695 | .signed => return func.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | 2635 | .signed => return cg.callIntrinsic(.__modti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2696 | .unsigned => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | 2636 | .unsigned => return cg.callIntrinsic(.__umodti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), |
| 2697 | }, | 2637 | }, |
| 2698 | .shr => switch (int_info.signedness) { | 2638 | .shr => switch (int_info.signedness) { |
| 2699 | .signed => return func.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), | 2639 | .signed => return cg.callIntrinsic(.__ashrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2700 | .unsigned => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), | 2640 | .unsigned => return cg.callIntrinsic(.__lshrti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2701 | }, | 2641 | }, |
| 2702 | .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), | 2642 | .shl => return cg.callIntrinsic(.__ashlti3, &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), |
| 2703 | .@"and", .@"or", .xor => { | 2643 | .@"and", .@"or", .xor => { |
| 2704 | const result = try func.allocStack(ty); | 2644 | const result = try cg.allocStack(ty); |
| 2705 | try func.emitWValue(result); | 2645 | try cg.emitWValue(result); |
| 2706 | const lhs_lsb = try func.load(lhs, Type.u64, 0); | 2646 | const lhs_lsb = try cg.load(lhs, Type.u64, 0); |
| 2707 | const rhs_lsb = try func.load(rhs, Type.u64, 0); | 2647 | const rhs_lsb = try cg.load(rhs, Type.u64, 0); |
| 2708 | const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op); | 2648 | const op_lsb = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op); |
| 2709 | try func.store(.stack, op_lsb, Type.u64, result.offset()); | 2649 | try cg.store(.stack, op_lsb, Type.u64, result.offset()); |
| 2710 | 2650 | ||
| 2711 | try func.emitWValue(result); | 2651 | try cg.emitWValue(result); |
| 2712 | const lhs_msb = try func.load(lhs, Type.u64, 8); | 2652 | const lhs_msb = try cg.load(lhs, Type.u64, 8); |
| 2713 | const rhs_msb = try func.load(rhs, Type.u64, 8); | 2653 | const rhs_msb = try cg.load(rhs, Type.u64, 8); |
| 2714 | const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op); | 2654 | const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op); |
| 2715 | try func.store(.stack, op_msb, Type.u64, result.offset() + 8); | 2655 | try cg.store(.stack, op_msb, Type.u64, result.offset() + 8); |
| 2716 | return result; | 2656 | return result; |
| 2717 | }, | 2657 | }, |
| 2718 | .add, .sub => { | 2658 | .add, .sub => { |
| 2719 | const result = try func.allocStack(ty); | 2659 | const result = try cg.allocStack(ty); |
| 2720 | var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); | 2660 | var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64); |
| 2721 | defer lhs_lsb.free(func); | 2661 | defer lhs_lsb.free(cg); |
| 2722 | var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); | 2662 | var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64); |
| 2723 | defer rhs_lsb.free(func); | 2663 | defer rhs_lsb.free(cg); |
| 2724 | var op_lsb = try (try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(func, Type.u64); | 2664 | var op_lsb = try (try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(cg, Type.u64); |
| 2725 | defer op_lsb.free(func); | 2665 | defer op_lsb.free(cg); |
| 2726 | 2666 | ||
| 2727 | const lhs_msb = try func.load(lhs, Type.u64, 8); | 2667 | const lhs_msb = try cg.load(lhs, Type.u64, 8); |
| 2728 | const rhs_msb = try func.load(rhs, Type.u64, 8); | 2668 | const rhs_msb = try cg.load(rhs, Type.u64, 8); |
| 2729 | const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op); | 2669 | const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op); |
| 2730 | 2670 | ||
| 2731 | const lt = if (op == .add) blk: { | 2671 | const lt = if (op == .add) blk: { |
| 2732 | break :blk try func.cmp(op_lsb, rhs_lsb, Type.u64, .lt); | 2672 | break :blk try cg.cmp(op_lsb, rhs_lsb, Type.u64, .lt); |
| 2733 | } else if (op == .sub) blk: { | 2673 | } else if (op == .sub) blk: { |
| 2734 | break :blk try func.cmp(lhs_lsb, rhs_lsb, Type.u64, .lt); | 2674 | break :blk try cg.cmp(lhs_lsb, rhs_lsb, Type.u64, .lt); |
| 2735 | } else unreachable; | 2675 | } else unreachable; |
| 2736 | const tmp = try func.intcast(lt, Type.u32, Type.u64); | 2676 | const tmp = try cg.intcast(lt, Type.u32, Type.u64); |
| 2737 | var tmp_op = try (try func.binOp(op_msb, tmp, Type.u64, op)).toLocal(func, Type.u64); | 2677 | var tmp_op = try (try cg.binOp(op_msb, tmp, Type.u64, op)).toLocal(cg, Type.u64); |
| 2738 | defer tmp_op.free(func); | 2678 | defer tmp_op.free(cg); |
| 2739 | 2679 | ||
| 2740 | try func.store(result, op_lsb, Type.u64, 0); | 2680 | try cg.store(result, op_lsb, Type.u64, 0); |
| 2741 | try func.store(result, tmp_op, Type.u64, 8); | 2681 | try cg.store(result, tmp_op, Type.u64, 8); |
| 2742 | return result; | 2682 | return result; |
| 2743 | }, | 2683 | }, |
| 2744 | else => return func.fail("TODO: Implement binary operation for big integers: '{s}'", .{@tagName(op)}), | 2684 | else => return cg.fail("TODO: Implement binary operation for big integers: '{s}'", .{@tagName(op)}), |
| 2745 | } | 2685 | } |
| 2746 | } | 2686 | } |
| 2747 | 2687 | ||
| ... | @@ -2819,199 +2759,214 @@ const FloatOp = enum { | ... | @@ -2819,199 +2759,214 @@ const FloatOp = enum { |
| 2819 | => null, | 2759 | => null, |
| 2820 | }; | 2760 | }; |
| 2821 | } | 2761 | } |
| 2762 | |||
| 2763 | fn intrinsic(op: FloatOp, bits: u16) Mir.Intrinsic { | ||
| 2764 | return switch (op) { | ||
| 2765 | inline .add, .sub, .div, .mul => |ct_op| switch (bits) { | ||
| 2766 | inline 16, 80, 128 => |ct_bits| @field( | ||
| 2767 | Mir.Intrinsic, | ||
| 2768 | "__" ++ @tagName(ct_op) ++ compilerRtFloatAbbrev(ct_bits) ++ "f3", | ||
| 2769 | ), | ||
| 2770 | else => unreachable, | ||
| 2771 | }, | ||
| 2772 | |||
| 2773 | inline .ceil, | ||
| 2774 | .fabs, | ||
| 2775 | .floor, | ||
| 2776 | .fmax, | ||
| 2777 | .fmin, | ||
| 2778 | .round, | ||
| 2779 | .sqrt, | ||
| 2780 | .trunc, | ||
| 2781 | => |ct_op| switch (bits) { | ||
| 2782 | inline 16, 80, 128 => |ct_bits| @field( | ||
| 2783 | Mir.Intrinsic, | ||
| 2784 | libcFloatPrefix(ct_bits) ++ @tagName(ct_op) ++ libcFloatSuffix(ct_bits), | ||
| 2785 | ), | ||
| 2786 | else => unreachable, | ||
| 2787 | }, | ||
| 2788 | |||
| 2789 | inline .cos, | ||
| 2790 | .exp, | ||
| 2791 | .exp2, | ||
| 2792 | .fma, | ||
| 2793 | .fmod, | ||
| 2794 | .log, | ||
| 2795 | .log10, | ||
| 2796 | .log2, | ||
| 2797 | .sin, | ||
| 2798 | .tan, | ||
| 2799 | => |ct_op| switch (bits) { | ||
| 2800 | inline 16, 32, 64, 80, 128 => |ct_bits| @field( | ||
| 2801 | Mir.Intrinsic, | ||
| 2802 | libcFloatPrefix(ct_bits) ++ @tagName(ct_op) ++ libcFloatSuffix(ct_bits), | ||
| 2803 | ), | ||
| 2804 | else => unreachable, | ||
| 2805 | }, | ||
| 2806 | |||
| 2807 | .neg => unreachable, | ||
| 2808 | }; | ||
| 2809 | } | ||
| 2822 | }; | 2810 | }; |
| 2823 | 2811 | ||
| 2824 | fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2812 | fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2825 | const pt = func.pt; | 2813 | const pt = cg.pt; |
| 2826 | const zcu = pt.zcu; | 2814 | const zcu = pt.zcu; |
| 2827 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 2815 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 2828 | const operand = try func.resolveInst(ty_op.operand); | 2816 | const operand = try cg.resolveInst(ty_op.operand); |
| 2829 | const ty = func.typeOf(ty_op.operand); | 2817 | const ty = cg.typeOf(ty_op.operand); |
| 2830 | const scalar_ty = ty.scalarType(zcu); | 2818 | const scalar_ty = ty.scalarType(zcu); |
| 2831 | 2819 | ||
| 2832 | switch (scalar_ty.zigTypeTag(zcu)) { | 2820 | switch (scalar_ty.zigTypeTag(zcu)) { |
| 2833 | .int => if (ty.zigTypeTag(zcu) == .vector) { | 2821 | .int => if (ty.zigTypeTag(zcu) == .vector) { |
| 2834 | return func.fail("TODO implement airAbs for {}", .{ty.fmt(pt)}); | 2822 | return cg.fail("TODO implement airAbs for {}", .{ty.fmt(pt)}); |
| 2835 | } else { | 2823 | } else { |
| 2836 | const int_bits = ty.intInfo(zcu).bits; | 2824 | const int_bits = ty.intInfo(zcu).bits; |
| 2837 | const wasm_bits = toWasmBits(int_bits) orelse { | 2825 | const wasm_bits = toWasmBits(int_bits) orelse { |
| 2838 | return func.fail("TODO: airAbs for signed integers larger than '{d}' bits", .{int_bits}); | 2826 | return cg.fail("TODO: airAbs for signed integers larger than '{d}' bits", .{int_bits}); |
| 2839 | }; | 2827 | }; |
| 2840 | 2828 | ||
| 2841 | switch (wasm_bits) { | 2829 | switch (wasm_bits) { |
| 2842 | 32 => { | 2830 | 32 => { |
| 2843 | try func.emitWValue(operand); | 2831 | try cg.emitWValue(operand); |
| 2844 | 2832 | ||
| 2845 | try func.addImm32(31); | 2833 | try cg.addImm32(31); |
| 2846 | try func.addTag(.i32_shr_s); | 2834 | try cg.addTag(.i32_shr_s); |
| 2847 | 2835 | ||
| 2848 | var tmp = try func.allocLocal(ty); | 2836 | var tmp = try cg.allocLocal(ty); |
| 2849 | defer tmp.free(func); | 2837 | defer tmp.free(cg); |
| 2850 | try func.addLabel(.local_tee, tmp.local.value); | 2838 | try cg.addLocal(.local_tee, tmp.local.value); |
| 2851 | 2839 | ||
| 2852 | try func.emitWValue(operand); | 2840 | try cg.emitWValue(operand); |
| 2853 | try func.addTag(.i32_xor); | 2841 | try cg.addTag(.i32_xor); |
| 2854 | try func.emitWValue(tmp); | 2842 | try cg.emitWValue(tmp); |
| 2855 | try func.addTag(.i32_sub); | 2843 | try cg.addTag(.i32_sub); |
| 2856 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 2844 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 2857 | }, | 2845 | }, |
| 2858 | 64 => { | 2846 | 64 => { |
| 2859 | try func.emitWValue(operand); | 2847 | try cg.emitWValue(operand); |
| 2860 | 2848 | ||
| 2861 | try func.addImm64(63); | 2849 | try cg.addImm64(63); |
| 2862 | try func.addTag(.i64_shr_s); | 2850 | try cg.addTag(.i64_shr_s); |
| 2863 | 2851 | ||
| 2864 | var tmp = try func.allocLocal(ty); | 2852 | var tmp = try cg.allocLocal(ty); |
| 2865 | defer tmp.free(func); | 2853 | defer tmp.free(cg); |
| 2866 | try func.addLabel(.local_tee, tmp.local.value); | 2854 | try cg.addLocal(.local_tee, tmp.local.value); |
| 2867 | 2855 | ||
| 2868 | try func.emitWValue(operand); | 2856 | try cg.emitWValue(operand); |
| 2869 | try func.addTag(.i64_xor); | 2857 | try cg.addTag(.i64_xor); |
| 2870 | try func.emitWValue(tmp); | 2858 | try cg.emitWValue(tmp); |
| 2871 | try func.addTag(.i64_sub); | 2859 | try cg.addTag(.i64_sub); |
| 2872 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 2860 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 2873 | }, | 2861 | }, |
| 2874 | 128 => { | 2862 | 128 => { |
| 2875 | const mask = try func.allocStack(Type.u128); | 2863 | const mask = try cg.allocStack(Type.u128); |
| 2876 | try func.emitWValue(mask); | 2864 | try cg.emitWValue(mask); |
| 2877 | try func.emitWValue(mask); | 2865 | try cg.emitWValue(mask); |
| 2878 | 2866 | ||
| 2879 | _ = try func.load(operand, Type.u64, 8); | 2867 | _ = try cg.load(operand, Type.u64, 8); |
| 2880 | try func.addImm64(63); | 2868 | try cg.addImm64(63); |
| 2881 | try func.addTag(.i64_shr_s); | 2869 | try cg.addTag(.i64_shr_s); |
| 2882 | 2870 | ||
| 2883 | var tmp = try func.allocLocal(Type.u64); | 2871 | var tmp = try cg.allocLocal(Type.u64); |
| 2884 | defer tmp.free(func); | 2872 | defer tmp.free(cg); |
| 2885 | try func.addLabel(.local_tee, tmp.local.value); | 2873 | try cg.addLocal(.local_tee, tmp.local.value); |
| 2886 | try func.store(.stack, .stack, Type.u64, mask.offset() + 0); | 2874 | try cg.store(.stack, .stack, Type.u64, mask.offset() + 0); |
| 2887 | try func.emitWValue(tmp); | 2875 | try cg.emitWValue(tmp); |
| 2888 | try func.store(.stack, .stack, Type.u64, mask.offset() + 8); | 2876 | try cg.store(.stack, .stack, Type.u64, mask.offset() + 8); |
| 2889 | 2877 | ||
| 2890 | const a = try func.binOpBigInt(operand, mask, Type.u128, .xor); | 2878 | const a = try cg.binOpBigInt(operand, mask, Type.u128, .xor); |
| 2891 | const b = try func.binOpBigInt(a, mask, Type.u128, .sub); | 2879 | const b = try cg.binOpBigInt(a, mask, Type.u128, .sub); |
| 2892 | 2880 | ||
| 2893 | return func.finishAir(inst, b, &.{ty_op.operand}); | 2881 | return cg.finishAir(inst, b, &.{ty_op.operand}); |
| 2894 | }, | 2882 | }, |
| 2895 | else => unreachable, | 2883 | else => unreachable, |
| 2896 | } | 2884 | } |
| 2897 | }, | 2885 | }, |
| 2898 | .float => { | 2886 | .float => { |
| 2899 | const result = try func.floatOp(.fabs, ty, &.{operand}); | 2887 | const result = try cg.floatOp(.fabs, ty, &.{operand}); |
| 2900 | return func.finishAir(inst, result, &.{ty_op.operand}); | 2888 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 2901 | }, | 2889 | }, |
| 2902 | else => unreachable, | 2890 | else => unreachable, |
| 2903 | } | 2891 | } |
| 2904 | } | 2892 | } |
| 2905 | 2893 | ||
| 2906 | fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { | 2894 | fn airUnaryFloatOp(cg: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { |
| 2907 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 2895 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 2908 | const operand = try func.resolveInst(un_op); | 2896 | const operand = try cg.resolveInst(un_op); |
| 2909 | const ty = func.typeOf(un_op); | 2897 | const ty = cg.typeOf(un_op); |
| 2910 | 2898 | ||
| 2911 | const result = try func.floatOp(op, ty, &.{operand}); | 2899 | const result = try cg.floatOp(op, ty, &.{operand}); |
| 2912 | return func.finishAir(inst, result, &.{un_op}); | 2900 | return cg.finishAir(inst, result, &.{un_op}); |
| 2913 | } | 2901 | } |
| 2914 | 2902 | ||
| 2915 | fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue { | 2903 | fn floatOp(cg: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue { |
| 2916 | const pt = func.pt; | 2904 | const zcu = cg.pt.zcu; |
| 2917 | const zcu = pt.zcu; | ||
| 2918 | if (ty.zigTypeTag(zcu) == .vector) { | 2905 | if (ty.zigTypeTag(zcu) == .vector) { |
| 2919 | return func.fail("TODO: Implement floatOps for vectors", .{}); | 2906 | return cg.fail("TODO: Implement floatOps for vectors", .{}); |
| 2920 | } | 2907 | } |
| 2921 | 2908 | ||
| 2922 | const float_bits = ty.floatBits(func.target.*); | 2909 | const float_bits = ty.floatBits(cg.target.*); |
| 2923 | 2910 | ||
| 2924 | if (float_op == .neg) { | 2911 | if (float_op == .neg) { |
| 2925 | return func.floatNeg(ty, args[0]); | 2912 | return cg.floatNeg(ty, args[0]); |
| 2926 | } | 2913 | } |
| 2927 | 2914 | ||
| 2928 | if (float_bits == 32 or float_bits == 64) { | 2915 | if (float_bits == 32 or float_bits == 64) { |
| 2929 | if (float_op.toOp()) |op| { | 2916 | if (float_op.toOp()) |op| { |
| 2930 | for (args) |operand| { | 2917 | for (args) |operand| { |
| 2931 | try func.emitWValue(operand); | 2918 | try cg.emitWValue(operand); |
| 2932 | } | 2919 | } |
| 2933 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target.*) }); | 2920 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, zcu, cg.target) }); |
| 2934 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2921 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2935 | return .stack; | 2922 | return .stack; |
| 2936 | } | 2923 | } |
| 2937 | } | 2924 | } |
| 2938 | 2925 | ||
| 2939 | var fn_name_buf: [64]u8 = undefined; | 2926 | const intrinsic = float_op.intrinsic(float_bits); |
| 2940 | const fn_name = switch (float_op) { | ||
| 2941 | .add, | ||
| 2942 | .sub, | ||
| 2943 | .div, | ||
| 2944 | .mul, | ||
| 2945 | => std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f3", .{ | ||
| 2946 | @tagName(float_op), target_util.compilerRtFloatAbbrev(float_bits), | ||
| 2947 | }) catch unreachable, | ||
| 2948 | |||
| 2949 | .ceil, | ||
| 2950 | .cos, | ||
| 2951 | .exp, | ||
| 2952 | .exp2, | ||
| 2953 | .fabs, | ||
| 2954 | .floor, | ||
| 2955 | .fma, | ||
| 2956 | .fmax, | ||
| 2957 | .fmin, | ||
| 2958 | .fmod, | ||
| 2959 | .log, | ||
| 2960 | .log10, | ||
| 2961 | .log2, | ||
| 2962 | .round, | ||
| 2963 | .sin, | ||
| 2964 | .sqrt, | ||
| 2965 | .tan, | ||
| 2966 | .trunc, | ||
| 2967 | => std.fmt.bufPrint(&fn_name_buf, "{s}{s}{s}", .{ | ||
| 2968 | target_util.libcFloatPrefix(float_bits), @tagName(float_op), target_util.libcFloatSuffix(float_bits), | ||
| 2969 | }) catch unreachable, | ||
| 2970 | .neg => unreachable, // handled above | ||
| 2971 | }; | ||
| 2972 | 2927 | ||
| 2973 | // fma requires three operands | 2928 | // fma requires three operands |
| 2974 | var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index }; | 2929 | var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index }; |
| 2975 | const param_types = param_types_buffer[0..args.len]; | 2930 | const param_types = param_types_buffer[0..args.len]; |
| 2976 | return func.callIntrinsic(fn_name, param_types, ty, args); | 2931 | return cg.callIntrinsic(intrinsic, param_types, ty, args); |
| 2977 | } | 2932 | } |
| 2978 | 2933 | ||
| 2979 | /// NOTE: The result value remains on top of the stack. | 2934 | /// NOTE: The result value remains on top of the stack. |
| 2980 | fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { | 2935 | fn floatNeg(cg: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { |
| 2981 | const float_bits = ty.floatBits(func.target.*); | 2936 | const float_bits = ty.floatBits(cg.target.*); |
| 2982 | switch (float_bits) { | 2937 | switch (float_bits) { |
| 2983 | 16 => { | 2938 | 16 => { |
| 2984 | try func.emitWValue(arg); | 2939 | try cg.emitWValue(arg); |
| 2985 | try func.addImm32(0x8000); | 2940 | try cg.addImm32(0x8000); |
| 2986 | try func.addTag(.i32_xor); | 2941 | try cg.addTag(.i32_xor); |
| 2987 | return .stack; | 2942 | return .stack; |
| 2988 | }, | 2943 | }, |
| 2989 | 32, 64 => { | 2944 | 32, 64 => { |
| 2990 | try func.emitWValue(arg); | 2945 | try cg.emitWValue(arg); |
| 2991 | const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64; | 2946 | const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64; |
| 2992 | const opcode = buildOpcode(.{ .op = .neg, .valtype1 = val_type }); | 2947 | const opcode = buildOpcode(.{ .op = .neg, .valtype1 = val_type }); |
| 2993 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2948 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2994 | return .stack; | 2949 | return .stack; |
| 2995 | }, | 2950 | }, |
| 2996 | 80, 128 => { | 2951 | 80, 128 => { |
| 2997 | const result = try func.allocStack(ty); | 2952 | const result = try cg.allocStack(ty); |
| 2998 | try func.emitWValue(result); | 2953 | try cg.emitWValue(result); |
| 2999 | try func.emitWValue(arg); | 2954 | try cg.emitWValue(arg); |
| 3000 | try func.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 }); | 2955 | try cg.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 }); |
| 3001 | try func.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 }); | 2956 | try cg.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 }); |
| 3002 | 2957 | ||
| 3003 | try func.emitWValue(result); | 2958 | try cg.emitWValue(result); |
| 3004 | try func.emitWValue(arg); | 2959 | try cg.emitWValue(arg); |
| 3005 | try func.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 }); | 2960 | try cg.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 }); |
| 3006 | 2961 | ||
| 3007 | if (float_bits == 80) { | 2962 | if (float_bits == 80) { |
| 3008 | try func.addImm64(0x8000); | 2963 | try cg.addImm64(0x8000); |
| 3009 | try func.addTag(.i64_xor); | 2964 | try cg.addTag(.i64_xor); |
| 3010 | try func.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 }); | 2965 | try cg.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 }); |
| 3011 | } else { | 2966 | } else { |
| 3012 | try func.addImm64(0x8000000000000000); | 2967 | try cg.addImm64(0x8000000000000000); |
| 3013 | try func.addTag(.i64_xor); | 2968 | try cg.addTag(.i64_xor); |
| 3014 | try func.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 }); | 2969 | try cg.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 }); |
| 3015 | } | 2970 | } |
| 3016 | return result; | 2971 | return result; |
| 3017 | }, | 2972 | }, |
| ... | @@ -3019,18 +2974,17 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { | ... | @@ -3019,18 +2974,17 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { |
| 3019 | } | 2974 | } |
| 3020 | } | 2975 | } |
| 3021 | 2976 | ||
| 3022 | fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 2977 | fn airWrapBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 3023 | const pt = func.pt; | 2978 | const zcu = cg.pt.zcu; |
| 3024 | const zcu = pt.zcu; | 2979 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3025 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 3026 | 2980 | ||
| 3027 | const lhs = try func.resolveInst(bin_op.lhs); | 2981 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 3028 | const rhs = try func.resolveInst(bin_op.rhs); | 2982 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 3029 | const lhs_ty = func.typeOf(bin_op.lhs); | 2983 | const lhs_ty = cg.typeOf(bin_op.lhs); |
| 3030 | const rhs_ty = func.typeOf(bin_op.rhs); | 2984 | const rhs_ty = cg.typeOf(bin_op.rhs); |
| 3031 | 2985 | ||
| 3032 | if (lhs_ty.zigTypeTag(zcu) == .vector or rhs_ty.zigTypeTag(zcu) == .vector) { | 2986 | if (lhs_ty.zigTypeTag(zcu) == .vector or rhs_ty.zigTypeTag(zcu) == .vector) { |
| 3033 | return func.fail("TODO: Implement wrapping arithmetic for vectors", .{}); | 2987 | return cg.fail("TODO: Implement wrapping arithmetic for vectors", .{}); |
| 3034 | } | 2988 | } |
| 3035 | 2989 | ||
| 3036 | // For certain operations, such as shifting, the types are different. | 2990 | // For certain operations, such as shifting, the types are different. |
| ... | @@ -3041,90 +2995,89 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | ... | @@ -3041,90 +2995,89 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 3041 | const result = switch (op) { | 2995 | const result = switch (op) { |
| 3042 | .shr, .shl => result: { | 2996 | .shr, .shl => result: { |
| 3043 | const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse { | 2997 | const lhs_wasm_bits = toWasmBits(@intCast(lhs_ty.bitSize(zcu))) orelse { |
| 3044 | return func.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)}); | 2998 | return cg.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)}); |
| 3045 | }; | 2999 | }; |
| 3046 | const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?; | 3000 | const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?; |
| 3047 | const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128) | 3001 | const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128) |
| 3048 | try (try func.intcast(rhs, rhs_ty, lhs_ty)).toLocal(func, lhs_ty) | 3002 | try (try cg.intcast(rhs, rhs_ty, lhs_ty)).toLocal(cg, lhs_ty) |
| 3049 | else | 3003 | else |
| 3050 | rhs; | 3004 | rhs; |
| 3051 | break :result try func.wrapBinOp(lhs, new_rhs, lhs_ty, op); | 3005 | break :result try cg.wrapBinOp(lhs, new_rhs, lhs_ty, op); |
| 3052 | }, | 3006 | }, |
| 3053 | else => try func.wrapBinOp(lhs, rhs, lhs_ty, op), | 3007 | else => try cg.wrapBinOp(lhs, rhs, lhs_ty, op), |
| 3054 | }; | 3008 | }; |
| 3055 | 3009 | ||
| 3056 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 3010 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 3057 | } | 3011 | } |
| 3058 | 3012 | ||
| 3059 | /// Performs a wrapping binary operation. | 3013 | /// Performs a wrapping binary operation. |
| 3060 | /// Asserts rhs is not a stack value when lhs also isn't. | 3014 | /// Asserts rhs is not a stack value when lhs also isn't. |
| 3061 | /// NOTE: Leaves the result on the stack when its Type is <= 64 bits | 3015 | /// NOTE: Leaves the result on the stack when its Type is <= 64 bits |
| 3062 | fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 3016 | fn wrapBinOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 3063 | const bin_local = try func.binOp(lhs, rhs, ty, op); | 3017 | const bin_local = try cg.binOp(lhs, rhs, ty, op); |
| 3064 | return func.wrapOperand(bin_local, ty); | 3018 | return cg.wrapOperand(bin_local, ty); |
| 3065 | } | 3019 | } |
| 3066 | 3020 | ||
| 3067 | /// Wraps an operand based on a given type's bitsize. | 3021 | /// Wraps an operand based on a given type's bitsize. |
| 3068 | /// Asserts `Type` is <= 128 bits. | 3022 | /// Asserts `Type` is <= 128 bits. |
| 3069 | /// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack, if wrapping was needed. | 3023 | /// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack, if wrapping was needed. |
| 3070 | fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { | 3024 | fn wrapOperand(cg: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 3071 | const pt = func.pt; | 3025 | const zcu = cg.pt.zcu; |
| 3072 | const zcu = pt.zcu; | ||
| 3073 | assert(ty.abiSize(zcu) <= 16); | 3026 | assert(ty.abiSize(zcu) <= 16); |
| 3074 | const int_bits: u16 = @intCast(ty.bitSize(zcu)); // TODO use ty.intInfo(zcu).bits | 3027 | const int_bits: u16 = @intCast(ty.bitSize(zcu)); // TODO use ty.intInfo(zcu).bits |
| 3075 | const wasm_bits = toWasmBits(int_bits) orelse { | 3028 | const wasm_bits = toWasmBits(int_bits) orelse { |
| 3076 | return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{int_bits}); | 3029 | return cg.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{int_bits}); |
| 3077 | }; | 3030 | }; |
| 3078 | 3031 | ||
| 3079 | if (wasm_bits == int_bits) return operand; | 3032 | if (wasm_bits == int_bits) return operand; |
| 3080 | 3033 | ||
| 3081 | switch (wasm_bits) { | 3034 | switch (wasm_bits) { |
| 3082 | 32 => { | 3035 | 32 => { |
| 3083 | try func.emitWValue(operand); | 3036 | try cg.emitWValue(operand); |
| 3084 | if (ty.isSignedInt(zcu)) { | 3037 | if (ty.isSignedInt(zcu)) { |
| 3085 | try func.addImm32(32 - int_bits); | 3038 | try cg.addImm32(32 - int_bits); |
| 3086 | try func.addTag(.i32_shl); | 3039 | try cg.addTag(.i32_shl); |
| 3087 | try func.addImm32(32 - int_bits); | 3040 | try cg.addImm32(32 - int_bits); |
| 3088 | try func.addTag(.i32_shr_s); | 3041 | try cg.addTag(.i32_shr_s); |
| 3089 | } else { | 3042 | } else { |
| 3090 | try func.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits)); | 3043 | try cg.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits)); |
| 3091 | try func.addTag(.i32_and); | 3044 | try cg.addTag(.i32_and); |
| 3092 | } | 3045 | } |
| 3093 | return .stack; | 3046 | return .stack; |
| 3094 | }, | 3047 | }, |
| 3095 | 64 => { | 3048 | 64 => { |
| 3096 | try func.emitWValue(operand); | 3049 | try cg.emitWValue(operand); |
| 3097 | if (ty.isSignedInt(zcu)) { | 3050 | if (ty.isSignedInt(zcu)) { |
| 3098 | try func.addImm64(64 - int_bits); | 3051 | try cg.addImm64(64 - int_bits); |
| 3099 | try func.addTag(.i64_shl); | 3052 | try cg.addTag(.i64_shl); |
| 3100 | try func.addImm64(64 - int_bits); | 3053 | try cg.addImm64(64 - int_bits); |
| 3101 | try func.addTag(.i64_shr_s); | 3054 | try cg.addTag(.i64_shr_s); |
| 3102 | } else { | 3055 | } else { |
| 3103 | try func.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits)); | 3056 | try cg.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits)); |
| 3104 | try func.addTag(.i64_and); | 3057 | try cg.addTag(.i64_and); |
| 3105 | } | 3058 | } |
| 3106 | return .stack; | 3059 | return .stack; |
| 3107 | }, | 3060 | }, |
| 3108 | 128 => { | 3061 | 128 => { |
| 3109 | assert(operand != .stack); | 3062 | assert(operand != .stack); |
| 3110 | const result = try func.allocStack(ty); | 3063 | const result = try cg.allocStack(ty); |
| 3111 | 3064 | ||
| 3112 | try func.emitWValue(result); | 3065 | try cg.emitWValue(result); |
| 3113 | _ = try func.load(operand, Type.u64, 0); | 3066 | _ = try cg.load(operand, Type.u64, 0); |
| 3114 | try func.store(.stack, .stack, Type.u64, result.offset()); | 3067 | try cg.store(.stack, .stack, Type.u64, result.offset()); |
| 3115 | 3068 | ||
| 3116 | try func.emitWValue(result); | 3069 | try cg.emitWValue(result); |
| 3117 | _ = try func.load(operand, Type.u64, 8); | 3070 | _ = try cg.load(operand, Type.u64, 8); |
| 3118 | if (ty.isSignedInt(zcu)) { | 3071 | if (ty.isSignedInt(zcu)) { |
| 3119 | try func.addImm64(128 - int_bits); | 3072 | try cg.addImm64(128 - int_bits); |
| 3120 | try func.addTag(.i64_shl); | 3073 | try cg.addTag(.i64_shl); |
| 3121 | try func.addImm64(128 - int_bits); | 3074 | try cg.addImm64(128 - int_bits); |
| 3122 | try func.addTag(.i64_shr_s); | 3075 | try cg.addTag(.i64_shr_s); |
| 3123 | } else { | 3076 | } else { |
| 3124 | try func.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits)); | 3077 | try cg.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits)); |
| 3125 | try func.addTag(.i64_and); | 3078 | try cg.addTag(.i64_and); |
| 3126 | } | 3079 | } |
| 3127 | try func.store(.stack, .stack, Type.u64, result.offset() + 8); | 3080 | try cg.store(.stack, .stack, Type.u64, result.offset() + 8); |
| 3128 | 3081 | ||
| 3129 | return result; | 3082 | return result; |
| 3130 | }, | 3083 | }, |
| ... | @@ -3132,17 +3085,17 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { | ... | @@ -3132,17 +3085,17 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 3132 | } | 3085 | } |
| 3133 | } | 3086 | } |
| 3134 | 3087 | ||
| 3135 | fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue { | 3088 | fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue { |
| 3136 | const pt = func.pt; | 3089 | const pt = cg.pt; |
| 3137 | const zcu = pt.zcu; | 3090 | const zcu = pt.zcu; |
| 3138 | const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; | 3091 | const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; |
| 3139 | const offset: u64 = prev_offset + ptr.byte_offset; | 3092 | const offset: u64 = prev_offset + ptr.byte_offset; |
| 3140 | return switch (ptr.base_addr) { | 3093 | return switch (ptr.base_addr) { |
| 3141 | .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)), | 3094 | .nav => |nav| return .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } }, |
| 3142 | .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)), | 3095 | .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset), .orig_ptr_ty = uav.orig_ty } }, |
| 3143 | .int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize), | 3096 | .int => return cg.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize), |
| 3144 | .eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}), | 3097 | .eu_payload => return cg.fail("Wasm TODO: lower error union payload pointer", .{}), |
| 3145 | .opt_payload => |opt_ptr| return func.lowerPtr(opt_ptr, offset), | 3098 | .opt_payload => |opt_ptr| return cg.lowerPtr(opt_ptr, offset), |
| 3146 | .field => |field| { | 3099 | .field => |field| { |
| 3147 | const base_ptr = Value.fromInterned(field.base); | 3100 | const base_ptr = Value.fromInterned(field.base); |
| 3148 | const base_ty = base_ptr.typeOf(zcu).childType(zcu); | 3101 | const base_ty = base_ptr.typeOf(zcu).childType(zcu); |
| ... | @@ -3151,7 +3104,7 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr | ... | @@ -3151,7 +3104,7 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr |
| 3151 | assert(base_ty.isSlice(zcu)); | 3104 | assert(base_ty.isSlice(zcu)); |
| 3152 | break :off switch (field.index) { | 3105 | break :off switch (field.index) { |
| 3153 | Value.slice_ptr_index => 0, | 3106 | Value.slice_ptr_index => 0, |
| 3154 | Value.slice_len_index => @divExact(func.target.ptrBitWidth(), 8), | 3107 | Value.slice_len_index => @divExact(cg.target.ptrBitWidth(), 8), |
| 3155 | else => unreachable, | 3108 | else => unreachable, |
| 3156 | }; | 3109 | }; |
| 3157 | }, | 3110 | }, |
| ... | @@ -3177,70 +3130,19 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr | ... | @@ -3177,70 +3130,19 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr |
| 3177 | }, | 3130 | }, |
| 3178 | else => unreachable, | 3131 | else => unreachable, |
| 3179 | }; | 3132 | }; |
| 3180 | return func.lowerPtr(field.base, offset + field_off); | 3133 | return cg.lowerPtr(field.base, offset + field_off); |
| 3181 | }, | 3134 | }, |
| 3182 | .arr_elem, .comptime_field, .comptime_alloc => unreachable, | 3135 | .arr_elem, .comptime_field, .comptime_alloc => unreachable, |
| 3183 | }; | 3136 | }; |
| 3184 | } | 3137 | } |
| 3185 | 3138 | ||
| 3186 | fn lowerUavRef( | ||
| 3187 | func: *CodeGen, | ||
| 3188 | uav: InternPool.Key.Ptr.BaseAddr.Uav, | ||
| 3189 | offset: u32, | ||
| 3190 | ) InnerError!WValue { | ||
| 3191 | const pt = func.pt; | ||
| 3192 | const zcu = pt.zcu; | ||
| 3193 | const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav.val)); | ||
| 3194 | |||
| 3195 | const is_fn_body = ty.zigTypeTag(zcu) == .@"fn"; | ||
| 3196 | if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(zcu)) { | ||
| 3197 | return .{ .imm32 = 0xaaaaaaaa }; | ||
| 3198 | } | ||
| 3199 | |||
| 3200 | const decl_align = zcu.intern_pool.indexToKey(uav.orig_ty).ptr_type.flags.alignment; | ||
| 3201 | const res = try func.bin_file.lowerUav(pt, uav.val, decl_align, func.src_loc); | ||
| 3202 | const target_sym_index = switch (res) { | ||
| 3203 | .mcv => |mcv| mcv.load_symbol, | ||
| 3204 | .fail => |err_msg| { | ||
| 3205 | func.err_msg = err_msg; | ||
| 3206 | return error.CodegenFail; | ||
| 3207 | }, | ||
| 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 | } | ||
| 3215 | |||
| 3216 | fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue { | ||
| 3217 | const pt = func.pt; | ||
| 3218 | const zcu = pt.zcu; | ||
| 3219 | const ip = &zcu.intern_pool; | ||
| 3220 | |||
| 3221 | const nav_ty = ip.getNav(nav_index).typeOf(ip); | ||
| 3222 | if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) { | ||
| 3223 | return .{ .imm32 = 0xaaaaaaaa }; | ||
| 3224 | } | ||
| 3225 | |||
| 3226 | const atom_index = try func.bin_file.getOrCreateAtomForNav(pt, nav_index); | ||
| 3227 | const atom = func.bin_file.getAtom(atom_index); | ||
| 3228 | |||
| 3229 | const target_sym_index = @intFromEnum(atom.sym_index); | ||
| 3230 | if (ip.isFunctionType(nav_ty)) { | ||
| 3231 | return .{ .function_index = target_sym_index }; | ||
| 3232 | } else if (offset == 0) { | ||
| 3233 | return .{ .memory = target_sym_index }; | ||
| 3234 | } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } }; | ||
| 3235 | } | ||
| 3236 | |||
| 3237 | /// Asserts that `isByRef` returns `false` for `ty`. | 3139 | /// Asserts that `isByRef` returns `false` for `ty`. |
| 3238 | fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | 3140 | fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3239 | const pt = func.pt; | 3141 | const pt = cg.pt; |
| 3240 | const zcu = pt.zcu; | 3142 | const zcu = pt.zcu; |
| 3241 | assert(!isByRef(ty, pt, func.target.*)); | 3143 | assert(!isByRef(ty, zcu, cg.target)); |
| 3242 | const ip = &zcu.intern_pool; | 3144 | const ip = &zcu.intern_pool; |
| 3243 | if (val.isUndefDeep(zcu)) return func.emitUndefined(ty); | 3145 | if (val.isUndefDeep(zcu)) return cg.emitUndefined(ty); |
| 3244 | 3146 | ||
| 3245 | switch (ip.indexToKey(val.ip_index)) { | 3147 | switch (ip.indexToKey(val.ip_index)) { |
| 3246 | .int_type, | 3148 | .int_type, |
| ... | @@ -3319,14 +3221,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3319,14 +3221,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3319 | const payload_type = ty.errorUnionPayload(zcu); | 3221 | const payload_type = ty.errorUnionPayload(zcu); |
| 3320 | if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { | 3222 | if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 3321 | // We use the error type directly as the type. | 3223 | // We use the error type directly as the type. |
| 3322 | return func.lowerConstant(err_val, err_ty); | 3224 | return cg.lowerConstant(err_val, err_ty); |
| 3323 | } | 3225 | } |
| 3324 | 3226 | ||
| 3325 | return func.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{}); | 3227 | return cg.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{}); |
| 3326 | }, | 3228 | }, |
| 3327 | .enum_tag => |enum_tag| { | 3229 | .enum_tag => |enum_tag| { |
| 3328 | const int_tag_ty = ip.typeOf(enum_tag.int); | 3230 | const int_tag_ty = ip.typeOf(enum_tag.int); |
| 3329 | return func.lowerConstant(Value.fromInterned(enum_tag.int), Type.fromInterned(int_tag_ty)); | 3231 | return cg.lowerConstant(Value.fromInterned(enum_tag.int), Type.fromInterned(int_tag_ty)); |
| 3330 | }, | 3232 | }, |
| 3331 | .float => |float| switch (float.storage) { | 3233 | .float => |float| switch (float.storage) { |
| 3332 | .f16 => |f16_val| return .{ .imm32 = @as(u16, @bitCast(f16_val)) }, | 3234 | .f16 => |f16_val| return .{ .imm32 = @as(u16, @bitCast(f16_val)) }, |
| ... | @@ -3334,18 +3236,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3334,18 +3236,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3334 | .f64 => |f64_val| return .{ .float64 = f64_val }, | 3236 | .f64 => |f64_val| return .{ .float64 = f64_val }, |
| 3335 | else => unreachable, | 3237 | else => unreachable, |
| 3336 | }, | 3238 | }, |
| 3337 | .slice => switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) { | 3239 | .slice => unreachable, // isByRef == true |
| 3338 | .mcv => |mcv| return .{ .memory = mcv.load_symbol }, | 3240 | .ptr => return cg.lowerPtr(val.toIntern(), 0), |
| 3339 | .fail => |err_msg| { | ||
| 3340 | func.err_msg = err_msg; | ||
| 3341 | return error.CodegenFail; | ||
| 3342 | }, | ||
| 3343 | }, | ||
| 3344 | .ptr => return func.lowerPtr(val.toIntern(), 0), | ||
| 3345 | .opt => if (ty.optionalReprIsPayload(zcu)) { | 3241 | .opt => if (ty.optionalReprIsPayload(zcu)) { |
| 3346 | const pl_ty = ty.optionalChild(zcu); | 3242 | const pl_ty = ty.optionalChild(zcu); |
| 3347 | if (val.optionalValue(zcu)) |payload| { | 3243 | if (val.optionalValue(zcu)) |payload| { |
| 3348 | return func.lowerConstant(payload, pl_ty); | 3244 | return cg.lowerConstant(payload, pl_ty); |
| 3349 | } else { | 3245 | } else { |
| 3350 | return .{ .imm32 = 0 }; | 3246 | return .{ .imm32 = 0 }; |
| 3351 | } | 3247 | } |
| ... | @@ -3353,12 +3249,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3353,12 +3249,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3353 | return .{ .imm32 = @intFromBool(!val.isNull(zcu)) }; | 3249 | return .{ .imm32 = @intFromBool(!val.isNull(zcu)) }; |
| 3354 | }, | 3250 | }, |
| 3355 | .aggregate => switch (ip.indexToKey(ty.ip_index)) { | 3251 | .aggregate => switch (ip.indexToKey(ty.ip_index)) { |
| 3356 | .array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}), | 3252 | .array_type => return cg.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}), |
| 3357 | .vector_type => { | 3253 | .vector_type => { |
| 3358 | assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct); | 3254 | assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct); |
| 3359 | var buf: [16]u8 = undefined; | 3255 | var buf: [16]u8 = undefined; |
| 3360 | val.writeToMemory(pt, &buf) catch unreachable; | 3256 | val.writeToMemory(pt, &buf) catch unreachable; |
| 3361 | return func.storeSimdImmd(buf); | 3257 | return cg.storeSimdImmd(buf); |
| 3362 | }, | 3258 | }, |
| 3363 | .struct_type => { | 3259 | .struct_type => { |
| 3364 | const struct_type = ip.loadStructType(ty.toIntern()); | 3260 | const struct_type = ip.loadStructType(ty.toIntern()); |
| ... | @@ -3372,7 +3268,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3372,7 +3268,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3372 | backing_int_ty, | 3268 | backing_int_ty, |
| 3373 | mem.readInt(u64, &buf, .little), | 3269 | mem.readInt(u64, &buf, .little), |
| 3374 | ); | 3270 | ); |
| 3375 | return func.lowerConstant(int_val, backing_int_ty); | 3271 | return cg.lowerConstant(int_val, backing_int_ty); |
| 3376 | }, | 3272 | }, |
| 3377 | else => unreachable, | 3273 | else => unreachable, |
| 3378 | }, | 3274 | }, |
| ... | @@ -3385,7 +3281,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3385,7 +3281,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3385 | const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; | 3281 | const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; |
| 3386 | break :field_ty Type.fromInterned(union_obj.field_types.get(ip)[field_index]); | 3282 | break :field_ty Type.fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 3387 | }; | 3283 | }; |
| 3388 | return func.lowerConstant(Value.fromInterned(un.val), constant_ty); | 3284 | return cg.lowerConstant(Value.fromInterned(un.val), constant_ty); |
| 3389 | }, | 3285 | }, |
| 3390 | .memoized_call => unreachable, | 3286 | .memoized_call => unreachable, |
| 3391 | } | 3287 | } |
| ... | @@ -3393,15 +3289,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3393,15 +3289,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3393 | 3289 | ||
| 3394 | /// Stores the value as a 128bit-immediate value by storing it inside | 3290 | /// Stores the value as a 128bit-immediate value by storing it inside |
| 3395 | /// the list and returning the index into this list as `WValue`. | 3291 | /// the list and returning the index into this list as `WValue`. |
| 3396 | fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue { | 3292 | fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue { |
| 3397 | const index = @as(u32, @intCast(func.simd_immediates.items.len)); | 3293 | const index = @as(u32, @intCast(cg.simd_immediates.items.len)); |
| 3398 | try func.simd_immediates.append(func.gpa, value); | 3294 | try cg.simd_immediates.append(cg.gpa, value); |
| 3399 | return .{ .imm128 = index }; | 3295 | return .{ .imm128 = index }; |
| 3400 | } | 3296 | } |
| 3401 | 3297 | ||
| 3402 | fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { | 3298 | fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 3403 | const pt = func.pt; | 3299 | const zcu = cg.pt.zcu; |
| 3404 | const zcu = pt.zcu; | ||
| 3405 | const ip = &zcu.intern_pool; | 3300 | const ip = &zcu.intern_pool; |
| 3406 | switch (ty.zigTypeTag(zcu)) { | 3301 | switch (ty.zigTypeTag(zcu)) { |
| 3407 | .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa }, | 3302 | .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa }, |
| ... | @@ -3410,21 +3305,20 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -3410,21 +3305,20 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { |
| 3410 | 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, | 3305 | 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, |
| 3411 | else => unreachable, | 3306 | else => unreachable, |
| 3412 | }, | 3307 | }, |
| 3413 | .float => switch (ty.floatBits(func.target.*)) { | 3308 | .float => switch (ty.floatBits(cg.target.*)) { |
| 3414 | 16 => return .{ .imm32 = 0xaaaaaaaa }, | 3309 | 16 => return .{ .imm32 = 0xaaaaaaaa }, |
| 3415 | 32 => return .{ .float32 = @as(f32, @bitCast(@as(u32, 0xaaaaaaaa))) }, | 3310 | 32 => return .{ .float32 = @as(f32, @bitCast(@as(u32, 0xaaaaaaaa))) }, |
| 3416 | 64 => return .{ .float64 = @as(f64, @bitCast(@as(u64, 0xaaaaaaaaaaaaaaaa))) }, | 3311 | 64 => return .{ .float64 = @as(f64, @bitCast(@as(u64, 0xaaaaaaaaaaaaaaaa))) }, |
| 3417 | else => unreachable, | 3312 | else => unreachable, |
| 3418 | }, | 3313 | }, |
| 3419 | .pointer => switch (func.arch()) { | 3314 | .pointer => switch (cg.ptr_size) { |
| 3420 | .wasm32 => return .{ .imm32 = 0xaaaaaaaa }, | 3315 | .wasm32 => return .{ .imm32 = 0xaaaaaaaa }, |
| 3421 | .wasm64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, | 3316 | .wasm64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, |
| 3422 | else => unreachable, | ||
| 3423 | }, | 3317 | }, |
| 3424 | .optional => { | 3318 | .optional => { |
| 3425 | const pl_ty = ty.optionalChild(zcu); | 3319 | const pl_ty = ty.optionalChild(zcu); |
| 3426 | if (ty.optionalReprIsPayload(zcu)) { | 3320 | if (ty.optionalReprIsPayload(zcu)) { |
| 3427 | return func.emitUndefined(pl_ty); | 3321 | return cg.emitUndefined(pl_ty); |
| 3428 | } | 3322 | } |
| 3429 | return .{ .imm32 = 0xaaaaaaaa }; | 3323 | return .{ .imm32 = 0xaaaaaaaa }; |
| 3430 | }, | 3324 | }, |
| ... | @@ -3433,26 +3327,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -3433,26 +3327,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { |
| 3433 | }, | 3327 | }, |
| 3434 | .@"struct" => { | 3328 | .@"struct" => { |
| 3435 | const packed_struct = zcu.typeToPackedStruct(ty).?; | 3329 | const packed_struct = zcu.typeToPackedStruct(ty).?; |
| 3436 | return func.emitUndefined(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip))); | 3330 | return cg.emitUndefined(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip))); |
| 3437 | }, | 3331 | }, |
| 3438 | else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag(zcu)}), | 3332 | else => return cg.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag(zcu)}), |
| 3439 | } | 3333 | } |
| 3440 | } | 3334 | } |
| 3441 | 3335 | ||
| 3442 | /// Returns a `Value` as a signed 32 bit value. | 3336 | /// Returns a `Value` as a signed 32 bit value. |
| 3443 | /// It's illegal to provide a value with a type that cannot be represented | 3337 | /// It's illegal to provide a value with a type that cannot be represented |
| 3444 | /// as an integer value. | 3338 | /// as an integer value. |
| 3445 | fn valueAsI32(func: *const CodeGen, val: Value) i32 { | 3339 | fn valueAsI32(cg: *const CodeGen, val: Value) i32 { |
| 3446 | const pt = func.pt; | 3340 | const zcu = cg.pt.zcu; |
| 3447 | const zcu = pt.zcu; | ||
| 3448 | const ip = &zcu.intern_pool; | 3341 | const ip = &zcu.intern_pool; |
| 3449 | 3342 | ||
| 3450 | switch (val.toIntern()) { | 3343 | switch (val.toIntern()) { |
| 3451 | .bool_true => return 1, | 3344 | .bool_true => return 1, |
| 3452 | .bool_false => return 0, | 3345 | .bool_false => return 0, |
| 3453 | else => return switch (ip.indexToKey(val.ip_index)) { | 3346 | else => return switch (ip.indexToKey(val.ip_index)) { |
| 3454 | .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, pt), | 3347 | .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, zcu), |
| 3455 | .int => |int| intStorageAsI32(int.storage, pt), | 3348 | .int => |int| intStorageAsI32(int.storage, zcu), |
| 3456 | .ptr => |ptr| { | 3349 | .ptr => |ptr| { |
| 3457 | assert(ptr.base_addr == .int); | 3350 | assert(ptr.base_addr == .int); |
| 3458 | return @intCast(ptr.byte_offset); | 3351 | return @intCast(ptr.byte_offset); |
| ... | @@ -3463,12 +3356,11 @@ fn valueAsI32(func: *const CodeGen, val: Value) i32 { | ... | @@ -3463,12 +3356,11 @@ fn valueAsI32(func: *const CodeGen, val: Value) i32 { |
| 3463 | } | 3356 | } |
| 3464 | } | 3357 | } |
| 3465 | 3358 | ||
| 3466 | fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, pt: Zcu.PerThread) i32 { | 3359 | fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, zcu: *const Zcu) i32 { |
| 3467 | return intStorageAsI32(ip.indexToKey(int).int.storage, pt); | 3360 | return intStorageAsI32(ip.indexToKey(int).int.storage, zcu); |
| 3468 | } | 3361 | } |
| 3469 | 3362 | ||
| 3470 | fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 { | 3363 | fn intStorageAsI32(storage: InternPool.Key.Int.Storage, zcu: *const Zcu) i32 { |
| 3471 | const zcu = pt.zcu; | ||
| 3472 | return switch (storage) { | 3364 | return switch (storage) { |
| 3473 | .i64 => |x| @as(i32, @intCast(x)), | 3365 | .i64 => |x| @as(i32, @intCast(x)), |
| 3474 | .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))), | 3366 | .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))), |
| ... | @@ -3478,145 +3370,144 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 { | ... | @@ -3478,145 +3370,144 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, pt: Zcu.PerThread) i32 { |
| 3478 | }; | 3370 | }; |
| 3479 | } | 3371 | } |
| 3480 | 3372 | ||
| 3481 | fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3373 | fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3482 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3374 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3483 | const extra = func.air.extraData(Air.Block, ty_pl.payload); | 3375 | const extra = cg.air.extraData(Air.Block, ty_pl.payload); |
| 3484 | try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); | 3376 | try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len])); |
| 3485 | } | 3377 | } |
| 3486 | 3378 | ||
| 3487 | fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { | 3379 | fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { |
| 3488 | const pt = func.pt; | 3380 | const zcu = cg.pt.zcu; |
| 3489 | const wasm_block_ty = genBlockType(block_ty, pt, func.target.*); | 3381 | const wasm_block_ty = genBlockType(block_ty, zcu, cg.target); |
| 3490 | 3382 | ||
| 3491 | // if wasm_block_ty is non-empty, we create a register to store the temporary value | 3383 | // 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: { | 3384 | const block_result: WValue = if (wasm_block_ty != .empty) blk: { |
| 3493 | const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty; | 3385 | const ty: Type = if (isByRef(block_ty, zcu, cg.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 overwritten | 3386 | break :blk try cg.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten |
| 3495 | } else .none; | 3387 | } else .none; |
| 3496 | 3388 | ||
| 3497 | try func.startBlock(.block, wasm.block_empty); | 3389 | try cg.startBlock(.block, .empty); |
| 3498 | // Here we set the current block idx, so breaks know the depth to jump | 3390 | // Here we set the current block idx, so breaks know the depth to jump |
| 3499 | // to when breaking out. | 3391 | // to when breaking out. |
| 3500 | try func.blocks.putNoClobber(func.gpa, inst, .{ | 3392 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ |
| 3501 | .label = func.block_depth, | 3393 | .label = cg.block_depth, |
| 3502 | .value = block_result, | 3394 | .value = block_result, |
| 3503 | }); | 3395 | }); |
| 3504 | 3396 | ||
| 3505 | try func.genBody(body); | 3397 | try cg.genBody(body); |
| 3506 | try func.endBlock(); | 3398 | try cg.endBlock(); |
| 3507 | 3399 | ||
| 3508 | const liveness = func.liveness.getBlock(inst); | 3400 | const liveness = cg.liveness.getBlock(inst); |
| 3509 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths.len); | 3401 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths.len); |
| 3510 | 3402 | ||
| 3511 | return func.finishAir(inst, block_result, &.{}); | 3403 | return cg.finishAir(inst, block_result, &.{}); |
| 3512 | } | 3404 | } |
| 3513 | 3405 | ||
| 3514 | /// appends a new wasm block to the code section and increases the `block_depth` by 1 | 3406 | /// appends a new wasm block to the code section and increases the `block_depth` by 1 |
| 3515 | fn startBlock(func: *CodeGen, block_tag: wasm.Opcode, valtype: u8) !void { | 3407 | fn startBlock(cg: *CodeGen, block_tag: std.wasm.Opcode, block_type: std.wasm.BlockType) !void { |
| 3516 | func.block_depth += 1; | 3408 | cg.block_depth += 1; |
| 3517 | try func.addInst(.{ | 3409 | try cg.addInst(.{ |
| 3518 | .tag = Mir.Inst.Tag.fromOpcode(block_tag), | 3410 | .tag = Mir.Inst.Tag.fromOpcode(block_tag), |
| 3519 | .data = .{ .block_type = valtype }, | 3411 | .data = .{ .block_type = block_type }, |
| 3520 | }); | 3412 | }); |
| 3521 | } | 3413 | } |
| 3522 | 3414 | ||
| 3523 | /// Ends the current wasm block and decreases the `block_depth` by 1 | 3415 | /// Ends the current wasm block and decreases the `block_depth` by 1 |
| 3524 | fn endBlock(func: *CodeGen) !void { | 3416 | fn endBlock(cg: *CodeGen) !void { |
| 3525 | try func.addTag(.end); | 3417 | try cg.addTag(.end); |
| 3526 | func.block_depth -= 1; | 3418 | cg.block_depth -= 1; |
| 3527 | } | 3419 | } |
| 3528 | 3420 | ||
| 3529 | fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3421 | fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3530 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3422 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3531 | const loop = func.air.extraData(Air.Block, ty_pl.payload); | 3423 | const loop = cg.air.extraData(Air.Block, ty_pl.payload); |
| 3532 | const body: []const Air.Inst.Index = @ptrCast(func.air.extra[loop.end..][0..loop.data.body_len]); | 3424 | const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[loop.end..][0..loop.data.body_len]); |
| 3533 | 3425 | ||
| 3534 | // result type of loop is always 'noreturn', meaning we can always | 3426 | // result type of loop is always 'noreturn', meaning we can always |
| 3535 | // emit the wasm type 'block_empty'. | 3427 | // emit the wasm type 'block_empty'. |
| 3536 | try func.startBlock(.loop, wasm.block_empty); | 3428 | try cg.startBlock(.loop, .empty); |
| 3537 | 3429 | ||
| 3538 | try func.loops.putNoClobber(func.gpa, inst, func.block_depth); | 3430 | try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth); |
| 3539 | defer assert(func.loops.remove(inst)); | 3431 | defer assert(cg.loops.remove(inst)); |
| 3540 | 3432 | ||
| 3541 | try func.genBody(body); | 3433 | try cg.genBody(body); |
| 3542 | try func.endBlock(); | 3434 | try cg.endBlock(); |
| 3543 | 3435 | ||
| 3544 | return func.finishAir(inst, .none, &.{}); | 3436 | return cg.finishAir(inst, .none, &.{}); |
| 3545 | } | 3437 | } |
| 3546 | 3438 | ||
| 3547 | fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3439 | fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3548 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 3440 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 3549 | const condition = try func.resolveInst(pl_op.operand); | 3441 | const condition = try cg.resolveInst(pl_op.operand); |
| 3550 | const extra = func.air.extraData(Air.CondBr, pl_op.payload); | 3442 | const extra = cg.air.extraData(Air.CondBr, pl_op.payload); |
| 3551 | const then_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.then_body_len]); | 3443 | const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.then_body_len]); |
| 3552 | const else_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); | 3444 | const else_body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); |
| 3553 | const liveness_condbr = func.liveness.getCondBr(inst); | 3445 | const liveness_condbr = cg.liveness.getCondBr(inst); |
| 3554 | 3446 | ||
| 3555 | // result type is always noreturn, so use `block_empty` as type. | 3447 | // result type is always noreturn, so use `block_empty` as type. |
| 3556 | try func.startBlock(.block, wasm.block_empty); | 3448 | try cg.startBlock(.block, .empty); |
| 3557 | // emit the conditional value | 3449 | // emit the conditional value |
| 3558 | try func.emitWValue(condition); | 3450 | try cg.emitWValue(condition); |
| 3559 | 3451 | ||
| 3560 | // we inserted the block in front of the condition | 3452 | // we inserted the block in front of the condition |
| 3561 | // so now check if condition matches. If not, break outside this block | 3453 | // so now check if condition matches. If not, break outside this block |
| 3562 | // and continue with the then codepath | 3454 | // and continue with the then codepath |
| 3563 | try func.addLabel(.br_if, 0); | 3455 | try cg.addLabel(.br_if, 0); |
| 3564 | 3456 | ||
| 3565 | try func.branches.ensureUnusedCapacity(func.gpa, 2); | 3457 | try cg.branches.ensureUnusedCapacity(cg.gpa, 2); |
| 3566 | { | 3458 | { |
| 3567 | func.branches.appendAssumeCapacity(.{}); | 3459 | cg.branches.appendAssumeCapacity(.{}); |
| 3568 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len))); | 3460 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len))); |
| 3569 | defer { | 3461 | defer { |
| 3570 | var else_stack = func.branches.pop(); | 3462 | var else_stack = cg.branches.pop(); |
| 3571 | else_stack.deinit(func.gpa); | 3463 | else_stack.deinit(cg.gpa); |
| 3572 | } | 3464 | } |
| 3573 | try func.genBody(else_body); | 3465 | try cg.genBody(else_body); |
| 3574 | try func.endBlock(); | 3466 | try cg.endBlock(); |
| 3575 | } | 3467 | } |
| 3576 | 3468 | ||
| 3577 | // Outer block that matches the condition | 3469 | // Outer block that matches the condition |
| 3578 | { | 3470 | { |
| 3579 | func.branches.appendAssumeCapacity(.{}); | 3471 | cg.branches.appendAssumeCapacity(.{}); |
| 3580 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len))); | 3472 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len))); |
| 3581 | defer { | 3473 | defer { |
| 3582 | var then_stack = func.branches.pop(); | 3474 | var then_stack = cg.branches.pop(); |
| 3583 | then_stack.deinit(func.gpa); | 3475 | then_stack.deinit(cg.gpa); |
| 3584 | } | 3476 | } |
| 3585 | try func.genBody(then_body); | 3477 | try cg.genBody(then_body); |
| 3586 | } | 3478 | } |
| 3587 | 3479 | ||
| 3588 | return func.finishAir(inst, .none, &.{}); | 3480 | return cg.finishAir(inst, .none, &.{}); |
| 3589 | } | 3481 | } |
| 3590 | 3482 | ||
| 3591 | fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void { | 3483 | fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void { |
| 3592 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3484 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3593 | 3485 | ||
| 3594 | const lhs = try func.resolveInst(bin_op.lhs); | 3486 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 3595 | const rhs = try func.resolveInst(bin_op.rhs); | 3487 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 3596 | const operand_ty = func.typeOf(bin_op.lhs); | 3488 | const operand_ty = cg.typeOf(bin_op.lhs); |
| 3597 | const result = try func.cmp(lhs, rhs, operand_ty, op); | 3489 | const result = try cg.cmp(lhs, rhs, operand_ty, op); |
| 3598 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 3490 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 3599 | } | 3491 | } |
| 3600 | 3492 | ||
| 3601 | /// Compares two operands. | 3493 | /// Compares two operands. |
| 3602 | /// Asserts rhs is not a stack value when the lhs isn't a stack value either | 3494 | /// Asserts rhs is not a stack value when the lhs isn't a stack value either |
| 3603 | /// NOTE: This leaves the result on top of the stack, rather than a new local. | 3495 | /// NOTE: This leaves the result on top of the stack, rather than a new local. |
| 3604 | fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { | 3496 | fn cmp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| 3605 | assert(!(lhs != .stack and rhs == .stack)); | 3497 | assert(!(lhs != .stack and rhs == .stack)); |
| 3606 | const pt = func.pt; | 3498 | const zcu = cg.pt.zcu; |
| 3607 | const zcu = pt.zcu; | ||
| 3608 | if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) { | 3499 | if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) { |
| 3609 | const payload_ty = ty.optionalChild(zcu); | 3500 | const payload_ty = ty.optionalChild(zcu); |
| 3610 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 3501 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 3611 | // When we hit this case, we must check the value of optionals | 3502 | // When we hit this case, we must check the value of optionals |
| 3612 | // that are not pointers. This means first checking against non-null for | 3503 | // that are not pointers. This means first checking against non-null for |
| 3613 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs | 3504 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs |
| 3614 | return func.cmpOptionals(lhs, rhs, ty, op); | 3505 | return cg.cmpOptionals(lhs, rhs, ty, op); |
| 3615 | } | 3506 | } |
| 3616 | } else if (ty.isAnyFloat()) { | 3507 | } else if (ty.isAnyFloat()) { |
| 3617 | return func.cmpFloat(ty, lhs, rhs, op); | 3508 | return cg.cmpFloat(ty, lhs, rhs, op); |
| 3618 | } else if (isByRef(ty, pt, func.target.*)) { | 3509 | } else if (isByRef(ty, zcu, cg.target)) { |
| 3619 | return func.cmpBigInt(lhs, rhs, ty, op); | 3510 | return cg.cmpBigInt(lhs, rhs, ty, op); |
| 3620 | } | 3511 | } |
| 3621 | 3512 | ||
| 3622 | const signedness: std.builtin.Signedness = blk: { | 3513 | const signedness: std.builtin.Signedness = blk: { |
| ... | @@ -3629,11 +3520,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO | ... | @@ -3629,11 +3520,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO |
| 3629 | 3520 | ||
| 3630 | // ensure that when we compare pointers, we emit | 3521 | // ensure that when we compare pointers, we emit |
| 3631 | // the true pointer of a stack value, rather than the stack pointer. | 3522 | // the true pointer of a stack value, rather than the stack pointer. |
| 3632 | try func.lowerToStack(lhs); | 3523 | try cg.lowerToStack(lhs); |
| 3633 | try func.lowerToStack(rhs); | 3524 | try cg.lowerToStack(rhs); |
| 3634 | 3525 | ||
| 3635 | const opcode: wasm.Opcode = buildOpcode(.{ | 3526 | const opcode: std.wasm.Opcode = buildOpcode(.{ |
| 3636 | .valtype1 = typeToValtype(ty, pt, func.target.*), | 3527 | .valtype1 = typeToValtype(ty, zcu, cg.target), |
| 3637 | .op = switch (op) { | 3528 | .op = switch (op) { |
| 3638 | .lt => .lt, | 3529 | .lt => .lt, |
| 3639 | .lte => .le, | 3530 | .lte => .le, |
| ... | @@ -3644,15 +3535,15 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO | ... | @@ -3644,15 +3535,15 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO |
| 3644 | }, | 3535 | }, |
| 3645 | .signedness = signedness, | 3536 | .signedness = signedness, |
| 3646 | }); | 3537 | }); |
| 3647 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 3538 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 3648 | 3539 | ||
| 3649 | return .stack; | 3540 | return .stack; |
| 3650 | } | 3541 | } |
| 3651 | 3542 | ||
| 3652 | /// Compares two floats. | 3543 | /// Compares two floats. |
| 3653 | /// NOTE: Leaves the result of the comparison on top of the stack. | 3544 | /// NOTE: Leaves the result of the comparison on top of the stack. |
| 3654 | fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.CompareOperator) InnerError!WValue { | 3545 | fn cmpFloat(cg: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.CompareOperator) InnerError!WValue { |
| 3655 | const float_bits = ty.floatBits(func.target.*); | 3546 | const float_bits = ty.floatBits(cg.target.*); |
| 3656 | 3547 | ||
| 3657 | const op: Op = switch (cmp_op) { | 3548 | const op: Op = switch (cmp_op) { |
| 3658 | .lt => .lt, | 3549 | .lt => .lt, |
| ... | @@ -3665,143 +3556,137 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math | ... | @@ -3665,143 +3556,137 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math |
| 3665 | 3556 | ||
| 3666 | switch (float_bits) { | 3557 | switch (float_bits) { |
| 3667 | 16 => { | 3558 | 16 => { |
| 3668 | _ = try func.fpext(lhs, Type.f16, Type.f32); | 3559 | _ = try cg.fpext(lhs, Type.f16, Type.f32); |
| 3669 | _ = try func.fpext(rhs, Type.f16, Type.f32); | 3560 | _ = try cg.fpext(rhs, Type.f16, Type.f32); |
| 3670 | const opcode = buildOpcode(.{ .op = op, .valtype1 = .f32 }); | 3561 | const opcode = buildOpcode(.{ .op = op, .valtype1 = .f32 }); |
| 3671 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 3562 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 3672 | return .stack; | 3563 | return .stack; |
| 3673 | }, | 3564 | }, |
| 3674 | 32, 64 => { | 3565 | 32, 64 => { |
| 3675 | try func.emitWValue(lhs); | 3566 | try cg.emitWValue(lhs); |
| 3676 | try func.emitWValue(rhs); | 3567 | try cg.emitWValue(rhs); |
| 3677 | const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64; | 3568 | const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64; |
| 3678 | const opcode = buildOpcode(.{ .op = op, .valtype1 = val_type }); | 3569 | const opcode = buildOpcode(.{ .op = op, .valtype1 = val_type }); |
| 3679 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 3570 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 3680 | return .stack; | 3571 | return .stack; |
| 3681 | }, | 3572 | }, |
| 3682 | 80, 128 => { | 3573 | 80, 128 => { |
| 3683 | var fn_name_buf: [32]u8 = undefined; | 3574 | const intrinsic = floatCmpIntrinsic(cmp_op, float_bits); |
| 3684 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f2", .{ | 3575 | const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); |
| 3685 | @tagName(op), target_util.compilerRtFloatAbbrev(float_bits), | 3576 | return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op); |
| 3686 | }) catch unreachable; | ||
| 3687 | |||
| 3688 | const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); | ||
| 3689 | return func.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op); | ||
| 3690 | }, | 3577 | }, |
| 3691 | else => unreachable, | 3578 | else => unreachable, |
| 3692 | } | 3579 | } |
| 3693 | } | 3580 | } |
| 3694 | 3581 | ||
| 3695 | fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3582 | fn airCmpVector(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3696 | _ = inst; | 3583 | _ = inst; |
| 3697 | return func.fail("TODO implement airCmpVector for wasm", .{}); | 3584 | return cg.fail("TODO implement airCmpVector for wasm", .{}); |
| 3698 | } | 3585 | } |
| 3699 | 3586 | ||
| 3700 | fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3587 | fn airCmpLtErrorsLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3701 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 3588 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 3702 | const operand = try func.resolveInst(un_op); | 3589 | const operand = try cg.resolveInst(un_op); |
| 3703 | const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null); | ||
| 3704 | const errors_len: WValue = .{ .memory = @intFromEnum(sym_index) }; | ||
| 3705 | 3590 | ||
| 3706 | try func.emitWValue(operand); | 3591 | try cg.emitWValue(operand); |
| 3707 | const pt = func.pt; | 3592 | const pt = cg.pt; |
| 3708 | const err_int_ty = try pt.errorIntType(); | 3593 | const err_int_ty = try pt.errorIntType(); |
| 3709 | const errors_len_val = try func.load(errors_len, err_int_ty, 0); | 3594 | try cg.addTag(.errors_len); |
| 3710 | const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt); | 3595 | const result = try cg.cmp(.stack, .stack, err_int_ty, .lt); |
| 3711 | 3596 | ||
| 3712 | return func.finishAir(inst, result, &.{un_op}); | 3597 | return cg.finishAir(inst, result, &.{un_op}); |
| 3713 | } | 3598 | } |
| 3714 | 3599 | ||
| 3715 | fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3600 | fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3716 | const zcu = func.pt.zcu; | 3601 | const zcu = cg.pt.zcu; |
| 3717 | const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br; | 3602 | const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br; |
| 3718 | const block = func.blocks.get(br.block_inst).?; | 3603 | const block = cg.blocks.get(br.block_inst).?; |
| 3719 | 3604 | ||
| 3720 | // if operand has codegen bits we should break with a value | 3605 | // if operand has codegen bits we should break with a value |
| 3721 | if (func.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) { | 3606 | if (cg.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) { |
| 3722 | const operand = try func.resolveInst(br.operand); | 3607 | const operand = try cg.resolveInst(br.operand); |
| 3723 | try func.lowerToStack(operand); | 3608 | try cg.lowerToStack(operand); |
| 3724 | 3609 | ||
| 3725 | if (block.value != .none) { | 3610 | if (block.value != .none) { |
| 3726 | try func.addLabel(.local_set, block.value.local.value); | 3611 | try cg.addLocal(.local_set, block.value.local.value); |
| 3727 | } | 3612 | } |
| 3728 | } | 3613 | } |
| 3729 | 3614 | ||
| 3730 | // We map every block to its block index. | 3615 | // We map every block to its block index. |
| 3731 | // We then determine how far we have to jump to it by subtracting it from current block depth | 3616 | // We then determine how far we have to jump to it by subtracting it from current block depth |
| 3732 | const idx: u32 = func.block_depth - block.label; | 3617 | const idx: u32 = cg.block_depth - block.label; |
| 3733 | try func.addLabel(.br, idx); | 3618 | try cg.addLabel(.br, idx); |
| 3734 | 3619 | ||
| 3735 | return func.finishAir(inst, .none, &.{br.operand}); | 3620 | return cg.finishAir(inst, .none, &.{br.operand}); |
| 3736 | } | 3621 | } |
| 3737 | 3622 | ||
| 3738 | fn airRepeat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3623 | fn airRepeat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3739 | const repeat = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat; | 3624 | const repeat = cg.air.instructions.items(.data)[@intFromEnum(inst)].repeat; |
| 3740 | const loop_label = func.loops.get(repeat.loop_inst).?; | 3625 | const loop_label = cg.loops.get(repeat.loop_inst).?; |
| 3741 | 3626 | ||
| 3742 | const idx: u32 = func.block_depth - loop_label; | 3627 | const idx: u32 = cg.block_depth - loop_label; |
| 3743 | try func.addLabel(.br, idx); | 3628 | try cg.addLabel(.br, idx); |
| 3744 | 3629 | ||
| 3745 | return func.finishAir(inst, .none, &.{}); | 3630 | return cg.finishAir(inst, .none, &.{}); |
| 3746 | } | 3631 | } |
| 3747 | 3632 | ||
| 3748 | fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3633 | fn airNot(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3749 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3634 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3750 | 3635 | ||
| 3751 | const operand = try func.resolveInst(ty_op.operand); | 3636 | const operand = try cg.resolveInst(ty_op.operand); |
| 3752 | const operand_ty = func.typeOf(ty_op.operand); | 3637 | const operand_ty = cg.typeOf(ty_op.operand); |
| 3753 | const pt = func.pt; | 3638 | const pt = cg.pt; |
| 3754 | const zcu = pt.zcu; | 3639 | const zcu = pt.zcu; |
| 3755 | 3640 | ||
| 3756 | const result = result: { | 3641 | const result = result: { |
| 3757 | if (operand_ty.zigTypeTag(zcu) == .bool) { | 3642 | if (operand_ty.zigTypeTag(zcu) == .bool) { |
| 3758 | try func.emitWValue(operand); | 3643 | try cg.emitWValue(operand); |
| 3759 | try func.addTag(.i32_eqz); | 3644 | try cg.addTag(.i32_eqz); |
| 3760 | const not_tmp = try func.allocLocal(operand_ty); | 3645 | const not_tmp = try cg.allocLocal(operand_ty); |
| 3761 | try func.addLabel(.local_set, not_tmp.local.value); | 3646 | try cg.addLocal(.local_set, not_tmp.local.value); |
| 3762 | break :result not_tmp; | 3647 | break :result not_tmp; |
| 3763 | } else { | 3648 | } else { |
| 3764 | const int_info = operand_ty.intInfo(zcu); | 3649 | const int_info = operand_ty.intInfo(zcu); |
| 3765 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 3650 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 3766 | return func.fail("TODO: Implement binary NOT for {}", .{operand_ty.fmt(pt)}); | 3651 | return cg.fail("TODO: Implement binary NOT for {}", .{operand_ty.fmt(pt)}); |
| 3767 | }; | 3652 | }; |
| 3768 | 3653 | ||
| 3769 | switch (wasm_bits) { | 3654 | switch (wasm_bits) { |
| 3770 | 32 => { | 3655 | 32 => { |
| 3771 | try func.emitWValue(operand); | 3656 | try cg.emitWValue(operand); |
| 3772 | try func.addImm32(switch (int_info.signedness) { | 3657 | try cg.addImm32(switch (int_info.signedness) { |
| 3773 | .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits), | 3658 | .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits), |
| 3774 | .signed => ~@as(u32, 0), | 3659 | .signed => ~@as(u32, 0), |
| 3775 | }); | 3660 | }); |
| 3776 | try func.addTag(.i32_xor); | 3661 | try cg.addTag(.i32_xor); |
| 3777 | break :result .stack; | 3662 | break :result .stack; |
| 3778 | }, | 3663 | }, |
| 3779 | 64 => { | 3664 | 64 => { |
| 3780 | try func.emitWValue(operand); | 3665 | try cg.emitWValue(operand); |
| 3781 | try func.addImm64(switch (int_info.signedness) { | 3666 | try cg.addImm64(switch (int_info.signedness) { |
| 3782 | .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits), | 3667 | .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits), |
| 3783 | .signed => ~@as(u64, 0), | 3668 | .signed => ~@as(u64, 0), |
| 3784 | }); | 3669 | }); |
| 3785 | try func.addTag(.i64_xor); | 3670 | try cg.addTag(.i64_xor); |
| 3786 | break :result .stack; | 3671 | break :result .stack; |
| 3787 | }, | 3672 | }, |
| 3788 | 128 => { | 3673 | 128 => { |
| 3789 | const ptr = try func.allocStack(operand_ty); | 3674 | const ptr = try cg.allocStack(operand_ty); |
| 3790 | 3675 | ||
| 3791 | try func.emitWValue(ptr); | 3676 | try cg.emitWValue(ptr); |
| 3792 | _ = try func.load(operand, Type.u64, 0); | 3677 | _ = try cg.load(operand, Type.u64, 0); |
| 3793 | try func.addImm64(~@as(u64, 0)); | 3678 | try cg.addImm64(~@as(u64, 0)); |
| 3794 | try func.addTag(.i64_xor); | 3679 | try cg.addTag(.i64_xor); |
| 3795 | try func.store(.stack, .stack, Type.u64, ptr.offset()); | 3680 | try cg.store(.stack, .stack, Type.u64, ptr.offset()); |
| 3796 | 3681 | ||
| 3797 | try func.emitWValue(ptr); | 3682 | try cg.emitWValue(ptr); |
| 3798 | _ = try func.load(operand, Type.u64, 8); | 3683 | _ = try cg.load(operand, Type.u64, 8); |
| 3799 | try func.addImm64(switch (int_info.signedness) { | 3684 | try cg.addImm64(switch (int_info.signedness) { |
| 3800 | .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits), | 3685 | .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits), |
| 3801 | .signed => ~@as(u64, 0), | 3686 | .signed => ~@as(u64, 0), |
| 3802 | }); | 3687 | }); |
| 3803 | try func.addTag(.i64_xor); | 3688 | try cg.addTag(.i64_xor); |
| 3804 | try func.store(.stack, .stack, Type.u64, ptr.offset() + 8); | 3689 | try cg.store(.stack, .stack, Type.u64, ptr.offset() + 8); |
| 3805 | 3690 | ||
| 3806 | break :result ptr; | 3691 | break :result ptr; |
| 3807 | }, | 3692 | }, |
| ... | @@ -3809,33 +3694,32 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3809,33 +3694,32 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3809 | } | 3694 | } |
| 3810 | } | 3695 | } |
| 3811 | }; | 3696 | }; |
| 3812 | return func.finishAir(inst, result, &.{ty_op.operand}); | 3697 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 3813 | } | 3698 | } |
| 3814 | 3699 | ||
| 3815 | fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3700 | fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3816 | try func.addTag(.@"unreachable"); | 3701 | try cg.addTag(.@"unreachable"); |
| 3817 | return func.finishAir(inst, .none, &.{}); | 3702 | return cg.finishAir(inst, .none, &.{}); |
| 3818 | } | 3703 | } |
| 3819 | 3704 | ||
| 3820 | fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3705 | fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3821 | // unsupported by wasm itfunc. Can be implemented once we support DWARF | 3706 | // unsupported by wasm itfunc. Can be implemented once we support DWARF |
| 3822 | // for wasm | 3707 | // for wasm |
| 3823 | try func.addTag(.@"unreachable"); | 3708 | try cg.addTag(.@"unreachable"); |
| 3824 | return func.finishAir(inst, .none, &.{}); | 3709 | return cg.finishAir(inst, .none, &.{}); |
| 3825 | } | 3710 | } |
| 3826 | 3711 | ||
| 3827 | fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3712 | fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3828 | try func.addTag(.@"unreachable"); | 3713 | try cg.addTag(.@"unreachable"); |
| 3829 | return func.finishAir(inst, .none, &.{}); | 3714 | return cg.finishAir(inst, .none, &.{}); |
| 3830 | } | 3715 | } |
| 3831 | 3716 | ||
| 3832 | fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3717 | fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3833 | const pt = func.pt; | 3718 | const zcu = cg.pt.zcu; |
| 3834 | const zcu = pt.zcu; | 3719 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3835 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3720 | const operand = try cg.resolveInst(ty_op.operand); |
| 3836 | const operand = try func.resolveInst(ty_op.operand); | 3721 | const wanted_ty = cg.typeOfIndex(inst); |
| 3837 | const wanted_ty = func.typeOfIndex(inst); | 3722 | const given_ty = cg.typeOf(ty_op.operand); |
| 3838 | const given_ty = func.typeOf(ty_op.operand); | ||
| 3839 | 3723 | ||
| 3840 | const bit_size = given_ty.bitSize(zcu); | 3724 | const bit_size = given_ty.bitSize(zcu); |
| 3841 | const needs_wrapping = (given_ty.isSignedInt(zcu) != wanted_ty.isSignedInt(zcu)) and | 3725 | const needs_wrapping = (given_ty.isSignedInt(zcu) != wanted_ty.isSignedInt(zcu)) and |
| ... | @@ -3843,39 +3727,38 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3843,39 +3727,38 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3843 | 3727 | ||
| 3844 | const result = result: { | 3728 | const result = result: { |
| 3845 | if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) { | 3729 | if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) { |
| 3846 | break :result try func.bitcast(wanted_ty, given_ty, operand); | 3730 | break :result try cg.bitcast(wanted_ty, given_ty, operand); |
| 3847 | } | 3731 | } |
| 3848 | 3732 | ||
| 3849 | if (isByRef(given_ty, pt, func.target.*) and !isByRef(wanted_ty, pt, func.target.*)) { | 3733 | if (isByRef(given_ty, zcu, cg.target) and !isByRef(wanted_ty, zcu, cg.target)) { |
| 3850 | const loaded_memory = try func.load(operand, wanted_ty, 0); | 3734 | const loaded_memory = try cg.load(operand, wanted_ty, 0); |
| 3851 | if (needs_wrapping) { | 3735 | if (needs_wrapping) { |
| 3852 | break :result try func.wrapOperand(loaded_memory, wanted_ty); | 3736 | break :result try cg.wrapOperand(loaded_memory, wanted_ty); |
| 3853 | } else { | 3737 | } else { |
| 3854 | break :result loaded_memory; | 3738 | break :result loaded_memory; |
| 3855 | } | 3739 | } |
| 3856 | } | 3740 | } |
| 3857 | if (!isByRef(given_ty, pt, func.target.*) and isByRef(wanted_ty, pt, func.target.*)) { | 3741 | if (!isByRef(given_ty, zcu, cg.target) and isByRef(wanted_ty, zcu, cg.target)) { |
| 3858 | const stack_memory = try func.allocStack(wanted_ty); | 3742 | const stack_memory = try cg.allocStack(wanted_ty); |
| 3859 | try func.store(stack_memory, operand, given_ty, 0); | 3743 | try cg.store(stack_memory, operand, given_ty, 0); |
| 3860 | if (needs_wrapping) { | 3744 | if (needs_wrapping) { |
| 3861 | break :result try func.wrapOperand(stack_memory, wanted_ty); | 3745 | break :result try cg.wrapOperand(stack_memory, wanted_ty); |
| 3862 | } else { | 3746 | } else { |
| 3863 | break :result stack_memory; | 3747 | break :result stack_memory; |
| 3864 | } | 3748 | } |
| 3865 | } | 3749 | } |
| 3866 | 3750 | ||
| 3867 | if (needs_wrapping) { | 3751 | if (needs_wrapping) { |
| 3868 | break :result try func.wrapOperand(operand, wanted_ty); | 3752 | break :result try cg.wrapOperand(operand, wanted_ty); |
| 3869 | } | 3753 | } |
| 3870 | 3754 | ||
| 3871 | break :result func.reuseOperand(ty_op.operand, operand); | 3755 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 3872 | }; | 3756 | }; |
| 3873 | return func.finishAir(inst, result, &.{ty_op.operand}); | 3757 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 3874 | } | 3758 | } |
| 3875 | 3759 | ||
| 3876 | fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { | 3760 | fn bitcast(cg: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { |
| 3877 | const pt = func.pt; | 3761 | const zcu = cg.pt.zcu; |
| 3878 | const zcu = pt.zcu; | ||
| 3879 | // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction | 3762 | // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction |
| 3880 | if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand; | 3763 | if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand; |
| 3881 | if (wanted_ty.ip_index == .f16_type or given_ty.ip_index == .f16_type) return operand; | 3764 | if (wanted_ty.ip_index == .f16_type or given_ty.ip_index == .f16_type) return operand; |
| ... | @@ -3884,41 +3767,39 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn | ... | @@ -3884,41 +3767,39 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn |
| 3884 | 3767 | ||
| 3885 | const opcode = buildOpcode(.{ | 3768 | const opcode = buildOpcode(.{ |
| 3886 | .op = .reinterpret, | 3769 | .op = .reinterpret, |
| 3887 | .valtype1 = typeToValtype(wanted_ty, pt, func.target.*), | 3770 | .valtype1 = typeToValtype(wanted_ty, zcu, cg.target), |
| 3888 | .valtype2 = typeToValtype(given_ty, pt, func.target.*), | 3771 | .valtype2 = typeToValtype(given_ty, zcu, cg.target), |
| 3889 | }); | 3772 | }); |
| 3890 | try func.emitWValue(operand); | 3773 | try cg.emitWValue(operand); |
| 3891 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 3774 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 3892 | return .stack; | 3775 | return .stack; |
| 3893 | } | 3776 | } |
| 3894 | 3777 | ||
| 3895 | fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3778 | fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3896 | const pt = func.pt; | 3779 | const zcu = cg.pt.zcu; |
| 3897 | const zcu = pt.zcu; | 3780 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3898 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3781 | const extra = cg.air.extraData(Air.StructField, ty_pl.payload); |
| 3899 | const extra = func.air.extraData(Air.StructField, ty_pl.payload); | ||
| 3900 | 3782 | ||
| 3901 | const struct_ptr = try func.resolveInst(extra.data.struct_operand); | 3783 | const struct_ptr = try cg.resolveInst(extra.data.struct_operand); |
| 3902 | const struct_ptr_ty = func.typeOf(extra.data.struct_operand); | 3784 | const struct_ptr_ty = cg.typeOf(extra.data.struct_operand); |
| 3903 | const struct_ty = struct_ptr_ty.childType(zcu); | 3785 | const struct_ty = struct_ptr_ty.childType(zcu); |
| 3904 | const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index); | 3786 | const result = try cg.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index); |
| 3905 | return func.finishAir(inst, result, &.{extra.data.struct_operand}); | 3787 | return cg.finishAir(inst, result, &.{extra.data.struct_operand}); |
| 3906 | } | 3788 | } |
| 3907 | 3789 | ||
| 3908 | fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void { | 3790 | fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void { |
| 3909 | const pt = func.pt; | 3791 | const zcu = cg.pt.zcu; |
| 3910 | const zcu = pt.zcu; | 3792 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3911 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 3793 | const struct_ptr = try cg.resolveInst(ty_op.operand); |
| 3912 | const struct_ptr = try func.resolveInst(ty_op.operand); | 3794 | const struct_ptr_ty = cg.typeOf(ty_op.operand); |
| 3913 | const struct_ptr_ty = func.typeOf(ty_op.operand); | ||
| 3914 | const struct_ty = struct_ptr_ty.childType(zcu); | 3795 | const struct_ty = struct_ptr_ty.childType(zcu); |
| 3915 | 3796 | ||
| 3916 | const result = try func.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index); | 3797 | const result = try cg.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index); |
| 3917 | return func.finishAir(inst, result, &.{ty_op.operand}); | 3798 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 3918 | } | 3799 | } |
| 3919 | 3800 | ||
| 3920 | fn structFieldPtr( | 3801 | fn structFieldPtr( |
| 3921 | func: *CodeGen, | 3802 | cg: *CodeGen, |
| 3922 | inst: Air.Inst.Index, | 3803 | inst: Air.Inst.Index, |
| 3923 | ref: Air.Inst.Ref, | 3804 | ref: Air.Inst.Ref, |
| 3924 | struct_ptr: WValue, | 3805 | struct_ptr: WValue, |
| ... | @@ -3926,9 +3807,9 @@ fn structFieldPtr( | ... | @@ -3926,9 +3807,9 @@ fn structFieldPtr( |
| 3926 | struct_ty: Type, | 3807 | struct_ty: Type, |
| 3927 | index: u32, | 3808 | index: u32, |
| 3928 | ) InnerError!WValue { | 3809 | ) InnerError!WValue { |
| 3929 | const pt = func.pt; | 3810 | const pt = cg.pt; |
| 3930 | const zcu = pt.zcu; | 3811 | const zcu = pt.zcu; |
| 3931 | const result_ty = func.typeOfIndex(inst); | 3812 | const result_ty = cg.typeOfIndex(inst); |
| 3932 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); | 3813 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); |
| 3933 | 3814 | ||
| 3934 | const offset = switch (struct_ty.containerLayout(zcu)) { | 3815 | const offset = switch (struct_ty.containerLayout(zcu)) { |
| ... | @@ -3947,28 +3828,28 @@ fn structFieldPtr( | ... | @@ -3947,28 +3828,28 @@ fn structFieldPtr( |
| 3947 | }; | 3828 | }; |
| 3948 | // save a load and store when we can simply reuse the operand | 3829 | // save a load and store when we can simply reuse the operand |
| 3949 | if (offset == 0) { | 3830 | if (offset == 0) { |
| 3950 | return func.reuseOperand(ref, struct_ptr); | 3831 | return cg.reuseOperand(ref, struct_ptr); |
| 3951 | } | 3832 | } |
| 3952 | switch (struct_ptr) { | 3833 | switch (struct_ptr) { |
| 3953 | .stack_offset => |stack_offset| { | 3834 | .stack_offset => |stack_offset| { |
| 3954 | return .{ .stack_offset = .{ .value = stack_offset.value + @as(u32, @intCast(offset)), .references = 1 } }; | 3835 | return .{ .stack_offset = .{ .value = stack_offset.value + @as(u32, @intCast(offset)), .references = 1 } }; |
| 3955 | }, | 3836 | }, |
| 3956 | else => return func.buildPointerOffset(struct_ptr, offset, .new), | 3837 | else => return cg.buildPointerOffset(struct_ptr, offset, .new), |
| 3957 | } | 3838 | } |
| 3958 | } | 3839 | } |
| 3959 | 3840 | ||
| 3960 | fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3841 | fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3961 | const pt = func.pt; | 3842 | const pt = cg.pt; |
| 3962 | const zcu = pt.zcu; | 3843 | const zcu = pt.zcu; |
| 3963 | const ip = &zcu.intern_pool; | 3844 | const ip = &zcu.intern_pool; |
| 3964 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3845 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3965 | const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data; | 3846 | const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data; |
| 3966 | 3847 | ||
| 3967 | const struct_ty = func.typeOf(struct_field.struct_operand); | 3848 | const struct_ty = cg.typeOf(struct_field.struct_operand); |
| 3968 | const operand = try func.resolveInst(struct_field.struct_operand); | 3849 | const operand = try cg.resolveInst(struct_field.struct_operand); |
| 3969 | const field_index = struct_field.field_index; | 3850 | const field_index = struct_field.field_index; |
| 3970 | const field_ty = struct_ty.fieldType(field_index, zcu); | 3851 | const field_ty = struct_ty.fieldType(field_index, zcu); |
| 3971 | if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{struct_field.struct_operand}); | 3852 | if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return cg.finishAir(inst, .none, &.{struct_field.struct_operand}); |
| 3972 | 3853 | ||
| 3973 | const result: WValue = switch (struct_ty.containerLayout(zcu)) { | 3854 | const result: WValue = switch (struct_ty.containerLayout(zcu)) { |
| 3974 | .@"packed" => switch (struct_ty.zigTypeTag(zcu)) { | 3855 | .@"packed" => switch (struct_ty.zigTypeTag(zcu)) { |
| ... | @@ -3977,42 +3858,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3977,42 +3858,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3977 | const offset = pt.structPackedFieldBitOffset(packed_struct, field_index); | 3858 | const offset = pt.structPackedFieldBitOffset(packed_struct, field_index); |
| 3978 | const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); | 3859 | const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); |
| 3979 | const wasm_bits = toWasmBits(backing_ty.intInfo(zcu).bits) orelse { | 3860 | const wasm_bits = toWasmBits(backing_ty.intInfo(zcu).bits) orelse { |
| 3980 | return func.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{}); | 3861 | return cg.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{}); |
| 3981 | }; | 3862 | }; |
| 3982 | const const_wvalue: WValue = if (wasm_bits == 32) | 3863 | const const_wvalue: WValue = if (wasm_bits == 32) |
| 3983 | .{ .imm32 = offset } | 3864 | .{ .imm32 = offset } |
| 3984 | else if (wasm_bits == 64) | 3865 | else if (wasm_bits == 64) |
| 3985 | .{ .imm64 = offset } | 3866 | .{ .imm64 = offset } |
| 3986 | else | 3867 | else |
| 3987 | return func.fail("TODO: airStructFieldVal for packed structs larger than 64 bits", .{}); | 3868 | return cg.fail("TODO: airStructFieldVal for packed structs larger than 64 bits", .{}); |
| 3988 | 3869 | ||
| 3989 | // for first field we don't require any shifting | 3870 | // for first field we don't require any shifting |
| 3990 | const shifted_value = if (offset == 0) | 3871 | const shifted_value = if (offset == 0) |
| 3991 | operand | 3872 | operand |
| 3992 | else | 3873 | else |
| 3993 | try func.binOp(operand, const_wvalue, backing_ty, .shr); | 3874 | try cg.binOp(operand, const_wvalue, backing_ty, .shr); |
| 3994 | 3875 | ||
| 3995 | if (field_ty.zigTypeTag(zcu) == .float) { | 3876 | if (field_ty.zigTypeTag(zcu) == .float) { |
| 3996 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); | 3877 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); |
| 3997 | const truncated = try func.trunc(shifted_value, int_type, backing_ty); | 3878 | const truncated = try cg.trunc(shifted_value, int_type, backing_ty); |
| 3998 | break :result try func.bitcast(field_ty, int_type, truncated); | 3879 | break :result try cg.bitcast(field_ty, int_type, truncated); |
| 3999 | } else if (field_ty.isPtrAtRuntime(zcu) and packed_struct.field_types.len == 1) { | 3880 | } else if (field_ty.isPtrAtRuntime(zcu) and packed_struct.field_types.len == 1) { |
| 4000 | // In this case we do not have to perform any transformations, | 3881 | // In this case we do not have to perform any transformations, |
| 4001 | // we can simply reuse the operand. | 3882 | // we can simply reuse the operand. |
| 4002 | break :result func.reuseOperand(struct_field.struct_operand, operand); | 3883 | break :result cg.reuseOperand(struct_field.struct_operand, operand); |
| 4003 | } else if (field_ty.isPtrAtRuntime(zcu)) { | 3884 | } else if (field_ty.isPtrAtRuntime(zcu)) { |
| 4004 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); | 3885 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); |
| 4005 | break :result try func.trunc(shifted_value, int_type, backing_ty); | 3886 | break :result try cg.trunc(shifted_value, int_type, backing_ty); |
| 4006 | } | 3887 | } |
| 4007 | break :result try func.trunc(shifted_value, field_ty, backing_ty); | 3888 | break :result try cg.trunc(shifted_value, field_ty, backing_ty); |
| 4008 | }, | 3889 | }, |
| 4009 | .@"union" => result: { | 3890 | .@"union" => result: { |
| 4010 | if (isByRef(struct_ty, pt, func.target.*)) { | 3891 | if (isByRef(struct_ty, zcu, cg.target)) { |
| 4011 | if (!isByRef(field_ty, pt, func.target.*)) { | 3892 | if (!isByRef(field_ty, zcu, cg.target)) { |
| 4012 | break :result try func.load(operand, field_ty, 0); | 3893 | break :result try cg.load(operand, field_ty, 0); |
| 4013 | } else { | 3894 | } else { |
| 4014 | const new_stack_val = try func.allocStack(field_ty); | 3895 | const new_stack_val = try cg.allocStack(field_ty); |
| 4015 | try func.store(new_stack_val, operand, field_ty, 0); | 3896 | try cg.store(new_stack_val, operand, field_ty, 0); |
| 4016 | break :result new_stack_val; | 3897 | break :result new_stack_val; |
| 4017 | } | 3898 | } |
| 4018 | } | 3899 | } |
| ... | @@ -4020,45 +3901,45 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4020,45 +3901,45 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4020 | const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(struct_ty.bitSize(zcu)))); | 3901 | const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(struct_ty.bitSize(zcu)))); |
| 4021 | if (field_ty.zigTypeTag(zcu) == .float) { | 3902 | if (field_ty.zigTypeTag(zcu) == .float) { |
| 4022 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); | 3903 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); |
| 4023 | const truncated = try func.trunc(operand, int_type, union_int_type); | 3904 | const truncated = try cg.trunc(operand, int_type, union_int_type); |
| 4024 | break :result try func.bitcast(field_ty, int_type, truncated); | 3905 | break :result try cg.bitcast(field_ty, int_type, truncated); |
| 4025 | } else if (field_ty.isPtrAtRuntime(zcu)) { | 3906 | } else if (field_ty.isPtrAtRuntime(zcu)) { |
| 4026 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); | 3907 | const int_type = try pt.intType(.unsigned, @as(u16, @intCast(field_ty.bitSize(zcu)))); |
| 4027 | break :result try func.trunc(operand, int_type, union_int_type); | 3908 | break :result try cg.trunc(operand, int_type, union_int_type); |
| 4028 | } | 3909 | } |
| 4029 | break :result try func.trunc(operand, field_ty, union_int_type); | 3910 | break :result try cg.trunc(operand, field_ty, union_int_type); |
| 4030 | }, | 3911 | }, |
| 4031 | else => unreachable, | 3912 | else => unreachable, |
| 4032 | }, | 3913 | }, |
| 4033 | else => result: { | 3914 | else => result: { |
| 4034 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse { | 3915 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse { |
| 4035 | return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)}); | 3916 | return cg.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)}); |
| 4036 | }; | 3917 | }; |
| 4037 | if (isByRef(field_ty, pt, func.target.*)) { | 3918 | if (isByRef(field_ty, zcu, cg.target)) { |
| 4038 | switch (operand) { | 3919 | switch (operand) { |
| 4039 | .stack_offset => |stack_offset| { | 3920 | .stack_offset => |stack_offset| { |
| 4040 | break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } }; | 3921 | break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } }; |
| 4041 | }, | 3922 | }, |
| 4042 | else => break :result try func.buildPointerOffset(operand, offset, .new), | 3923 | else => break :result try cg.buildPointerOffset(operand, offset, .new), |
| 4043 | } | 3924 | } |
| 4044 | } | 3925 | } |
| 4045 | break :result try func.load(operand, field_ty, offset); | 3926 | break :result try cg.load(operand, field_ty, offset); |
| 4046 | }, | 3927 | }, |
| 4047 | }; | 3928 | }; |
| 4048 | 3929 | ||
| 4049 | return func.finishAir(inst, result, &.{struct_field.struct_operand}); | 3930 | return cg.finishAir(inst, result, &.{struct_field.struct_operand}); |
| 4050 | } | 3931 | } |
| 4051 | 3932 | ||
| 4052 | fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3933 | fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4053 | const pt = func.pt; | 3934 | const pt = cg.pt; |
| 4054 | const zcu = pt.zcu; | 3935 | const zcu = pt.zcu; |
| 4055 | // result type is always 'noreturn' | 3936 | // result type is always 'noreturn' |
| 4056 | const blocktype = wasm.block_empty; | 3937 | const blocktype: std.wasm.BlockType = .empty; |
| 4057 | const switch_br = func.air.unwrapSwitch(inst); | 3938 | const switch_br = cg.air.unwrapSwitch(inst); |
| 4058 | const target = try func.resolveInst(switch_br.operand); | 3939 | const target = try cg.resolveInst(switch_br.operand); |
| 4059 | const target_ty = func.typeOf(switch_br.operand); | 3940 | const target_ty = cg.typeOf(switch_br.operand); |
| 4060 | const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1); | 3941 | const liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1); |
| 4061 | defer func.gpa.free(liveness.deaths); | 3942 | defer cg.gpa.free(liveness.deaths); |
| 4062 | 3943 | ||
| 4063 | // a list that maps each value with its value and body based on the order inside the list. | 3944 | // a list that maps each value with its value and body based on the order inside the list. |
| 4064 | const CaseValue = union(enum) { | 3945 | const CaseValue = union(enum) { |
| ... | @@ -4068,21 +3949,21 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4068,21 +3949,21 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4068 | var case_list = try std.ArrayList(struct { | 3949 | var case_list = try std.ArrayList(struct { |
| 4069 | values: []const CaseValue, | 3950 | values: []const CaseValue, |
| 4070 | body: []const Air.Inst.Index, | 3951 | body: []const Air.Inst.Index, |
| 4071 | }).initCapacity(func.gpa, switch_br.cases_len); | 3952 | }).initCapacity(cg.gpa, switch_br.cases_len); |
| 4072 | defer for (case_list.items) |case| { | 3953 | defer for (case_list.items) |case| { |
| 4073 | func.gpa.free(case.values); | 3954 | cg.gpa.free(case.values); |
| 4074 | } else case_list.deinit(); | 3955 | } else case_list.deinit(); |
| 4075 | 3956 | ||
| 4076 | var lowest_maybe: ?i32 = null; | 3957 | var lowest_maybe: ?i32 = null; |
| 4077 | var highest_maybe: ?i32 = null; | 3958 | var highest_maybe: ?i32 = null; |
| 4078 | var it = switch_br.iterateCases(); | 3959 | var it = switch_br.iterateCases(); |
| 4079 | while (it.next()) |case| { | 3960 | while (it.next()) |case| { |
| 4080 | const values = try func.gpa.alloc(CaseValue, case.items.len + case.ranges.len); | 3961 | const values = try cg.gpa.alloc(CaseValue, case.items.len + case.ranges.len); |
| 4081 | errdefer func.gpa.free(values); | 3962 | errdefer cg.gpa.free(values); |
| 4082 | 3963 | ||
| 4083 | for (case.items, 0..) |ref, i| { | 3964 | for (case.items, 0..) |ref, i| { |
| 4084 | const item_val = (try func.air.value(ref, pt)).?; | 3965 | const item_val = (try cg.air.value(ref, pt)).?; |
| 4085 | const int_val = func.valueAsI32(item_val); | 3966 | const int_val = cg.valueAsI32(item_val); |
| 4086 | if (lowest_maybe == null or int_val < lowest_maybe.?) { | 3967 | if (lowest_maybe == null or int_val < lowest_maybe.?) { |
| 4087 | lowest_maybe = int_val; | 3968 | lowest_maybe = int_val; |
| 4088 | } | 3969 | } |
| ... | @@ -4093,15 +3974,15 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4093,15 +3974,15 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4093 | } | 3974 | } |
| 4094 | 3975 | ||
| 4095 | for (case.ranges, 0..) |range, i| { | 3976 | for (case.ranges, 0..) |range, i| { |
| 4096 | const min_val = (try func.air.value(range[0], pt)).?; | 3977 | const min_val = (try cg.air.value(range[0], pt)).?; |
| 4097 | const int_min_val = func.valueAsI32(min_val); | 3978 | const int_min_val = cg.valueAsI32(min_val); |
| 4098 | 3979 | ||
| 4099 | if (lowest_maybe == null or int_min_val < lowest_maybe.?) { | 3980 | if (lowest_maybe == null or int_min_val < lowest_maybe.?) { |
| 4100 | lowest_maybe = int_min_val; | 3981 | lowest_maybe = int_min_val; |
| 4101 | } | 3982 | } |
| 4102 | 3983 | ||
| 4103 | const max_val = (try func.air.value(range[1], pt)).?; | 3984 | const max_val = (try cg.air.value(range[1], pt)).?; |
| 4104 | const int_max_val = func.valueAsI32(max_val); | 3985 | const int_max_val = cg.valueAsI32(max_val); |
| 4105 | 3986 | ||
| 4106 | if (highest_maybe == null or int_max_val > highest_maybe.?) { | 3987 | if (highest_maybe == null or int_max_val > highest_maybe.?) { |
| 4107 | highest_maybe = int_max_val; | 3988 | highest_maybe = int_max_val; |
| ... | @@ -4116,7 +3997,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4116,7 +3997,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4116 | } | 3997 | } |
| 4117 | 3998 | ||
| 4118 | case_list.appendAssumeCapacity(.{ .values = values, .body = case.body }); | 3999 | case_list.appendAssumeCapacity(.{ .values = values, .body = case.body }); |
| 4119 | try func.startBlock(.block, blocktype); | 4000 | try cg.startBlock(.block, blocktype); |
| 4120 | } | 4001 | } |
| 4121 | 4002 | ||
| 4122 | // When highest and lowest are null, we have no cases and can use a jump table | 4003 | // When highest and lowest are null, we have no cases and can use a jump table |
| ... | @@ -4132,7 +4013,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4132,7 +4013,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4132 | const else_body = it.elseBody(); | 4013 | const else_body = it.elseBody(); |
| 4133 | const has_else_body = else_body.len != 0; | 4014 | const has_else_body = else_body.len != 0; |
| 4134 | if (has_else_body) { | 4015 | if (has_else_body) { |
| 4135 | try func.startBlock(.block, blocktype); | 4016 | try cg.startBlock(.block, blocktype); |
| 4136 | } | 4017 | } |
| 4137 | 4018 | ||
| 4138 | if (!is_sparse) { | 4019 | if (!is_sparse) { |
| ... | @@ -4140,25 +4021,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4140,25 +4021,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4140 | // The value 'target' represents the index into the table. | 4021 | // The value 'target' represents the index into the table. |
| 4141 | // Each index in the table represents a label to the branch | 4022 | // Each index in the table represents a label to the branch |
| 4142 | // to jump to. | 4023 | // to jump to. |
| 4143 | try func.startBlock(.block, blocktype); | 4024 | try cg.startBlock(.block, blocktype); |
| 4144 | try func.emitWValue(target); | 4025 | try cg.emitWValue(target); |
| 4145 | if (lowest < 0) { | 4026 | if (lowest < 0) { |
| 4146 | // since br_table works using indexes, starting from '0', we must ensure all values | 4027 | // since br_table works using indexes, starting from '0', we must ensure all values |
| 4147 | // we put inside, are atleast 0. | 4028 | // we put inside, are atleast 0. |
| 4148 | try func.addImm32(@bitCast(lowest * -1)); | 4029 | try cg.addImm32(@bitCast(lowest * -1)); |
| 4149 | try func.addTag(.i32_add); | 4030 | try cg.addTag(.i32_add); |
| 4150 | } else if (lowest > 0) { | 4031 | } else if (lowest > 0) { |
| 4151 | // make the index start from 0 by substracting the lowest value | 4032 | // make the index start from 0 by substracting the lowest value |
| 4152 | try func.addImm32(@bitCast(lowest)); | 4033 | try cg.addImm32(@bitCast(lowest)); |
| 4153 | try func.addTag(.i32_sub); | 4034 | try cg.addTag(.i32_sub); |
| 4154 | } | 4035 | } |
| 4155 | 4036 | ||
| 4156 | // Account for default branch so always add '1' | 4037 | // Account for default branch so always add '1' |
| 4157 | const depth = @as(u32, @intCast(highest - lowest + @intFromBool(has_else_body))) + 1; | 4038 | const depth = @as(u32, @intCast(highest - lowest + @intFromBool(has_else_body))) + 1; |
| 4158 | const jump_table: Mir.JumpTable = .{ .length = depth }; | 4039 | const jump_table: Mir.JumpTable = .{ .length = depth }; |
| 4159 | const table_extra_index = try func.addExtra(jump_table); | 4040 | const table_extra_index = try cg.addExtra(jump_table); |
| 4160 | try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | 4041 | try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); |
| 4161 | try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); | 4042 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth); |
| 4162 | var value = lowest; | 4043 | var value = lowest; |
| 4163 | while (value <= highest) : (value += 1) { | 4044 | while (value <= highest) : (value += 1) { |
| 4164 | // idx represents the branch we jump to | 4045 | // idx represents the branch we jump to |
| ... | @@ -4179,78 +4060,77 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4179,78 +4060,77 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4179 | // by using a jump table for this instead of if-else chains. | 4060 | // by using a jump table for this instead of if-else chains. |
| 4180 | break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .error_set) switch_br.cases_len else unreachable; | 4061 | break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .error_set) switch_br.cases_len else unreachable; |
| 4181 | }; | 4062 | }; |
| 4182 | func.mir_extra.appendAssumeCapacity(idx); | 4063 | cg.mir_extra.appendAssumeCapacity(idx); |
| 4183 | } else if (has_else_body) { | 4064 | } else if (has_else_body) { |
| 4184 | func.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch | 4065 | cg.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch |
| 4185 | } | 4066 | } |
| 4186 | try func.endBlock(); | 4067 | try cg.endBlock(); |
| 4187 | } | 4068 | } |
| 4188 | 4069 | ||
| 4189 | try func.branches.ensureUnusedCapacity(func.gpa, case_list.items.len + @intFromBool(has_else_body)); | 4070 | try cg.branches.ensureUnusedCapacity(cg.gpa, case_list.items.len + @intFromBool(has_else_body)); |
| 4190 | for (case_list.items, 0..) |case, index| { | 4071 | for (case_list.items, 0..) |case, index| { |
| 4191 | // when sparse, we use if/else-chain, so emit conditional checks | 4072 | // when sparse, we use if/else-chain, so emit conditional checks |
| 4192 | if (is_sparse) { | 4073 | if (is_sparse) { |
| 4193 | // for single value prong we can emit a simple condition | 4074 | // for single value prong we can emit a simple condition |
| 4194 | if (case.values.len == 1 and case.values[0] == .singular) { | 4075 | if (case.values.len == 1 and case.values[0] == .singular) { |
| 4195 | const val = try func.lowerConstant(case.values[0].singular.value, target_ty); | 4076 | const val = try cg.lowerConstant(case.values[0].singular.value, target_ty); |
| 4196 | // not equal, because we want to jump out of this block if it does not match the condition. | 4077 | // not equal, because we want to jump out of this block if it does not match the condition. |
| 4197 | _ = try func.cmp(target, val, target_ty, .neq); | 4078 | _ = try cg.cmp(target, val, target_ty, .neq); |
| 4198 | try func.addLabel(.br_if, 0); | 4079 | try cg.addLabel(.br_if, 0); |
| 4199 | } else { | 4080 | } else { |
| 4200 | // in multi-value prongs we must check if any prongs match the target value. | 4081 | // in multi-value prongs we must check if any prongs match the target value. |
| 4201 | try func.startBlock(.block, blocktype); | 4082 | try cg.startBlock(.block, blocktype); |
| 4202 | for (case.values) |value| { | 4083 | for (case.values) |value| { |
| 4203 | switch (value) { | 4084 | switch (value) { |
| 4204 | .singular => |single_val| { | 4085 | .singular => |single_val| { |
| 4205 | const val = try func.lowerConstant(single_val.value, target_ty); | 4086 | const val = try cg.lowerConstant(single_val.value, target_ty); |
| 4206 | _ = try func.cmp(target, val, target_ty, .eq); | 4087 | _ = try cg.cmp(target, val, target_ty, .eq); |
| 4207 | }, | 4088 | }, |
| 4208 | .range => |range| { | 4089 | .range => |range| { |
| 4209 | const min_val = try func.lowerConstant(range.min_value, target_ty); | 4090 | const min_val = try cg.lowerConstant(range.min_value, target_ty); |
| 4210 | const max_val = try func.lowerConstant(range.max_value, target_ty); | 4091 | const max_val = try cg.lowerConstant(range.max_value, target_ty); |
| 4211 | 4092 | ||
| 4212 | const gte = try func.cmp(target, min_val, target_ty, .gte); | 4093 | const gte = try cg.cmp(target, min_val, target_ty, .gte); |
| 4213 | const lte = try func.cmp(target, max_val, target_ty, .lte); | 4094 | const lte = try cg.cmp(target, max_val, target_ty, .lte); |
| 4214 | _ = try func.binOp(gte, lte, Type.bool, .@"and"); | 4095 | _ = try cg.binOp(gte, lte, Type.bool, .@"and"); |
| 4215 | }, | 4096 | }, |
| 4216 | } | 4097 | } |
| 4217 | try func.addLabel(.br_if, 0); | 4098 | try cg.addLabel(.br_if, 0); |
| 4218 | } | 4099 | } |
| 4219 | // value did not match any of the prong values | 4100 | // value did not match any of the prong values |
| 4220 | try func.addLabel(.br, 1); | 4101 | try cg.addLabel(.br, 1); |
| 4221 | try func.endBlock(); | 4102 | try cg.endBlock(); |
| 4222 | } | 4103 | } |
| 4223 | } | 4104 | } |
| 4224 | func.branches.appendAssumeCapacity(.{}); | 4105 | cg.branches.appendAssumeCapacity(.{}); |
| 4225 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len); | 4106 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths[index].len); |
| 4226 | defer { | 4107 | defer { |
| 4227 | var case_branch = func.branches.pop(); | 4108 | var case_branch = cg.branches.pop(); |
| 4228 | case_branch.deinit(func.gpa); | 4109 | case_branch.deinit(cg.gpa); |
| 4229 | } | 4110 | } |
| 4230 | try func.genBody(case.body); | 4111 | try cg.genBody(case.body); |
| 4231 | try func.endBlock(); | 4112 | try cg.endBlock(); |
| 4232 | } | 4113 | } |
| 4233 | 4114 | ||
| 4234 | if (has_else_body) { | 4115 | if (has_else_body) { |
| 4235 | func.branches.appendAssumeCapacity(.{}); | 4116 | cg.branches.appendAssumeCapacity(.{}); |
| 4236 | const else_deaths = liveness.deaths.len - 1; | 4117 | const else_deaths = liveness.deaths.len - 1; |
| 4237 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len); | 4118 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths[else_deaths].len); |
| 4238 | defer { | 4119 | defer { |
| 4239 | var else_branch = func.branches.pop(); | 4120 | var else_branch = cg.branches.pop(); |
| 4240 | else_branch.deinit(func.gpa); | 4121 | else_branch.deinit(cg.gpa); |
| 4241 | } | 4122 | } |
| 4242 | try func.genBody(else_body); | 4123 | try cg.genBody(else_body); |
| 4243 | try func.endBlock(); | 4124 | try cg.endBlock(); |
| 4244 | } | 4125 | } |
| 4245 | return func.finishAir(inst, .none, &.{}); | 4126 | return cg.finishAir(inst, .none, &.{}); |
| 4246 | } | 4127 | } |
| 4247 | 4128 | ||
| 4248 | fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void { | 4129 | fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void { |
| 4249 | const pt = func.pt; | 4130 | const zcu = cg.pt.zcu; |
| 4250 | const zcu = pt.zcu; | 4131 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4251 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4132 | const operand = try cg.resolveInst(un_op); |
| 4252 | const operand = try func.resolveInst(un_op); | 4133 | const err_union_ty = cg.typeOf(un_op); |
| 4253 | const err_union_ty = func.typeOf(un_op); | ||
| 4254 | const pl_ty = err_union_ty.errorUnionPayload(zcu); | 4134 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| 4255 | 4135 | ||
| 4256 | const result: WValue = result: { | 4136 | const result: WValue = result: { |
| ... | @@ -4262,57 +4142,55 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro | ... | @@ -4262,57 +4142,55 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro |
| 4262 | } | 4142 | } |
| 4263 | } | 4143 | } |
| 4264 | 4144 | ||
| 4265 | try func.emitWValue(operand); | 4145 | try cg.emitWValue(operand); |
| 4266 | if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4146 | if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4267 | try func.addMemArg(.i32_load16_u, .{ | 4147 | try cg.addMemArg(.i32_load16_u, .{ |
| 4268 | .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), | 4148 | .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), |
| 4269 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), | 4149 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), |
| 4270 | }); | 4150 | }); |
| 4271 | } | 4151 | } |
| 4272 | 4152 | ||
| 4273 | // Compare the error value with '0' | 4153 | // Compare the error value with '0' |
| 4274 | try func.addImm32(0); | 4154 | try cg.addImm32(0); |
| 4275 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 4155 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 4276 | break :result .stack; | 4156 | break :result .stack; |
| 4277 | }; | 4157 | }; |
| 4278 | return func.finishAir(inst, result, &.{un_op}); | 4158 | return cg.finishAir(inst, result, &.{un_op}); |
| 4279 | } | 4159 | } |
| 4280 | 4160 | ||
| 4281 | fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { | 4161 | fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { |
| 4282 | const pt = func.pt; | 4162 | const zcu = cg.pt.zcu; |
| 4283 | const zcu = pt.zcu; | 4163 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4284 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 4285 | 4164 | ||
| 4286 | const operand = try func.resolveInst(ty_op.operand); | 4165 | const operand = try cg.resolveInst(ty_op.operand); |
| 4287 | const op_ty = func.typeOf(ty_op.operand); | 4166 | const op_ty = cg.typeOf(ty_op.operand); |
| 4288 | const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty; | 4167 | const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty; |
| 4289 | const payload_ty = err_ty.errorUnionPayload(zcu); | 4168 | const payload_ty = err_ty.errorUnionPayload(zcu); |
| 4290 | 4169 | ||
| 4291 | const result: WValue = result: { | 4170 | const result: WValue = result: { |
| 4292 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4171 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4293 | if (op_is_ptr) { | 4172 | if (op_is_ptr) { |
| 4294 | break :result func.reuseOperand(ty_op.operand, operand); | 4173 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 4295 | } | 4174 | } |
| 4296 | break :result .none; | 4175 | break :result .none; |
| 4297 | } | 4176 | } |
| 4298 | 4177 | ||
| 4299 | const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))); | 4178 | const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))); |
| 4300 | if (op_is_ptr or isByRef(payload_ty, pt, func.target.*)) { | 4179 | if (op_is_ptr or isByRef(payload_ty, zcu, cg.target)) { |
| 4301 | break :result try func.buildPointerOffset(operand, pl_offset, .new); | 4180 | break :result try cg.buildPointerOffset(operand, pl_offset, .new); |
| 4302 | } | 4181 | } |
| 4303 | 4182 | ||
| 4304 | break :result try func.load(operand, payload_ty, pl_offset); | 4183 | break :result try cg.load(operand, payload_ty, pl_offset); |
| 4305 | }; | 4184 | }; |
| 4306 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4185 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4307 | } | 4186 | } |
| 4308 | 4187 | ||
| 4309 | fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { | 4188 | fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { |
| 4310 | const pt = func.pt; | 4189 | const zcu = cg.pt.zcu; |
| 4311 | const zcu = pt.zcu; | 4190 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4312 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 4313 | 4191 | ||
| 4314 | const operand = try func.resolveInst(ty_op.operand); | 4192 | const operand = try cg.resolveInst(ty_op.operand); |
| 4315 | const op_ty = func.typeOf(ty_op.operand); | 4193 | const op_ty = cg.typeOf(ty_op.operand); |
| 4316 | const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty; | 4194 | const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty; |
| 4317 | const payload_ty = err_ty.errorUnionPayload(zcu); | 4195 | const payload_ty = err_ty.errorUnionPayload(zcu); |
| 4318 | 4196 | ||
| ... | @@ -4322,104 +4200,101 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) | ... | @@ -4322,104 +4200,101 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) |
| 4322 | } | 4200 | } |
| 4323 | 4201 | ||
| 4324 | if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4202 | if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4325 | break :result func.reuseOperand(ty_op.operand, operand); | 4203 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 4326 | } | 4204 | } |
| 4327 | 4205 | ||
| 4328 | break :result try func.load(operand, Type.anyerror, @intCast(errUnionErrorOffset(payload_ty, zcu))); | 4206 | break :result try cg.load(operand, Type.anyerror, @intCast(errUnionErrorOffset(payload_ty, zcu))); |
| 4329 | }; | 4207 | }; |
| 4330 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4208 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4331 | } | 4209 | } |
| 4332 | 4210 | ||
| 4333 | fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4211 | fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4334 | const zcu = func.pt.zcu; | 4212 | const zcu = cg.pt.zcu; |
| 4335 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4213 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4336 | 4214 | ||
| 4337 | const operand = try func.resolveInst(ty_op.operand); | 4215 | const operand = try cg.resolveInst(ty_op.operand); |
| 4338 | const err_ty = func.typeOfIndex(inst); | 4216 | const err_ty = cg.typeOfIndex(inst); |
| 4339 | 4217 | ||
| 4340 | const pl_ty = func.typeOf(ty_op.operand); | 4218 | const pl_ty = cg.typeOf(ty_op.operand); |
| 4341 | const result = result: { | 4219 | const result = result: { |
| 4342 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4220 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4343 | break :result func.reuseOperand(ty_op.operand, operand); | 4221 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 4344 | } | 4222 | } |
| 4345 | 4223 | ||
| 4346 | const err_union = try func.allocStack(err_ty); | 4224 | const err_union = try cg.allocStack(err_ty); |
| 4347 | const payload_ptr = try func.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new); | 4225 | const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new); |
| 4348 | try func.store(payload_ptr, operand, pl_ty, 0); | 4226 | try cg.store(payload_ptr, operand, pl_ty, 0); |
| 4349 | 4227 | ||
| 4350 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. | 4228 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. |
| 4351 | try func.emitWValue(err_union); | 4229 | try cg.emitWValue(err_union); |
| 4352 | try func.addImm32(0); | 4230 | try cg.addImm32(0); |
| 4353 | const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); | 4231 | const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); |
| 4354 | try func.addMemArg(.i32_store16, .{ | 4232 | try cg.addMemArg(.i32_store16, .{ |
| 4355 | .offset = err_union.offset() + err_val_offset, | 4233 | .offset = err_union.offset() + err_val_offset, |
| 4356 | .alignment = 2, | 4234 | .alignment = 2, |
| 4357 | }); | 4235 | }); |
| 4358 | break :result err_union; | 4236 | break :result err_union; |
| 4359 | }; | 4237 | }; |
| 4360 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4238 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4361 | } | 4239 | } |
| 4362 | 4240 | ||
| 4363 | fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4241 | fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4364 | const pt = func.pt; | 4242 | const zcu = cg.pt.zcu; |
| 4365 | const zcu = pt.zcu; | 4243 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4366 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 4367 | 4244 | ||
| 4368 | const operand = try func.resolveInst(ty_op.operand); | 4245 | const operand = try cg.resolveInst(ty_op.operand); |
| 4369 | const err_ty = ty_op.ty.toType(); | 4246 | const err_ty = ty_op.ty.toType(); |
| 4370 | const pl_ty = err_ty.errorUnionPayload(zcu); | 4247 | const pl_ty = err_ty.errorUnionPayload(zcu); |
| 4371 | 4248 | ||
| 4372 | const result = result: { | 4249 | const result = result: { |
| 4373 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4250 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4374 | break :result func.reuseOperand(ty_op.operand, operand); | 4251 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 4375 | } | 4252 | } |
| 4376 | 4253 | ||
| 4377 | const err_union = try func.allocStack(err_ty); | 4254 | const err_union = try cg.allocStack(err_ty); |
| 4378 | // store error value | 4255 | // store error value |
| 4379 | try func.store(err_union, operand, Type.anyerror, @intCast(errUnionErrorOffset(pl_ty, zcu))); | 4256 | try cg.store(err_union, operand, Type.anyerror, @intCast(errUnionErrorOffset(pl_ty, zcu))); |
| 4380 | 4257 | ||
| 4381 | // write 'undefined' to the payload | 4258 | // write 'undefined' to the payload |
| 4382 | const payload_ptr = try func.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new); | 4259 | const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new); |
| 4383 | const len = @as(u32, @intCast(err_ty.errorUnionPayload(zcu).abiSize(zcu))); | 4260 | const len = @as(u32, @intCast(err_ty.errorUnionPayload(zcu).abiSize(zcu))); |
| 4384 | try func.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa }); | 4261 | try cg.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa }); |
| 4385 | 4262 | ||
| 4386 | break :result err_union; | 4263 | break :result err_union; |
| 4387 | }; | 4264 | }; |
| 4388 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4265 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4389 | } | 4266 | } |
| 4390 | 4267 | ||
| 4391 | fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4268 | fn airIntcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4392 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4269 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4393 | 4270 | ||
| 4394 | const ty = ty_op.ty.toType(); | 4271 | const ty = ty_op.ty.toType(); |
| 4395 | const operand = try func.resolveInst(ty_op.operand); | 4272 | const operand = try cg.resolveInst(ty_op.operand); |
| 4396 | const operand_ty = func.typeOf(ty_op.operand); | 4273 | const operand_ty = cg.typeOf(ty_op.operand); |
| 4397 | const pt = func.pt; | 4274 | const zcu = cg.pt.zcu; |
| 4398 | const zcu = pt.zcu; | ||
| 4399 | if (ty.zigTypeTag(zcu) == .vector or operand_ty.zigTypeTag(zcu) == .vector) { | 4275 | if (ty.zigTypeTag(zcu) == .vector or operand_ty.zigTypeTag(zcu) == .vector) { |
| 4400 | return func.fail("todo Wasm intcast for vectors", .{}); | 4276 | return cg.fail("todo Wasm intcast for vectors", .{}); |
| 4401 | } | 4277 | } |
| 4402 | if (ty.abiSize(zcu) > 16 or operand_ty.abiSize(zcu) > 16) { | 4278 | if (ty.abiSize(zcu) > 16 or operand_ty.abiSize(zcu) > 16) { |
| 4403 | return func.fail("todo Wasm intcast for bitsize > 128", .{}); | 4279 | return cg.fail("todo Wasm intcast for bitsize > 128", .{}); |
| 4404 | } | 4280 | } |
| 4405 | 4281 | ||
| 4406 | const op_bits = toWasmBits(@intCast(operand_ty.bitSize(zcu))).?; | 4282 | const op_bits = toWasmBits(@intCast(operand_ty.bitSize(zcu))).?; |
| 4407 | const wanted_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; | 4283 | const wanted_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; |
| 4408 | const result = if (op_bits == wanted_bits) | 4284 | const result = if (op_bits == wanted_bits) |
| 4409 | func.reuseOperand(ty_op.operand, operand) | 4285 | cg.reuseOperand(ty_op.operand, operand) |
| 4410 | else | 4286 | else |
| 4411 | try func.intcast(operand, operand_ty, ty); | 4287 | try cg.intcast(operand, operand_ty, ty); |
| 4412 | 4288 | ||
| 4413 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4289 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4414 | } | 4290 | } |
| 4415 | 4291 | ||
| 4416 | /// Upcasts or downcasts an integer based on the given and wanted types, | 4292 | /// Upcasts or downcasts an integer based on the given and wanted types, |
| 4417 | /// and stores the result in a new operand. | 4293 | /// and stores the result in a new operand. |
| 4418 | /// Asserts type's bitsize <= 128 | 4294 | /// Asserts type's bitsize <= 128 |
| 4419 | /// NOTE: May leave the result on the top of the stack. | 4295 | /// NOTE: May leave the result on the top of the stack. |
| 4420 | fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | 4296 | fn intcast(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 4421 | const pt = func.pt; | 4297 | const zcu = cg.pt.zcu; |
| 4422 | const zcu = pt.zcu; | ||
| 4423 | const given_bitsize = @as(u16, @intCast(given.bitSize(zcu))); | 4298 | const given_bitsize = @as(u16, @intCast(given.bitSize(zcu))); |
| 4424 | const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(zcu))); | 4299 | const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(zcu))); |
| 4425 | assert(given_bitsize <= 128); | 4300 | assert(given_bitsize <= 128); |
| ... | @@ -4432,470 +4307,456 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro | ... | @@ -4432,470 +4307,456 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4432 | } | 4307 | } |
| 4433 | 4308 | ||
| 4434 | if (op_bits == 64 and wanted_bits == 32) { | 4309 | if (op_bits == 64 and wanted_bits == 32) { |
| 4435 | try func.emitWValue(operand); | 4310 | try cg.emitWValue(operand); |
| 4436 | try func.addTag(.i32_wrap_i64); | 4311 | try cg.addTag(.i32_wrap_i64); |
| 4437 | return .stack; | 4312 | return .stack; |
| 4438 | } else if (op_bits == 32 and wanted_bits == 64) { | 4313 | } else if (op_bits == 32 and wanted_bits == 64) { |
| 4439 | try func.emitWValue(operand); | 4314 | try cg.emitWValue(operand); |
| 4440 | try func.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u); | 4315 | try cg.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u); |
| 4441 | return .stack; | 4316 | return .stack; |
| 4442 | } else if (wanted_bits == 128) { | 4317 | } else if (wanted_bits == 128) { |
| 4443 | // for 128bit integers we store the integer in the virtual stack, rather than a local | 4318 | // for 128bit integers we store the integer in the virtual stack, rather than a local |
| 4444 | const stack_ptr = try func.allocStack(wanted); | 4319 | const stack_ptr = try cg.allocStack(wanted); |
| 4445 | try func.emitWValue(stack_ptr); | 4320 | try cg.emitWValue(stack_ptr); |
| 4446 | 4321 | ||
| 4447 | // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it | 4322 | // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it |
| 4448 | // meaning less store operations are required. | 4323 | // meaning less store operations are required. |
| 4449 | const lhs = if (op_bits == 32) blk: { | 4324 | const lhs = if (op_bits == 32) blk: { |
| 4450 | const sign_ty = if (wanted.isSignedInt(zcu)) Type.i64 else Type.u64; | 4325 | const sign_ty = if (wanted.isSignedInt(zcu)) Type.i64 else Type.u64; |
| 4451 | break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty); | 4326 | break :blk try (try cg.intcast(operand, given, sign_ty)).toLocal(cg, sign_ty); |
| 4452 | } else operand; | 4327 | } else operand; |
| 4453 | 4328 | ||
| 4454 | // store lsb first | 4329 | // store lsb first |
| 4455 | try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset()); | 4330 | try cg.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset()); |
| 4456 | 4331 | ||
| 4457 | // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value | 4332 | // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value |
| 4458 | if (wanted.isSignedInt(zcu)) { | 4333 | if (wanted.isSignedInt(zcu)) { |
| 4459 | try func.emitWValue(stack_ptr); | 4334 | try cg.emitWValue(stack_ptr); |
| 4460 | const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); | 4335 | const shr = try cg.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); |
| 4461 | try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); | 4336 | try cg.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); |
| 4462 | } else { | 4337 | } else { |
| 4463 | // Ensure memory of msb is zero'd | 4338 | // Ensure memory of msb is zero'd |
| 4464 | try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); | 4339 | try cg.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); |
| 4465 | } | 4340 | } |
| 4466 | return stack_ptr; | 4341 | return stack_ptr; |
| 4467 | } else return func.load(operand, wanted, 0); | 4342 | } else return cg.load(operand, wanted, 0); |
| 4468 | } | 4343 | } |
| 4469 | 4344 | ||
| 4470 | fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { | 4345 | fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { |
| 4471 | const pt = func.pt; | 4346 | const zcu = cg.pt.zcu; |
| 4472 | const zcu = pt.zcu; | 4347 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4473 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4348 | const operand = try cg.resolveInst(un_op); |
| 4474 | const operand = try func.resolveInst(un_op); | ||
| 4475 | 4349 | ||
| 4476 | const op_ty = func.typeOf(un_op); | 4350 | const op_ty = cg.typeOf(un_op); |
| 4477 | const optional_ty = if (op_kind == .ptr) op_ty.childType(zcu) else op_ty; | 4351 | const optional_ty = if (op_kind == .ptr) op_ty.childType(zcu) else op_ty; |
| 4478 | const result = try func.isNull(operand, optional_ty, opcode); | 4352 | const result = try cg.isNull(operand, optional_ty, opcode); |
| 4479 | return func.finishAir(inst, result, &.{un_op}); | 4353 | return cg.finishAir(inst, result, &.{un_op}); |
| 4480 | } | 4354 | } |
| 4481 | 4355 | ||
| 4482 | /// For a given type and operand, checks if it's considered `null`. | 4356 | /// For a given type and operand, checks if it's considered `null`. |
| 4483 | /// NOTE: Leaves the result on the stack | 4357 | /// NOTE: Leaves the result on the stack |
| 4484 | fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { | 4358 | fn isNull(cg: *CodeGen, operand: WValue, optional_ty: Type, opcode: std.wasm.Opcode) InnerError!WValue { |
| 4485 | const pt = func.pt; | 4359 | const pt = cg.pt; |
| 4486 | const zcu = pt.zcu; | 4360 | const zcu = pt.zcu; |
| 4487 | try func.emitWValue(operand); | 4361 | try cg.emitWValue(operand); |
| 4488 | const payload_ty = optional_ty.optionalChild(zcu); | 4362 | const payload_ty = optional_ty.optionalChild(zcu); |
| 4489 | if (!optional_ty.optionalReprIsPayload(zcu)) { | 4363 | if (!optional_ty.optionalReprIsPayload(zcu)) { |
| 4490 | // When payload is zero-bits, we can treat operand as a value, rather than | 4364 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 4491 | // a pointer to the stack value | 4365 | // a pointer to the stack value |
| 4492 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4366 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4493 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { | 4367 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { |
| 4494 | return func.fail("Optional type {} too big to fit into stack frame", .{optional_ty.fmt(pt)}); | 4368 | return cg.fail("Optional type {} too big to fit into stack frame", .{optional_ty.fmt(pt)}); |
| 4495 | }; | 4369 | }; |
| 4496 | try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset() + offset, .alignment = 1 }); | 4370 | try cg.addMemArg(.i32_load8_u, .{ .offset = operand.offset() + offset, .alignment = 1 }); |
| 4497 | } | 4371 | } |
| 4498 | } else if (payload_ty.isSlice(zcu)) { | 4372 | } else if (payload_ty.isSlice(zcu)) { |
| 4499 | switch (func.arch()) { | 4373 | switch (cg.ptr_size) { |
| 4500 | .wasm32 => try func.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }), | 4374 | .wasm32 => try cg.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }), |
| 4501 | .wasm64 => try func.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }), | 4375 | .wasm64 => try cg.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }), |
| 4502 | else => unreachable, | ||
| 4503 | } | 4376 | } |
| 4504 | } | 4377 | } |
| 4505 | 4378 | ||
| 4506 | // Compare the null value with '0' | 4379 | // Compare the null value with '0' |
| 4507 | try func.addImm32(0); | 4380 | try cg.addImm32(0); |
| 4508 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 4381 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 4509 | 4382 | ||
| 4510 | return .stack; | 4383 | return .stack; |
| 4511 | } | 4384 | } |
| 4512 | 4385 | ||
| 4513 | fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4386 | fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4514 | const pt = func.pt; | 4387 | const zcu = cg.pt.zcu; |
| 4515 | const zcu = pt.zcu; | 4388 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4516 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4389 | const opt_ty = cg.typeOf(ty_op.operand); |
| 4517 | const opt_ty = func.typeOf(ty_op.operand); | 4390 | const payload_ty = cg.typeOfIndex(inst); |
| 4518 | const payload_ty = func.typeOfIndex(inst); | ||
| 4519 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4391 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4520 | return func.finishAir(inst, .none, &.{ty_op.operand}); | 4392 | return cg.finishAir(inst, .none, &.{ty_op.operand}); |
| 4521 | } | 4393 | } |
| 4522 | 4394 | ||
| 4523 | const result = result: { | 4395 | const result = result: { |
| 4524 | const operand = try func.resolveInst(ty_op.operand); | 4396 | const operand = try cg.resolveInst(ty_op.operand); |
| 4525 | if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand); | 4397 | if (opt_ty.optionalReprIsPayload(zcu)) break :result cg.reuseOperand(ty_op.operand, operand); |
| 4526 | 4398 | ||
| 4527 | if (isByRef(payload_ty, pt, func.target.*)) { | 4399 | if (isByRef(payload_ty, zcu, cg.target)) { |
| 4528 | break :result try func.buildPointerOffset(operand, 0, .new); | 4400 | break :result try cg.buildPointerOffset(operand, 0, .new); |
| 4529 | } | 4401 | } |
| 4530 | 4402 | ||
| 4531 | break :result try func.load(operand, payload_ty, 0); | 4403 | break :result try cg.load(operand, payload_ty, 0); |
| 4532 | }; | 4404 | }; |
| 4533 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4405 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4534 | } | 4406 | } |
| 4535 | 4407 | ||
| 4536 | fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4408 | fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4537 | const pt = func.pt; | 4409 | const zcu = cg.pt.zcu; |
| 4538 | const zcu = pt.zcu; | 4410 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4539 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4411 | const operand = try cg.resolveInst(ty_op.operand); |
| 4540 | const operand = try func.resolveInst(ty_op.operand); | 4412 | const opt_ty = cg.typeOf(ty_op.operand).childType(zcu); |
| 4541 | const opt_ty = func.typeOf(ty_op.operand).childType(zcu); | ||
| 4542 | 4413 | ||
| 4543 | const result = result: { | 4414 | const result = result: { |
| 4544 | const payload_ty = opt_ty.optionalChild(zcu); | 4415 | const payload_ty = opt_ty.optionalChild(zcu); |
| 4545 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or opt_ty.optionalReprIsPayload(zcu)) { | 4416 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or opt_ty.optionalReprIsPayload(zcu)) { |
| 4546 | break :result func.reuseOperand(ty_op.operand, operand); | 4417 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 4547 | } | 4418 | } |
| 4548 | 4419 | ||
| 4549 | break :result try func.buildPointerOffset(operand, 0, .new); | 4420 | break :result try cg.buildPointerOffset(operand, 0, .new); |
| 4550 | }; | 4421 | }; |
| 4551 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4422 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4552 | } | 4423 | } |
| 4553 | 4424 | ||
| 4554 | fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4425 | fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4555 | const pt = func.pt; | 4426 | const pt = cg.pt; |
| 4556 | const zcu = pt.zcu; | 4427 | const zcu = pt.zcu; |
| 4557 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4428 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4558 | const operand = try func.resolveInst(ty_op.operand); | 4429 | const operand = try cg.resolveInst(ty_op.operand); |
| 4559 | const opt_ty = func.typeOf(ty_op.operand).childType(zcu); | 4430 | const opt_ty = cg.typeOf(ty_op.operand).childType(zcu); |
| 4560 | const payload_ty = opt_ty.optionalChild(zcu); | 4431 | const payload_ty = opt_ty.optionalChild(zcu); |
| 4561 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4432 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4562 | return func.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); | 4433 | return cg.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); |
| 4563 | } | 4434 | } |
| 4564 | 4435 | ||
| 4565 | if (opt_ty.optionalReprIsPayload(zcu)) { | 4436 | if (opt_ty.optionalReprIsPayload(zcu)) { |
| 4566 | return func.finishAir(inst, operand, &.{ty_op.operand}); | 4437 | return cg.finishAir(inst, operand, &.{ty_op.operand}); |
| 4567 | } | 4438 | } |
| 4568 | 4439 | ||
| 4569 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { | 4440 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { |
| 4570 | return func.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(pt)}); | 4441 | return cg.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(pt)}); |
| 4571 | }; | 4442 | }; |
| 4572 | 4443 | ||
| 4573 | try func.emitWValue(operand); | 4444 | try cg.emitWValue(operand); |
| 4574 | try func.addImm32(1); | 4445 | try cg.addImm32(1); |
| 4575 | try func.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 }); | 4446 | try cg.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 }); |
| 4576 | 4447 | ||
| 4577 | const result = try func.buildPointerOffset(operand, 0, .new); | 4448 | const result = try cg.buildPointerOffset(operand, 0, .new); |
| 4578 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4449 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4579 | } | 4450 | } |
| 4580 | 4451 | ||
| 4581 | fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4452 | fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4582 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4453 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4583 | const payload_ty = func.typeOf(ty_op.operand); | 4454 | const payload_ty = cg.typeOf(ty_op.operand); |
| 4584 | const pt = func.pt; | 4455 | const pt = cg.pt; |
| 4585 | const zcu = pt.zcu; | 4456 | const zcu = pt.zcu; |
| 4586 | 4457 | ||
| 4587 | const result = result: { | 4458 | const result = result: { |
| 4588 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4459 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4589 | const non_null_bit = try func.allocStack(Type.u1); | 4460 | const non_null_bit = try cg.allocStack(Type.u1); |
| 4590 | try func.emitWValue(non_null_bit); | 4461 | try cg.emitWValue(non_null_bit); |
| 4591 | try func.addImm32(1); | 4462 | try cg.addImm32(1); |
| 4592 | try func.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 }); | 4463 | try cg.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 }); |
| 4593 | break :result non_null_bit; | 4464 | break :result non_null_bit; |
| 4594 | } | 4465 | } |
| 4595 | 4466 | ||
| 4596 | const operand = try func.resolveInst(ty_op.operand); | 4467 | const operand = try cg.resolveInst(ty_op.operand); |
| 4597 | const op_ty = func.typeOfIndex(inst); | 4468 | const op_ty = cg.typeOfIndex(inst); |
| 4598 | if (op_ty.optionalReprIsPayload(zcu)) { | 4469 | if (op_ty.optionalReprIsPayload(zcu)) { |
| 4599 | break :result func.reuseOperand(ty_op.operand, operand); | 4470 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 4600 | } | 4471 | } |
| 4601 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { | 4472 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { |
| 4602 | return func.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(pt)}); | 4473 | return cg.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(pt)}); |
| 4603 | }; | 4474 | }; |
| 4604 | 4475 | ||
| 4605 | // Create optional type, set the non-null bit, and store the operand inside the optional type | 4476 | // Create optional type, set the non-null bit, and store the operand inside the optional type |
| 4606 | const result_ptr = try func.allocStack(op_ty); | 4477 | const result_ptr = try cg.allocStack(op_ty); |
| 4607 | try func.emitWValue(result_ptr); | 4478 | try cg.emitWValue(result_ptr); |
| 4608 | try func.addImm32(1); | 4479 | try cg.addImm32(1); |
| 4609 | try func.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 }); | 4480 | try cg.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 }); |
| 4610 | 4481 | ||
| 4611 | const payload_ptr = try func.buildPointerOffset(result_ptr, 0, .new); | 4482 | const payload_ptr = try cg.buildPointerOffset(result_ptr, 0, .new); |
| 4612 | try func.store(payload_ptr, operand, payload_ty, 0); | 4483 | try cg.store(payload_ptr, operand, payload_ty, 0); |
| 4613 | break :result result_ptr; | 4484 | break :result result_ptr; |
| 4614 | }; | 4485 | }; |
| 4615 | 4486 | ||
| 4616 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4487 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4617 | } | 4488 | } |
| 4618 | 4489 | ||
| 4619 | fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4490 | fn airSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4620 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4491 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4621 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | 4492 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4622 | 4493 | ||
| 4623 | const lhs = try func.resolveInst(bin_op.lhs); | 4494 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 4624 | const rhs = try func.resolveInst(bin_op.rhs); | 4495 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 4625 | const slice_ty = func.typeOfIndex(inst); | 4496 | const slice_ty = cg.typeOfIndex(inst); |
| 4626 | 4497 | ||
| 4627 | const slice = try func.allocStack(slice_ty); | 4498 | const slice = try cg.allocStack(slice_ty); |
| 4628 | try func.store(slice, lhs, Type.usize, 0); | 4499 | try cg.store(slice, lhs, Type.usize, 0); |
| 4629 | try func.store(slice, rhs, Type.usize, func.ptrSize()); | 4500 | try cg.store(slice, rhs, Type.usize, cg.ptrSize()); |
| 4630 | 4501 | ||
| 4631 | return func.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs }); | 4502 | return cg.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs }); |
| 4632 | } | 4503 | } |
| 4633 | 4504 | ||
| 4634 | fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4505 | fn airSliceLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4635 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4506 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4636 | 4507 | ||
| 4637 | const operand = try func.resolveInst(ty_op.operand); | 4508 | const operand = try cg.resolveInst(ty_op.operand); |
| 4638 | return func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand}); | 4509 | return cg.finishAir(inst, try cg.sliceLen(operand), &.{ty_op.operand}); |
| 4639 | } | 4510 | } |
| 4640 | 4511 | ||
| 4641 | fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4512 | fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4642 | const pt = func.pt; | 4513 | const zcu = cg.pt.zcu; |
| 4643 | const zcu = pt.zcu; | 4514 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4644 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 4645 | 4515 | ||
| 4646 | const slice_ty = func.typeOf(bin_op.lhs); | 4516 | const slice_ty = cg.typeOf(bin_op.lhs); |
| 4647 | const slice = try func.resolveInst(bin_op.lhs); | 4517 | const slice = try cg.resolveInst(bin_op.lhs); |
| 4648 | const index = try func.resolveInst(bin_op.rhs); | 4518 | const index = try cg.resolveInst(bin_op.rhs); |
| 4649 | const elem_ty = slice_ty.childType(zcu); | 4519 | const elem_ty = slice_ty.childType(zcu); |
| 4650 | const elem_size = elem_ty.abiSize(zcu); | 4520 | const elem_size = elem_ty.abiSize(zcu); |
| 4651 | 4521 | ||
| 4652 | // load pointer onto stack | 4522 | // load pointer onto stack |
| 4653 | _ = try func.load(slice, Type.usize, 0); | 4523 | _ = try cg.load(slice, Type.usize, 0); |
| 4654 | 4524 | ||
| 4655 | // calculate index into slice | 4525 | // calculate index into slice |
| 4656 | try func.emitWValue(index); | 4526 | try cg.emitWValue(index); |
| 4657 | try func.addImm32(@intCast(elem_size)); | 4527 | try cg.addImm32(@intCast(elem_size)); |
| 4658 | try func.addTag(.i32_mul); | 4528 | try cg.addTag(.i32_mul); |
| 4659 | try func.addTag(.i32_add); | 4529 | try cg.addTag(.i32_add); |
| 4660 | 4530 | ||
| 4661 | const elem_result = if (isByRef(elem_ty, pt, func.target.*)) | 4531 | const elem_result = if (isByRef(elem_ty, zcu, cg.target)) |
| 4662 | .stack | 4532 | .stack |
| 4663 | else | 4533 | else |
| 4664 | try func.load(.stack, elem_ty, 0); | 4534 | try cg.load(.stack, elem_ty, 0); |
| 4665 | 4535 | ||
| 4666 | return func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs }); | 4536 | return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs }); |
| 4667 | } | 4537 | } |
| 4668 | 4538 | ||
| 4669 | fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4539 | fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4670 | const pt = func.pt; | 4540 | const zcu = cg.pt.zcu; |
| 4671 | const zcu = pt.zcu; | 4541 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4672 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4542 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4673 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 4674 | 4543 | ||
| 4675 | const elem_ty = ty_pl.ty.toType().childType(zcu); | 4544 | const elem_ty = ty_pl.ty.toType().childType(zcu); |
| 4676 | const elem_size = elem_ty.abiSize(zcu); | 4545 | const elem_size = elem_ty.abiSize(zcu); |
| 4677 | 4546 | ||
| 4678 | const slice = try func.resolveInst(bin_op.lhs); | 4547 | const slice = try cg.resolveInst(bin_op.lhs); |
| 4679 | const index = try func.resolveInst(bin_op.rhs); | 4548 | const index = try cg.resolveInst(bin_op.rhs); |
| 4680 | 4549 | ||
| 4681 | _ = try func.load(slice, Type.usize, 0); | 4550 | _ = try cg.load(slice, Type.usize, 0); |
| 4682 | 4551 | ||
| 4683 | // calculate index into slice | 4552 | // calculate index into slice |
| 4684 | try func.emitWValue(index); | 4553 | try cg.emitWValue(index); |
| 4685 | try func.addImm32(@intCast(elem_size)); | 4554 | try cg.addImm32(@intCast(elem_size)); |
| 4686 | try func.addTag(.i32_mul); | 4555 | try cg.addTag(.i32_mul); |
| 4687 | try func.addTag(.i32_add); | 4556 | try cg.addTag(.i32_add); |
| 4688 | 4557 | ||
| 4689 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 4558 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 4690 | } | 4559 | } |
| 4691 | 4560 | ||
| 4692 | fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4561 | fn airSlicePtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4693 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4562 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4694 | const operand = try func.resolveInst(ty_op.operand); | 4563 | const operand = try cg.resolveInst(ty_op.operand); |
| 4695 | return func.finishAir(inst, try func.slicePtr(operand), &.{ty_op.operand}); | 4564 | return cg.finishAir(inst, try cg.slicePtr(operand), &.{ty_op.operand}); |
| 4696 | } | 4565 | } |
| 4697 | 4566 | ||
| 4698 | fn slicePtr(func: *CodeGen, operand: WValue) InnerError!WValue { | 4567 | fn slicePtr(cg: *CodeGen, operand: WValue) InnerError!WValue { |
| 4699 | const ptr = try func.load(operand, Type.usize, 0); | 4568 | const ptr = try cg.load(operand, Type.usize, 0); |
| 4700 | return ptr.toLocal(func, Type.usize); | 4569 | return ptr.toLocal(cg, Type.usize); |
| 4701 | } | 4570 | } |
| 4702 | 4571 | ||
| 4703 | fn sliceLen(func: *CodeGen, operand: WValue) InnerError!WValue { | 4572 | fn sliceLen(cg: *CodeGen, operand: WValue) InnerError!WValue { |
| 4704 | const len = try func.load(operand, Type.usize, func.ptrSize()); | 4573 | const len = try cg.load(operand, Type.usize, cg.ptrSize()); |
| 4705 | return len.toLocal(func, Type.usize); | 4574 | return len.toLocal(cg, Type.usize); |
| 4706 | } | 4575 | } |
| 4707 | 4576 | ||
| 4708 | fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4577 | fn airTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4709 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4578 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4710 | 4579 | ||
| 4711 | const operand = try func.resolveInst(ty_op.operand); | 4580 | const operand = try cg.resolveInst(ty_op.operand); |
| 4712 | const wanted_ty: Type = ty_op.ty.toType(); | 4581 | const wanted_ty: Type = ty_op.ty.toType(); |
| 4713 | const op_ty = func.typeOf(ty_op.operand); | 4582 | const op_ty = cg.typeOf(ty_op.operand); |
| 4714 | const pt = func.pt; | 4583 | const zcu = cg.pt.zcu; |
| 4715 | const zcu = pt.zcu; | ||
| 4716 | 4584 | ||
| 4717 | if (wanted_ty.zigTypeTag(zcu) == .vector or op_ty.zigTypeTag(zcu) == .vector) { | 4585 | if (wanted_ty.zigTypeTag(zcu) == .vector or op_ty.zigTypeTag(zcu) == .vector) { |
| 4718 | return func.fail("TODO: trunc for vectors", .{}); | 4586 | return cg.fail("TODO: trunc for vectors", .{}); |
| 4719 | } | 4587 | } |
| 4720 | 4588 | ||
| 4721 | const result = if (op_ty.bitSize(zcu) == wanted_ty.bitSize(zcu)) | 4589 | const result = if (op_ty.bitSize(zcu) == wanted_ty.bitSize(zcu)) |
| 4722 | func.reuseOperand(ty_op.operand, operand) | 4590 | cg.reuseOperand(ty_op.operand, operand) |
| 4723 | else | 4591 | else |
| 4724 | try func.trunc(operand, wanted_ty, op_ty); | 4592 | try cg.trunc(operand, wanted_ty, op_ty); |
| 4725 | 4593 | ||
| 4726 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4594 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 4727 | } | 4595 | } |
| 4728 | 4596 | ||
| 4729 | /// Truncates a given operand to a given type, discarding any overflown bits. | 4597 | /// Truncates a given operand to a given type, discarding any overflown bits. |
| 4730 | /// NOTE: Resulting value is left on the stack. | 4598 | /// NOTE: Resulting value is left on the stack. |
| 4731 | fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue { | 4599 | fn trunc(cg: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue { |
| 4732 | const pt = func.pt; | 4600 | const zcu = cg.pt.zcu; |
| 4733 | const zcu = pt.zcu; | ||
| 4734 | const given_bits = @as(u16, @intCast(given_ty.bitSize(zcu))); | 4601 | const given_bits = @as(u16, @intCast(given_ty.bitSize(zcu))); |
| 4735 | if (toWasmBits(given_bits) == null) { | 4602 | if (toWasmBits(given_bits) == null) { |
| 4736 | return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{given_bits}); | 4603 | return cg.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{given_bits}); |
| 4737 | } | 4604 | } |
| 4738 | 4605 | ||
| 4739 | var result = try func.intcast(operand, given_ty, wanted_ty); | 4606 | var result = try cg.intcast(operand, given_ty, wanted_ty); |
| 4740 | const wanted_bits = @as(u16, @intCast(wanted_ty.bitSize(zcu))); | 4607 | const wanted_bits = @as(u16, @intCast(wanted_ty.bitSize(zcu))); |
| 4741 | const wasm_bits = toWasmBits(wanted_bits).?; | 4608 | const wasm_bits = toWasmBits(wanted_bits).?; |
| 4742 | if (wasm_bits != wanted_bits) { | 4609 | if (wasm_bits != wanted_bits) { |
| 4743 | result = try func.wrapOperand(result, wanted_ty); | 4610 | result = try cg.wrapOperand(result, wanted_ty); |
| 4744 | } | 4611 | } |
| 4745 | return result; | 4612 | return result; |
| 4746 | } | 4613 | } |
| 4747 | 4614 | ||
| 4748 | fn airIntFromBool(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4615 | fn airIntFromBool(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4749 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4616 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4750 | const operand = try func.resolveInst(un_op); | 4617 | const operand = try cg.resolveInst(un_op); |
| 4751 | const result = func.reuseOperand(un_op, operand); | 4618 | const result = cg.reuseOperand(un_op, operand); |
| 4752 | 4619 | ||
| 4753 | return func.finishAir(inst, result, &.{un_op}); | 4620 | return cg.finishAir(inst, result, &.{un_op}); |
| 4754 | } | 4621 | } |
| 4755 | 4622 | ||
| 4756 | fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4623 | fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4757 | const pt = func.pt; | 4624 | const zcu = cg.pt.zcu; |
| 4758 | const zcu = pt.zcu; | 4625 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4759 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 4760 | 4626 | ||
| 4761 | const operand = try func.resolveInst(ty_op.operand); | 4627 | const operand = try cg.resolveInst(ty_op.operand); |
| 4762 | const array_ty = func.typeOf(ty_op.operand).childType(zcu); | 4628 | const array_ty = cg.typeOf(ty_op.operand).childType(zcu); |
| 4763 | const slice_ty = ty_op.ty.toType(); | 4629 | const slice_ty = ty_op.ty.toType(); |
| 4764 | 4630 | ||
| 4765 | // create a slice on the stack | 4631 | // create a slice on the stack |
| 4766 | const slice_local = try func.allocStack(slice_ty); | 4632 | const slice_local = try cg.allocStack(slice_ty); |
| 4767 | 4633 | ||
| 4768 | // store the array ptr in the slice | 4634 | // store the array ptr in the slice |
| 4769 | if (array_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4635 | if (array_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4770 | try func.store(slice_local, operand, Type.usize, 0); | 4636 | try cg.store(slice_local, operand, Type.usize, 0); |
| 4771 | } | 4637 | } |
| 4772 | 4638 | ||
| 4773 | // store the length of the array in the slice | 4639 | // store the length of the array in the slice |
| 4774 | const array_len: u32 = @intCast(array_ty.arrayLen(zcu)); | 4640 | const array_len: u32 = @intCast(array_ty.arrayLen(zcu)); |
| 4775 | try func.store(slice_local, .{ .imm32 = array_len }, Type.usize, func.ptrSize()); | 4641 | try cg.store(slice_local, .{ .imm32 = array_len }, Type.usize, cg.ptrSize()); |
| 4776 | 4642 | ||
| 4777 | return func.finishAir(inst, slice_local, &.{ty_op.operand}); | 4643 | return cg.finishAir(inst, slice_local, &.{ty_op.operand}); |
| 4778 | } | 4644 | } |
| 4779 | 4645 | ||
| 4780 | fn airIntFromPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4646 | fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4781 | const pt = func.pt; | 4647 | const zcu = cg.pt.zcu; |
| 4782 | const zcu = pt.zcu; | 4648 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4783 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4649 | const operand = try cg.resolveInst(un_op); |
| 4784 | const operand = try func.resolveInst(un_op); | 4650 | const ptr_ty = cg.typeOf(un_op); |
| 4785 | const ptr_ty = func.typeOf(un_op); | ||
| 4786 | const result = if (ptr_ty.isSlice(zcu)) | 4651 | const result = if (ptr_ty.isSlice(zcu)) |
| 4787 | try func.slicePtr(operand) | 4652 | try cg.slicePtr(operand) |
| 4788 | else switch (operand) { | 4653 | else switch (operand) { |
| 4789 | // for stack offset, return a pointer to this offset. | 4654 | // for stack offset, return a pointer to this offset. |
| 4790 | .stack_offset => try func.buildPointerOffset(operand, 0, .new), | 4655 | .stack_offset => try cg.buildPointerOffset(operand, 0, .new), |
| 4791 | else => func.reuseOperand(un_op, operand), | 4656 | else => cg.reuseOperand(un_op, operand), |
| 4792 | }; | 4657 | }; |
| 4793 | return func.finishAir(inst, result, &.{un_op}); | 4658 | return cg.finishAir(inst, result, &.{un_op}); |
| 4794 | } | 4659 | } |
| 4795 | 4660 | ||
| 4796 | fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4661 | fn airPtrElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4797 | const pt = func.pt; | 4662 | const zcu = cg.pt.zcu; |
| 4798 | const zcu = pt.zcu; | 4663 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4799 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 4800 | 4664 | ||
| 4801 | const ptr_ty = func.typeOf(bin_op.lhs); | 4665 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 4802 | const ptr = try func.resolveInst(bin_op.lhs); | 4666 | const ptr = try cg.resolveInst(bin_op.lhs); |
| 4803 | const index = try func.resolveInst(bin_op.rhs); | 4667 | const index = try cg.resolveInst(bin_op.rhs); |
| 4804 | const elem_ty = ptr_ty.childType(zcu); | 4668 | const elem_ty = ptr_ty.childType(zcu); |
| 4805 | const elem_size = elem_ty.abiSize(zcu); | 4669 | const elem_size = elem_ty.abiSize(zcu); |
| 4806 | 4670 | ||
| 4807 | // load pointer onto the stack | 4671 | // load pointer onto the stack |
| 4808 | if (ptr_ty.isSlice(zcu)) { | 4672 | if (ptr_ty.isSlice(zcu)) { |
| 4809 | _ = try func.load(ptr, Type.usize, 0); | 4673 | _ = try cg.load(ptr, Type.usize, 0); |
| 4810 | } else { | 4674 | } else { |
| 4811 | try func.lowerToStack(ptr); | 4675 | try cg.lowerToStack(ptr); |
| 4812 | } | 4676 | } |
| 4813 | 4677 | ||
| 4814 | // calculate index into slice | 4678 | // calculate index into slice |
| 4815 | try func.emitWValue(index); | 4679 | try cg.emitWValue(index); |
| 4816 | try func.addImm32(@intCast(elem_size)); | 4680 | try cg.addImm32(@intCast(elem_size)); |
| 4817 | try func.addTag(.i32_mul); | 4681 | try cg.addTag(.i32_mul); |
| 4818 | try func.addTag(.i32_add); | 4682 | try cg.addTag(.i32_add); |
| 4819 | 4683 | ||
| 4820 | const elem_result = if (isByRef(elem_ty, pt, func.target.*)) | 4684 | const elem_result = if (isByRef(elem_ty, zcu, cg.target)) |
| 4821 | .stack | 4685 | .stack |
| 4822 | else | 4686 | else |
| 4823 | try func.load(.stack, elem_ty, 0); | 4687 | try cg.load(.stack, elem_ty, 0); |
| 4824 | 4688 | ||
| 4825 | return func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs }); | 4689 | return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs }); |
| 4826 | } | 4690 | } |
| 4827 | 4691 | ||
| 4828 | fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4692 | fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4829 | const pt = func.pt; | 4693 | const zcu = cg.pt.zcu; |
| 4830 | const zcu = pt.zcu; | 4694 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4831 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4695 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4832 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 4833 | 4696 | ||
| 4834 | const ptr_ty = func.typeOf(bin_op.lhs); | 4697 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 4835 | const elem_ty = ty_pl.ty.toType().childType(zcu); | 4698 | const elem_ty = ty_pl.ty.toType().childType(zcu); |
| 4836 | const elem_size = elem_ty.abiSize(zcu); | 4699 | const elem_size = elem_ty.abiSize(zcu); |
| 4837 | 4700 | ||
| 4838 | const ptr = try func.resolveInst(bin_op.lhs); | 4701 | const ptr = try cg.resolveInst(bin_op.lhs); |
| 4839 | const index = try func.resolveInst(bin_op.rhs); | 4702 | const index = try cg.resolveInst(bin_op.rhs); |
| 4840 | 4703 | ||
| 4841 | // load pointer onto the stack | 4704 | // load pointer onto the stack |
| 4842 | if (ptr_ty.isSlice(zcu)) { | 4705 | if (ptr_ty.isSlice(zcu)) { |
| 4843 | _ = try func.load(ptr, Type.usize, 0); | 4706 | _ = try cg.load(ptr, Type.usize, 0); |
| 4844 | } else { | 4707 | } else { |
| 4845 | try func.lowerToStack(ptr); | 4708 | try cg.lowerToStack(ptr); |
| 4846 | } | 4709 | } |
| 4847 | 4710 | ||
| 4848 | // calculate index into ptr | 4711 | // calculate index into ptr |
| 4849 | try func.emitWValue(index); | 4712 | try cg.emitWValue(index); |
| 4850 | try func.addImm32(@intCast(elem_size)); | 4713 | try cg.addImm32(@intCast(elem_size)); |
| 4851 | try func.addTag(.i32_mul); | 4714 | try cg.addTag(.i32_mul); |
| 4852 | try func.addTag(.i32_add); | 4715 | try cg.addTag(.i32_add); |
| 4853 | 4716 | ||
| 4854 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 4717 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 4855 | } | 4718 | } |
| 4856 | 4719 | ||
| 4857 | fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 4720 | fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 4858 | const pt = func.pt; | 4721 | const zcu = cg.pt.zcu; |
| 4859 | const zcu = pt.zcu; | 4722 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4860 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4723 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4861 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 4862 | 4724 | ||
| 4863 | const ptr = try func.resolveInst(bin_op.lhs); | 4725 | const ptr = try cg.resolveInst(bin_op.lhs); |
| 4864 | const offset = try func.resolveInst(bin_op.rhs); | 4726 | const offset = try cg.resolveInst(bin_op.rhs); |
| 4865 | const ptr_ty = func.typeOf(bin_op.lhs); | 4727 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 4866 | const pointee_ty = switch (ptr_ty.ptrSize(zcu)) { | 4728 | const pointee_ty = switch (ptr_ty.ptrSize(zcu)) { |
| 4867 | .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type | 4729 | .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type |
| 4868 | else => ptr_ty.childType(zcu), | 4730 | else => ptr_ty.childType(zcu), |
| 4869 | }; | 4731 | }; |
| 4870 | 4732 | ||
| 4871 | const valtype = typeToValtype(Type.usize, pt, func.target.*); | 4733 | const valtype = typeToValtype(Type.usize, zcu, cg.target); |
| 4872 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); | 4734 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); |
| 4873 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); | 4735 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); |
| 4874 | 4736 | ||
| 4875 | try func.lowerToStack(ptr); | 4737 | try cg.lowerToStack(ptr); |
| 4876 | try func.emitWValue(offset); | 4738 | try cg.emitWValue(offset); |
| 4877 | try func.addImm32(@intCast(pointee_ty.abiSize(zcu))); | 4739 | try cg.addImm32(@intCast(pointee_ty.abiSize(zcu))); |
| 4878 | try func.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); | 4740 | try cg.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); |
| 4879 | try func.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode)); | 4741 | try cg.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode)); |
| 4880 | 4742 | ||
| 4881 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 4743 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 4882 | } | 4744 | } |
| 4883 | 4745 | ||
| 4884 | fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { | 4746 | fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { |
| 4885 | const pt = func.pt; | 4747 | const zcu = cg.pt.zcu; |
| 4886 | const zcu = pt.zcu; | ||
| 4887 | if (safety) { | 4748 | if (safety) { |
| 4888 | // TODO if the value is undef, write 0xaa bytes to dest | 4749 | // TODO if the value is undef, write 0xaa bytes to dest |
| 4889 | } else { | 4750 | } else { |
| 4890 | // TODO if the value is undef, don't lower this instruction | 4751 | // TODO if the value is undef, don't lower this instruction |
| 4891 | } | 4752 | } |
| 4892 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 4753 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4893 | 4754 | ||
| 4894 | const ptr = try func.resolveInst(bin_op.lhs); | 4755 | const ptr = try cg.resolveInst(bin_op.lhs); |
| 4895 | const ptr_ty = func.typeOf(bin_op.lhs); | 4756 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 4896 | const value = try func.resolveInst(bin_op.rhs); | 4757 | const value = try cg.resolveInst(bin_op.rhs); |
| 4897 | const len = switch (ptr_ty.ptrSize(zcu)) { | 4758 | const len = switch (ptr_ty.ptrSize(zcu)) { |
| 4898 | .Slice => try func.sliceLen(ptr), | 4759 | .Slice => try cg.sliceLen(ptr), |
| 4899 | .One => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }), | 4760 | .One => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }), |
| 4900 | .C, .Many => unreachable, | 4761 | .C, .Many => unreachable, |
| 4901 | }; | 4762 | }; |
| ... | @@ -4905,27 +4766,27 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void | ... | @@ -4905,27 +4766,27 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 4905 | else | 4766 | else |
| 4906 | ptr_ty.childType(zcu); | 4767 | ptr_ty.childType(zcu); |
| 4907 | 4768 | ||
| 4908 | const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty); | 4769 | const dst_ptr = try cg.sliceOrArrayPtr(ptr, ptr_ty); |
| 4909 | try func.memset(elem_ty, dst_ptr, len, value); | 4770 | try cg.memset(elem_ty, dst_ptr, len, value); |
| 4910 | 4771 | ||
| 4911 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 4772 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 4912 | } | 4773 | } |
| 4913 | 4774 | ||
| 4914 | /// Sets a region of memory at `ptr` to the value of `value` | 4775 | /// Sets a region of memory at `ptr` to the value of `value` |
| 4915 | /// When the user has enabled the bulk_memory feature, we lower | 4776 | /// When the user has enabled the bulk_memory feature, we lower |
| 4916 | /// this to wasm's memset instruction. When the feature is not present, | 4777 | /// this to wasm's memset instruction. When the feature is not present, |
| 4917 | /// we implement it manually. | 4778 | /// we implement it manually. |
| 4918 | fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void { | 4779 | fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void { |
| 4919 | const pt = func.pt; | 4780 | const zcu = cg.pt.zcu; |
| 4920 | const abi_size = @as(u32, @intCast(elem_ty.abiSize(pt.zcu))); | 4781 | const abi_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); |
| 4921 | 4782 | ||
| 4922 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. | 4783 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| 4923 | // If not, we lower it ourselves. | 4784 | // If not, we lower it ourselves. |
| 4924 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) { | 4785 | if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) { |
| 4925 | try func.lowerToStack(ptr); | 4786 | try cg.lowerToStack(ptr); |
| 4926 | try func.emitWValue(value); | 4787 | try cg.emitWValue(value); |
| 4927 | try func.emitWValue(len); | 4788 | try cg.emitWValue(len); |
| 4928 | try func.addExtended(.memory_fill); | 4789 | try cg.addExtended(.memory_fill); |
| 4929 | return; | 4790 | return; |
| 4930 | } | 4791 | } |
| 4931 | 4792 | ||
| ... | @@ -4933,100 +4794,95 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue | ... | @@ -4933,100 +4794,95 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue |
| 4933 | .imm32 => |val| .{ .imm32 = val * abi_size }, | 4794 | .imm32 => |val| .{ .imm32 = val * abi_size }, |
| 4934 | .imm64 => |val| .{ .imm64 = val * abi_size }, | 4795 | .imm64 => |val| .{ .imm64 = val * abi_size }, |
| 4935 | else => if (abi_size != 1) blk: { | 4796 | else => if (abi_size != 1) blk: { |
| 4936 | const new_len = try func.ensureAllocLocal(Type.usize); | 4797 | const new_len = try cg.ensureAllocLocal(Type.usize); |
| 4937 | try func.emitWValue(len); | 4798 | try cg.emitWValue(len); |
| 4938 | switch (func.arch()) { | 4799 | switch (cg.ptr_size) { |
| 4939 | .wasm32 => { | 4800 | .wasm32 => { |
| 4940 | try func.emitWValue(.{ .imm32 = abi_size }); | 4801 | try cg.emitWValue(.{ .imm32 = abi_size }); |
| 4941 | try func.addTag(.i32_mul); | 4802 | try cg.addTag(.i32_mul); |
| 4942 | }, | 4803 | }, |
| 4943 | .wasm64 => { | 4804 | .wasm64 => { |
| 4944 | try func.emitWValue(.{ .imm64 = abi_size }); | 4805 | try cg.emitWValue(.{ .imm64 = abi_size }); |
| 4945 | try func.addTag(.i64_mul); | 4806 | try cg.addTag(.i64_mul); |
| 4946 | }, | 4807 | }, |
| 4947 | else => unreachable, | ||
| 4948 | } | 4808 | } |
| 4949 | try func.addLabel(.local_set, new_len.local.value); | 4809 | try cg.addLocal(.local_set, new_len.local.value); |
| 4950 | break :blk new_len; | 4810 | break :blk new_len; |
| 4951 | } else len, | 4811 | } else len, |
| 4952 | }; | 4812 | }; |
| 4953 | 4813 | ||
| 4954 | var end_ptr = try func.allocLocal(Type.usize); | 4814 | var end_ptr = try cg.allocLocal(Type.usize); |
| 4955 | defer end_ptr.free(func); | 4815 | defer end_ptr.free(cg); |
| 4956 | var new_ptr = try func.buildPointerOffset(ptr, 0, .new); | 4816 | var new_ptr = try cg.buildPointerOffset(ptr, 0, .new); |
| 4957 | defer new_ptr.free(func); | 4817 | defer new_ptr.free(cg); |
| 4958 | 4818 | ||
| 4959 | // get the loop conditional: if current pointer address equals final pointer's address | 4819 | // get the loop conditional: if current pointer address equals final pointer's address |
| 4960 | try func.lowerToStack(ptr); | 4820 | try cg.lowerToStack(ptr); |
| 4961 | try func.emitWValue(final_len); | 4821 | try cg.emitWValue(final_len); |
| 4962 | switch (func.arch()) { | 4822 | switch (cg.ptr_size) { |
| 4963 | .wasm32 => try func.addTag(.i32_add), | 4823 | .wasm32 => try cg.addTag(.i32_add), |
| 4964 | .wasm64 => try func.addTag(.i64_add), | 4824 | .wasm64 => try cg.addTag(.i64_add), |
| 4965 | else => unreachable, | ||
| 4966 | } | 4825 | } |
| 4967 | try func.addLabel(.local_set, end_ptr.local.value); | 4826 | try cg.addLocal(.local_set, end_ptr.local.value); |
| 4968 | 4827 | ||
| 4969 | // outer block to jump to when loop is done | 4828 | // outer block to jump to when loop is done |
| 4970 | try func.startBlock(.block, wasm.block_empty); | 4829 | try cg.startBlock(.block, .empty); |
| 4971 | try func.startBlock(.loop, wasm.block_empty); | 4830 | try cg.startBlock(.loop, .empty); |
| 4972 | 4831 | ||
| 4973 | // check for condition for loop end | 4832 | // check for condition for loop end |
| 4974 | try func.emitWValue(new_ptr); | 4833 | try cg.emitWValue(new_ptr); |
| 4975 | try func.emitWValue(end_ptr); | 4834 | try cg.emitWValue(end_ptr); |
| 4976 | switch (func.arch()) { | 4835 | switch (cg.ptr_size) { |
| 4977 | .wasm32 => try func.addTag(.i32_eq), | 4836 | .wasm32 => try cg.addTag(.i32_eq), |
| 4978 | .wasm64 => try func.addTag(.i64_eq), | 4837 | .wasm64 => try cg.addTag(.i64_eq), |
| 4979 | else => unreachable, | ||
| 4980 | } | 4838 | } |
| 4981 | try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished) | 4839 | try cg.addLabel(.br_if, 1); // jump out of loop into outer block (finished) |
| 4982 | 4840 | ||
| 4983 | // store the value at the current position of the pointer | 4841 | // store the value at the current position of the pointer |
| 4984 | try func.store(new_ptr, value, elem_ty, 0); | 4842 | try cg.store(new_ptr, value, elem_ty, 0); |
| 4985 | 4843 | ||
| 4986 | // move the pointer to the next element | 4844 | // move the pointer to the next element |
| 4987 | try func.emitWValue(new_ptr); | 4845 | try cg.emitWValue(new_ptr); |
| 4988 | switch (func.arch()) { | 4846 | switch (cg.ptr_size) { |
| 4989 | .wasm32 => { | 4847 | .wasm32 => { |
| 4990 | try func.emitWValue(.{ .imm32 = abi_size }); | 4848 | try cg.emitWValue(.{ .imm32 = abi_size }); |
| 4991 | try func.addTag(.i32_add); | 4849 | try cg.addTag(.i32_add); |
| 4992 | }, | 4850 | }, |
| 4993 | .wasm64 => { | 4851 | .wasm64 => { |
| 4994 | try func.emitWValue(.{ .imm64 = abi_size }); | 4852 | try cg.emitWValue(.{ .imm64 = abi_size }); |
| 4995 | try func.addTag(.i64_add); | 4853 | try cg.addTag(.i64_add); |
| 4996 | }, | 4854 | }, |
| 4997 | else => unreachable, | ||
| 4998 | } | 4855 | } |
| 4999 | try func.addLabel(.local_set, new_ptr.local.value); | 4856 | try cg.addLocal(.local_set, new_ptr.local.value); |
| 5000 | 4857 | ||
| 5001 | // end of loop | 4858 | // end of loop |
| 5002 | try func.addLabel(.br, 0); // jump to start of loop | 4859 | try cg.addLabel(.br, 0); // jump to start of loop |
| 5003 | try func.endBlock(); | 4860 | try cg.endBlock(); |
| 5004 | try func.endBlock(); | 4861 | try cg.endBlock(); |
| 5005 | } | 4862 | } |
| 5006 | 4863 | ||
| 5007 | fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4864 | fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5008 | const pt = func.pt; | 4865 | const zcu = cg.pt.zcu; |
| 5009 | const zcu = pt.zcu; | 4866 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 5010 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 5011 | 4867 | ||
| 5012 | const array_ty = func.typeOf(bin_op.lhs); | 4868 | const array_ty = cg.typeOf(bin_op.lhs); |
| 5013 | const array = try func.resolveInst(bin_op.lhs); | 4869 | const array = try cg.resolveInst(bin_op.lhs); |
| 5014 | const index = try func.resolveInst(bin_op.rhs); | 4870 | const index = try cg.resolveInst(bin_op.rhs); |
| 5015 | const elem_ty = array_ty.childType(zcu); | 4871 | const elem_ty = array_ty.childType(zcu); |
| 5016 | const elem_size = elem_ty.abiSize(zcu); | 4872 | const elem_size = elem_ty.abiSize(zcu); |
| 5017 | 4873 | ||
| 5018 | if (isByRef(array_ty, pt, func.target.*)) { | 4874 | if (isByRef(array_ty, zcu, cg.target)) { |
| 5019 | try func.lowerToStack(array); | 4875 | try cg.lowerToStack(array); |
| 5020 | try func.emitWValue(index); | 4876 | try cg.emitWValue(index); |
| 5021 | try func.addImm32(@intCast(elem_size)); | 4877 | try cg.addImm32(@intCast(elem_size)); |
| 5022 | try func.addTag(.i32_mul); | 4878 | try cg.addTag(.i32_mul); |
| 5023 | try func.addTag(.i32_add); | 4879 | try cg.addTag(.i32_add); |
| 5024 | } else { | 4880 | } else { |
| 5025 | std.debug.assert(array_ty.zigTypeTag(zcu) == .vector); | 4881 | assert(array_ty.zigTypeTag(zcu) == .vector); |
| 5026 | 4882 | ||
| 5027 | switch (index) { | 4883 | switch (index) { |
| 5028 | inline .imm32, .imm64 => |lane| { | 4884 | inline .imm32, .imm64 => |lane| { |
| 5029 | const opcode: wasm.SimdOpcode = switch (elem_ty.bitSize(zcu)) { | 4885 | 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, | 4886 | 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, | 4887 | 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, | 4888 | 32 => if (elem_ty.isInt(zcu)) .i32x4_extract_lane else .f32x4_extract_lane, |
| ... | @@ -5034,174 +4890,185 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5034,174 +4890,185 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5034 | else => unreachable, | 4890 | else => unreachable, |
| 5035 | }; | 4891 | }; |
| 5036 | 4892 | ||
| 5037 | var operands = [_]u32{ std.wasm.simdOpcode(opcode), @as(u8, @intCast(lane)) }; | 4893 | var operands = [_]u32{ @intFromEnum(opcode), @as(u8, @intCast(lane)) }; |
| 5038 | 4894 | ||
| 5039 | try func.emitWValue(array); | 4895 | try cg.emitWValue(array); |
| 5040 | 4896 | ||
| 5041 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | 4897 | const extra_index = cg.extraLen(); |
| 5042 | try func.mir_extra.appendSlice(func.gpa, &operands); | 4898 | try cg.mir_extra.appendSlice(cg.gpa, &operands); |
| 5043 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 4899 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 5044 | 4900 | ||
| 5045 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 4901 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 5046 | }, | 4902 | }, |
| 5047 | else => { | 4903 | else => { |
| 5048 | const stack_vec = try func.allocStack(array_ty); | 4904 | const stack_vec = try cg.allocStack(array_ty); |
| 5049 | try func.store(stack_vec, array, array_ty, 0); | 4905 | try cg.store(stack_vec, array, array_ty, 0); |
| 5050 | 4906 | ||
| 5051 | // Is a non-unrolled vector (v128) | 4907 | // Is a non-unrolled vector (v128) |
| 5052 | try func.lowerToStack(stack_vec); | 4908 | try cg.lowerToStack(stack_vec); |
| 5053 | try func.emitWValue(index); | 4909 | try cg.emitWValue(index); |
| 5054 | try func.addImm32(@intCast(elem_size)); | 4910 | try cg.addImm32(@intCast(elem_size)); |
| 5055 | try func.addTag(.i32_mul); | 4911 | try cg.addTag(.i32_mul); |
| 5056 | try func.addTag(.i32_add); | 4912 | try cg.addTag(.i32_add); |
| 5057 | }, | 4913 | }, |
| 5058 | } | 4914 | } |
| 5059 | } | 4915 | } |
| 5060 | 4916 | ||
| 5061 | const elem_result = if (isByRef(elem_ty, pt, func.target.*)) | 4917 | const elem_result = if (isByRef(elem_ty, zcu, cg.target)) |
| 5062 | .stack | 4918 | .stack |
| 5063 | else | 4919 | else |
| 5064 | try func.load(.stack, elem_ty, 0); | 4920 | try cg.load(.stack, elem_ty, 0); |
| 5065 | 4921 | ||
| 5066 | return func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs }); | 4922 | return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs }); |
| 5067 | } | 4923 | } |
| 5068 | 4924 | ||
| 5069 | fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4925 | fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5070 | const pt = func.pt; | 4926 | const zcu = cg.pt.zcu; |
| 5071 | const zcu = pt.zcu; | 4927 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5072 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 5073 | 4928 | ||
| 5074 | const operand = try func.resolveInst(ty_op.operand); | 4929 | const operand = try cg.resolveInst(ty_op.operand); |
| 5075 | const op_ty = func.typeOf(ty_op.operand); | 4930 | const op_ty = cg.typeOf(ty_op.operand); |
| 5076 | const op_bits = op_ty.floatBits(func.target.*); | 4931 | const op_bits = op_ty.floatBits(cg.target.*); |
| 5077 | 4932 | ||
| 5078 | const dest_ty = func.typeOfIndex(inst); | 4933 | const dest_ty = cg.typeOfIndex(inst); |
| 5079 | const dest_info = dest_ty.intInfo(zcu); | 4934 | const dest_info = dest_ty.intInfo(zcu); |
| 5080 | 4935 | ||
| 5081 | if (dest_info.bits > 128) { | 4936 | if (dest_info.bits > 128) { |
| 5082 | return func.fail("TODO: intFromFloat for integers/floats with bitsize {}", .{dest_info.bits}); | 4937 | return cg.fail("TODO: intFromFloat for integers/floats with bitsize {}", .{dest_info.bits}); |
| 5083 | } | 4938 | } |
| 5084 | 4939 | ||
| 5085 | if ((op_bits != 32 and op_bits != 64) or dest_info.bits > 64) { | 4940 | if ((op_bits != 32 and op_bits != 64) or dest_info.bits > 64) { |
| 5086 | const dest_bitsize = if (dest_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); | 4941 | const dest_bitsize = if (dest_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); |
| 5087 | 4942 | ||
| 5088 | var fn_name_buf: [16]u8 = undefined; | 4943 | const intrinsic = switch (dest_info.signedness) { |
| 5089 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__fix{s}{s}f{s}i", .{ | 4944 | inline .signed, .unsigned => |ct_s| switch (op_bits) { |
| 5090 | switch (dest_info.signedness) { | 4945 | inline 16, 32, 64, 80, 128 => |ct_op_bits| switch (dest_bitsize) { |
| 5091 | .signed => "", | 4946 | inline 32, 64, 128 => |ct_dest_bits| @field( |
| 5092 | .unsigned => "uns", | 4947 | Mir.Intrinsic, |
| 4948 | "__fix" ++ switch (ct_s) { | ||
| 4949 | .signed => "", | ||
| 4950 | .unsigned => "uns", | ||
| 4951 | } ++ | ||
| 4952 | compilerRtFloatAbbrev(ct_op_bits) ++ "f" ++ | ||
| 4953 | compilerRtIntAbbrev(ct_dest_bits) ++ "i", | ||
| 4954 | ), | ||
| 4955 | else => unreachable, | ||
| 4956 | }, | ||
| 4957 | else => unreachable, | ||
| 5093 | }, | 4958 | }, |
| 5094 | target_util.compilerRtFloatAbbrev(op_bits), | 4959 | }; |
| 5095 | target_util.compilerRtIntAbbrev(dest_bitsize), | 4960 | const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); |
| 5096 | }) catch unreachable; | 4961 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5097 | |||
| 5098 | const result = try func.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); | ||
| 5099 | return func.finishAir(inst, result, &.{ty_op.operand}); | ||
| 5100 | } | 4962 | } |
| 5101 | 4963 | ||
| 5102 | try func.emitWValue(operand); | 4964 | try cg.emitWValue(operand); |
| 5103 | const op = buildOpcode(.{ | 4965 | const op = buildOpcode(.{ |
| 5104 | .op = .trunc, | 4966 | .op = .trunc, |
| 5105 | .valtype1 = typeToValtype(dest_ty, pt, func.target.*), | 4967 | .valtype1 = typeToValtype(dest_ty, zcu, cg.target), |
| 5106 | .valtype2 = typeToValtype(op_ty, pt, func.target.*), | 4968 | .valtype2 = typeToValtype(op_ty, zcu, cg.target), |
| 5107 | .signedness = dest_info.signedness, | 4969 | .signedness = dest_info.signedness, |
| 5108 | }); | 4970 | }); |
| 5109 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); | 4971 | try cg.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| 5110 | const result = try func.wrapOperand(.stack, dest_ty); | 4972 | const result = try cg.wrapOperand(.stack, dest_ty); |
| 5111 | return func.finishAir(inst, result, &.{ty_op.operand}); | 4973 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5112 | } | 4974 | } |
| 5113 | 4975 | ||
| 5114 | fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4976 | fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5115 | const pt = func.pt; | 4977 | const zcu = cg.pt.zcu; |
| 5116 | const zcu = pt.zcu; | 4978 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5117 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 5118 | 4979 | ||
| 5119 | const operand = try func.resolveInst(ty_op.operand); | 4980 | const operand = try cg.resolveInst(ty_op.operand); |
| 5120 | const op_ty = func.typeOf(ty_op.operand); | 4981 | const op_ty = cg.typeOf(ty_op.operand); |
| 5121 | const op_info = op_ty.intInfo(zcu); | 4982 | const op_info = op_ty.intInfo(zcu); |
| 5122 | 4983 | ||
| 5123 | const dest_ty = func.typeOfIndex(inst); | 4984 | const dest_ty = cg.typeOfIndex(inst); |
| 5124 | const dest_bits = dest_ty.floatBits(func.target.*); | 4985 | const dest_bits = dest_ty.floatBits(cg.target.*); |
| 5125 | 4986 | ||
| 5126 | if (op_info.bits > 128) { | 4987 | if (op_info.bits > 128) { |
| 5127 | return func.fail("TODO: floatFromInt for integers/floats with bitsize {d} bits", .{op_info.bits}); | 4988 | return cg.fail("TODO: floatFromInt for integers/floats with bitsize {d} bits", .{op_info.bits}); |
| 5128 | } | 4989 | } |
| 5129 | 4990 | ||
| 5130 | if (op_info.bits > 64 or (dest_bits > 64 or dest_bits < 32)) { | 4991 | if (op_info.bits > 64 or (dest_bits > 64 or dest_bits < 32)) { |
| 5131 | const op_bitsize = if (op_info.bits <= 16) 16 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); | 4992 | const op_bitsize = if (op_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); |
| 5132 | 4993 | ||
| 5133 | var fn_name_buf: [16]u8 = undefined; | 4994 | const intrinsic = switch (op_info.signedness) { |
| 5134 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__float{s}{s}i{s}f", .{ | 4995 | inline .signed, .unsigned => |ct_s| switch (op_bitsize) { |
| 5135 | switch (op_info.signedness) { | 4996 | inline 32, 64, 128 => |ct_int_bits| switch (dest_bits) { |
| 5136 | .signed => "", | 4997 | inline 16, 32, 64, 80, 128 => |ct_float_bits| @field( |
| 5137 | .unsigned => "un", | 4998 | Mir.Intrinsic, |
| 4999 | "__float" ++ switch (ct_s) { | ||
| 5000 | .signed => "", | ||
| 5001 | .unsigned => "un", | ||
| 5002 | } ++ | ||
| 5003 | compilerRtIntAbbrev(ct_int_bits) ++ "i" ++ | ||
| 5004 | compilerRtFloatAbbrev(ct_float_bits) ++ "f", | ||
| 5005 | ), | ||
| 5006 | else => unreachable, | ||
| 5007 | }, | ||
| 5008 | else => unreachable, | ||
| 5138 | }, | 5009 | }, |
| 5139 | target_util.compilerRtIntAbbrev(op_bitsize), | 5010 | }; |
| 5140 | target_util.compilerRtFloatAbbrev(dest_bits), | ||
| 5141 | }) catch unreachable; | ||
| 5142 | 5011 | ||
| 5143 | const result = try func.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); | 5012 | const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); |
| 5144 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5013 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5145 | } | 5014 | } |
| 5146 | 5015 | ||
| 5147 | try func.emitWValue(operand); | 5016 | try cg.emitWValue(operand); |
| 5148 | const op = buildOpcode(.{ | 5017 | const op = buildOpcode(.{ |
| 5149 | .op = .convert, | 5018 | .op = .convert, |
| 5150 | .valtype1 = typeToValtype(dest_ty, pt, func.target.*), | 5019 | .valtype1 = typeToValtype(dest_ty, zcu, cg.target), |
| 5151 | .valtype2 = typeToValtype(op_ty, pt, func.target.*), | 5020 | .valtype2 = typeToValtype(op_ty, zcu, cg.target), |
| 5152 | .signedness = op_info.signedness, | 5021 | .signedness = op_info.signedness, |
| 5153 | }); | 5022 | }); |
| 5154 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); | 5023 | try cg.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| 5155 | 5024 | ||
| 5156 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 5025 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 5157 | } | 5026 | } |
| 5158 | 5027 | ||
| 5159 | fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5028 | fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5160 | const pt = func.pt; | 5029 | const zcu = cg.pt.zcu; |
| 5161 | const zcu = pt.zcu; | 5030 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5162 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5031 | const operand = try cg.resolveInst(ty_op.operand); |
| 5163 | const operand = try func.resolveInst(ty_op.operand); | 5032 | const ty = cg.typeOfIndex(inst); |
| 5164 | const ty = func.typeOfIndex(inst); | ||
| 5165 | const elem_ty = ty.childType(zcu); | 5033 | const elem_ty = ty.childType(zcu); |
| 5166 | 5034 | ||
| 5167 | if (determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct) blk: { | 5035 | if (determineSimdStoreStrategy(ty, zcu, cg.target) == .direct) blk: { |
| 5168 | switch (operand) { | 5036 | switch (operand) { |
| 5169 | // when the operand lives in the linear memory section, we can directly | 5037 | // when the operand lives in the linear memory section, we can directly |
| 5170 | // load and splat the value at once. Meaning we do not first have to load | 5038 | // load and splat the value at once. Meaning we do not first have to load |
| 5171 | // the scalar value onto the stack. | 5039 | // the scalar value onto the stack. |
| 5172 | .stack_offset, .memory, .memory_offset => { | 5040 | .stack_offset, .nav_ref, .uav_ref => { |
| 5173 | const opcode = switch (elem_ty.bitSize(zcu)) { | 5041 | const opcode = switch (elem_ty.bitSize(zcu)) { |
| 5174 | 8 => std.wasm.simdOpcode(.v128_load8_splat), | 5042 | 8 => @intFromEnum(std.wasm.SimdOpcode.v128_load8_splat), |
| 5175 | 16 => std.wasm.simdOpcode(.v128_load16_splat), | 5043 | 16 => @intFromEnum(std.wasm.SimdOpcode.v128_load16_splat), |
| 5176 | 32 => std.wasm.simdOpcode(.v128_load32_splat), | 5044 | 32 => @intFromEnum(std.wasm.SimdOpcode.v128_load32_splat), |
| 5177 | 64 => std.wasm.simdOpcode(.v128_load64_splat), | 5045 | 64 => @intFromEnum(std.wasm.SimdOpcode.v128_load64_splat), |
| 5178 | else => break :blk, // Cannot make use of simd-instructions | 5046 | else => break :blk, // Cannot make use of simd-instructions |
| 5179 | }; | 5047 | }; |
| 5180 | try func.emitWValue(operand); | 5048 | try cg.emitWValue(operand); |
| 5181 | // TODO: Add helper functions for simd opcodes | 5049 | const extra_index: u32 = cg.extraLen(); |
| 5182 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | ||
| 5183 | // stores as := opcode, offset, alignment (opcode::memarg) | 5050 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 5184 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ | 5051 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 5185 | opcode, | 5052 | opcode, |
| 5186 | operand.offset(), | 5053 | operand.offset(), |
| 5187 | @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?), | 5054 | @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?), |
| 5188 | }); | 5055 | }); |
| 5189 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 5056 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 5190 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 5057 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 5191 | }, | 5058 | }, |
| 5192 | .local => { | 5059 | .local => { |
| 5193 | const opcode = switch (elem_ty.bitSize(zcu)) { | 5060 | const opcode = switch (elem_ty.bitSize(zcu)) { |
| 5194 | 8 => std.wasm.simdOpcode(.i8x16_splat), | 5061 | 8 => @intFromEnum(std.wasm.SimdOpcode.i8x16_splat), |
| 5195 | 16 => std.wasm.simdOpcode(.i16x8_splat), | 5062 | 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), | 5063 | 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), | 5064 | 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-instructions | 5065 | else => break :blk, // Cannot make use of simd-instructions |
| 5199 | }; | 5066 | }; |
| 5200 | try func.emitWValue(operand); | 5067 | try cg.emitWValue(operand); |
| 5201 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | 5068 | const extra_index = cg.extraLen(); |
| 5202 | try func.mir_extra.append(func.gpa, opcode); | 5069 | try cg.mir_extra.append(cg.gpa, opcode); |
| 5203 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 5070 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 5204 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 5071 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 5205 | }, | 5072 | }, |
| 5206 | else => unreachable, | 5073 | else => unreachable, |
| 5207 | } | 5074 | } |
| ... | @@ -5209,38 +5076,38 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5209,38 +5076,38 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5209 | const elem_size = elem_ty.bitSize(zcu); | 5076 | const elem_size = elem_ty.bitSize(zcu); |
| 5210 | const vector_len = @as(usize, @intCast(ty.vectorLen(zcu))); | 5077 | const vector_len = @as(usize, @intCast(ty.vectorLen(zcu))); |
| 5211 | if ((!std.math.isPowerOfTwo(elem_size) or elem_size % 8 != 0) and vector_len > 1) { | 5078 | if ((!std.math.isPowerOfTwo(elem_size) or elem_size % 8 != 0) and vector_len > 1) { |
| 5212 | return func.fail("TODO: WebAssembly `@splat` for arbitrary element bitsize {d}", .{elem_size}); | 5079 | return cg.fail("TODO: WebAssembly `@splat` for arbitrary element bitsize {d}", .{elem_size}); |
| 5213 | } | 5080 | } |
| 5214 | 5081 | ||
| 5215 | const result = try func.allocStack(ty); | 5082 | const result = try cg.allocStack(ty); |
| 5216 | const elem_byte_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); | 5083 | const elem_byte_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); |
| 5217 | var index: usize = 0; | 5084 | var index: usize = 0; |
| 5218 | var offset: u32 = 0; | 5085 | var offset: u32 = 0; |
| 5219 | while (index < vector_len) : (index += 1) { | 5086 | while (index < vector_len) : (index += 1) { |
| 5220 | try func.store(result, operand, elem_ty, offset); | 5087 | try cg.store(result, operand, elem_ty, offset); |
| 5221 | offset += elem_byte_size; | 5088 | offset += elem_byte_size; |
| 5222 | } | 5089 | } |
| 5223 | 5090 | ||
| 5224 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5091 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5225 | } | 5092 | } |
| 5226 | 5093 | ||
| 5227 | fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5094 | fn airSelect(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5228 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 5095 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 5229 | const operand = try func.resolveInst(pl_op.operand); | 5096 | const operand = try cg.resolveInst(pl_op.operand); |
| 5230 | 5097 | ||
| 5231 | _ = operand; | 5098 | _ = operand; |
| 5232 | return func.fail("TODO: Implement wasm airSelect", .{}); | 5099 | return cg.fail("TODO: Implement wasm airSelect", .{}); |
| 5233 | } | 5100 | } |
| 5234 | 5101 | ||
| 5235 | fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5102 | fn airShuffle(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5236 | const pt = func.pt; | 5103 | const pt = cg.pt; |
| 5237 | const zcu = pt.zcu; | 5104 | const zcu = pt.zcu; |
| 5238 | const inst_ty = func.typeOfIndex(inst); | 5105 | const inst_ty = cg.typeOfIndex(inst); |
| 5239 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5106 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5240 | const extra = func.air.extraData(Air.Shuffle, ty_pl.payload).data; | 5107 | const extra = cg.air.extraData(Air.Shuffle, ty_pl.payload).data; |
| 5241 | 5108 | ||
| 5242 | const a = try func.resolveInst(extra.a); | 5109 | const a = try cg.resolveInst(extra.a); |
| 5243 | const b = try func.resolveInst(extra.b); | 5110 | const b = try cg.resolveInst(extra.b); |
| 5244 | const mask = Value.fromInterned(extra.mask); | 5111 | const mask = Value.fromInterned(extra.mask); |
| 5245 | const mask_len = extra.mask_len; | 5112 | const mask_len = extra.mask_len; |
| 5246 | 5113 | ||
| ... | @@ -5248,26 +5115,26 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5248,26 +5115,26 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5248 | const elem_size = child_ty.abiSize(zcu); | 5115 | const elem_size = child_ty.abiSize(zcu); |
| 5249 | 5116 | ||
| 5250 | // TODO: One of them could be by ref; handle in loop | 5117 | // TODO: One of them could be by ref; handle in loop |
| 5251 | if (isByRef(func.typeOf(extra.a), pt, func.target.*) or isByRef(inst_ty, pt, func.target.*)) { | 5118 | if (isByRef(cg.typeOf(extra.a), zcu, cg.target) or isByRef(inst_ty, zcu, cg.target)) { |
| 5252 | const result = try func.allocStack(inst_ty); | 5119 | const result = try cg.allocStack(inst_ty); |
| 5253 | 5120 | ||
| 5254 | for (0..mask_len) |index| { | 5121 | for (0..mask_len) |index| { |
| 5255 | const value = (try mask.elemValue(pt, index)).toSignedInt(zcu); | 5122 | const value = (try mask.elemValue(pt, index)).toSignedInt(zcu); |
| 5256 | 5123 | ||
| 5257 | try func.emitWValue(result); | 5124 | try cg.emitWValue(result); |
| 5258 | 5125 | ||
| 5259 | const loaded = if (value >= 0) | 5126 | const loaded = if (value >= 0) |
| 5260 | try func.load(a, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * value))) | 5127 | try cg.load(a, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * value))) |
| 5261 | else | 5128 | else |
| 5262 | try func.load(b, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * ~value))); | 5129 | try cg.load(b, child_ty, @as(u32, @intCast(@as(i64, @intCast(elem_size)) * ~value))); |
| 5263 | 5130 | ||
| 5264 | try func.store(.stack, loaded, child_ty, result.stack_offset.value + @as(u32, @intCast(elem_size)) * @as(u32, @intCast(index))); | 5131 | try cg.store(.stack, loaded, child_ty, result.stack_offset.value + @as(u32, @intCast(elem_size)) * @as(u32, @intCast(index))); |
| 5265 | } | 5132 | } |
| 5266 | 5133 | ||
| 5267 | return func.finishAir(inst, result, &.{ extra.a, extra.b }); | 5134 | return cg.finishAir(inst, result, &.{ extra.a, extra.b }); |
| 5268 | } else { | 5135 | } else { |
| 5269 | var operands = [_]u32{ | 5136 | var operands = [_]u32{ |
| 5270 | std.wasm.simdOpcode(.i8x16_shuffle), | 5137 | @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle), |
| 5271 | } ++ [1]u32{undefined} ** 4; | 5138 | } ++ [1]u32{undefined} ** 4; |
| 5272 | 5139 | ||
| 5273 | var lanes = mem.asBytes(operands[1..]); | 5140 | var lanes = mem.asBytes(operands[1..]); |
| ... | @@ -5283,91 +5150,91 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5283,91 +5150,91 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5283 | } | 5150 | } |
| 5284 | } | 5151 | } |
| 5285 | 5152 | ||
| 5286 | try func.emitWValue(a); | 5153 | try cg.emitWValue(a); |
| 5287 | try func.emitWValue(b); | 5154 | try cg.emitWValue(b); |
| 5288 | 5155 | ||
| 5289 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | 5156 | const extra_index = cg.extraLen(); |
| 5290 | try func.mir_extra.appendSlice(func.gpa, &operands); | 5157 | try cg.mir_extra.appendSlice(cg.gpa, &operands); |
| 5291 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 5158 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 5292 | 5159 | ||
| 5293 | return func.finishAir(inst, .stack, &.{ extra.a, extra.b }); | 5160 | return cg.finishAir(inst, .stack, &.{ extra.a, extra.b }); |
| 5294 | } | 5161 | } |
| 5295 | } | 5162 | } |
| 5296 | 5163 | ||
| 5297 | fn airReduce(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5164 | fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5298 | const reduce = func.air.instructions.items(.data)[@intFromEnum(inst)].reduce; | 5165 | const reduce = cg.air.instructions.items(.data)[@intFromEnum(inst)].reduce; |
| 5299 | const operand = try func.resolveInst(reduce.operand); | 5166 | const operand = try cg.resolveInst(reduce.operand); |
| 5300 | 5167 | ||
| 5301 | _ = operand; | 5168 | _ = operand; |
| 5302 | return func.fail("TODO: Implement wasm airReduce", .{}); | 5169 | return cg.fail("TODO: Implement wasm airReduce", .{}); |
| 5303 | } | 5170 | } |
| 5304 | 5171 | ||
| 5305 | fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5172 | fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5306 | const pt = func.pt; | 5173 | const pt = cg.pt; |
| 5307 | const zcu = pt.zcu; | 5174 | const zcu = pt.zcu; |
| 5308 | const ip = &zcu.intern_pool; | 5175 | const ip = &zcu.intern_pool; |
| 5309 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5176 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5310 | const result_ty = func.typeOfIndex(inst); | 5177 | const result_ty = cg.typeOfIndex(inst); |
| 5311 | const len = @as(usize, @intCast(result_ty.arrayLen(zcu))); | 5178 | const len = @as(usize, @intCast(result_ty.arrayLen(zcu))); |
| 5312 | const elements = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[ty_pl.payload..][0..len])); | 5179 | const elements = @as([]const Air.Inst.Ref, @ptrCast(cg.air.extra[ty_pl.payload..][0..len])); |
| 5313 | 5180 | ||
| 5314 | const result: WValue = result_value: { | 5181 | const result: WValue = result_value: { |
| 5315 | switch (result_ty.zigTypeTag(zcu)) { | 5182 | switch (result_ty.zigTypeTag(zcu)) { |
| 5316 | .array => { | 5183 | .array => { |
| 5317 | const result = try func.allocStack(result_ty); | 5184 | const result = try cg.allocStack(result_ty); |
| 5318 | const elem_ty = result_ty.childType(zcu); | 5185 | const elem_ty = result_ty.childType(zcu); |
| 5319 | const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); | 5186 | const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); |
| 5320 | const sentinel = if (result_ty.sentinel(zcu)) |sent| blk: { | 5187 | const sentinel = if (result_ty.sentinel(zcu)) |sent| blk: { |
| 5321 | break :blk try func.lowerConstant(sent, elem_ty); | 5188 | break :blk try cg.lowerConstant(sent, elem_ty); |
| 5322 | } else null; | 5189 | } else null; |
| 5323 | 5190 | ||
| 5324 | // When the element type is by reference, we must copy the entire | 5191 | // When the element type is by reference, we must copy the entire |
| 5325 | // value. It is therefore safer to move the offset pointer and store | 5192 | // value. It is therefore safer to move the offset pointer and store |
| 5326 | // each value individually, instead of using store offsets. | 5193 | // each value individually, instead of using store offsets. |
| 5327 | if (isByRef(elem_ty, pt, func.target.*)) { | 5194 | if (isByRef(elem_ty, zcu, cg.target)) { |
| 5328 | // copy stack pointer into a temporary local, which is | 5195 | // copy stack pointer into a temporary local, which is |
| 5329 | // moved for each element to store each value in the right position. | 5196 | // moved for each element to store each value in the right position. |
| 5330 | const offset = try func.buildPointerOffset(result, 0, .new); | 5197 | const offset = try cg.buildPointerOffset(result, 0, .new); |
| 5331 | for (elements, 0..) |elem, elem_index| { | 5198 | for (elements, 0..) |elem, elem_index| { |
| 5332 | const elem_val = try func.resolveInst(elem); | 5199 | const elem_val = try cg.resolveInst(elem); |
| 5333 | try func.store(offset, elem_val, elem_ty, 0); | 5200 | try cg.store(offset, elem_val, elem_ty, 0); |
| 5334 | 5201 | ||
| 5335 | if (elem_index < elements.len - 1 and sentinel == null) { | 5202 | if (elem_index < elements.len - 1 and sentinel == null) { |
| 5336 | _ = try func.buildPointerOffset(offset, elem_size, .modify); | 5203 | _ = try cg.buildPointerOffset(offset, elem_size, .modify); |
| 5337 | } | 5204 | } |
| 5338 | } | 5205 | } |
| 5339 | if (sentinel) |sent| { | 5206 | if (sentinel) |sent| { |
| 5340 | try func.store(offset, sent, elem_ty, 0); | 5207 | try cg.store(offset, sent, elem_ty, 0); |
| 5341 | } | 5208 | } |
| 5342 | } else { | 5209 | } else { |
| 5343 | var offset: u32 = 0; | 5210 | var offset: u32 = 0; |
| 5344 | for (elements) |elem| { | 5211 | for (elements) |elem| { |
| 5345 | const elem_val = try func.resolveInst(elem); | 5212 | const elem_val = try cg.resolveInst(elem); |
| 5346 | try func.store(result, elem_val, elem_ty, offset); | 5213 | try cg.store(result, elem_val, elem_ty, offset); |
| 5347 | offset += elem_size; | 5214 | offset += elem_size; |
| 5348 | } | 5215 | } |
| 5349 | if (sentinel) |sent| { | 5216 | if (sentinel) |sent| { |
| 5350 | try func.store(result, sent, elem_ty, offset); | 5217 | try cg.store(result, sent, elem_ty, offset); |
| 5351 | } | 5218 | } |
| 5352 | } | 5219 | } |
| 5353 | break :result_value result; | 5220 | break :result_value result; |
| 5354 | }, | 5221 | }, |
| 5355 | .@"struct" => switch (result_ty.containerLayout(zcu)) { | 5222 | .@"struct" => switch (result_ty.containerLayout(zcu)) { |
| 5356 | .@"packed" => { | 5223 | .@"packed" => { |
| 5357 | if (isByRef(result_ty, pt, func.target.*)) { | 5224 | if (isByRef(result_ty, zcu, cg.target)) { |
| 5358 | return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{}); | 5225 | return cg.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{}); |
| 5359 | } | 5226 | } |
| 5360 | const packed_struct = zcu.typeToPackedStruct(result_ty).?; | 5227 | const packed_struct = zcu.typeToPackedStruct(result_ty).?; |
| 5361 | const field_types = packed_struct.field_types; | 5228 | const field_types = packed_struct.field_types; |
| 5362 | const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); | 5229 | const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); |
| 5363 | 5230 | ||
| 5364 | // ensure the result is zero'd | 5231 | // ensure the result is zero'd |
| 5365 | const result = try func.allocLocal(backing_type); | 5232 | const result = try cg.allocLocal(backing_type); |
| 5366 | if (backing_type.bitSize(zcu) <= 32) | 5233 | if (backing_type.bitSize(zcu) <= 32) |
| 5367 | try func.addImm32(0) | 5234 | try cg.addImm32(0) |
| 5368 | else | 5235 | else |
| 5369 | try func.addImm64(0); | 5236 | try cg.addImm64(0); |
| 5370 | try func.addLabel(.local_set, result.local.value); | 5237 | try cg.addLocal(.local_set, result.local.value); |
| 5371 | 5238 | ||
| 5372 | var current_bit: u16 = 0; | 5239 | var current_bit: u16 = 0; |
| 5373 | for (elements, 0..) |elem, elem_index| { | 5240 | for (elements, 0..) |elem, elem_index| { |
| ... | @@ -5379,46 +5246,46 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5379,46 +5246,46 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5379 | else | 5246 | else |
| 5380 | .{ .imm64 = current_bit }; | 5247 | .{ .imm64 = current_bit }; |
| 5381 | 5248 | ||
| 5382 | const value = try func.resolveInst(elem); | 5249 | const value = try cg.resolveInst(elem); |
| 5383 | const value_bit_size: u16 = @intCast(field_ty.bitSize(zcu)); | 5250 | const value_bit_size: u16 = @intCast(field_ty.bitSize(zcu)); |
| 5384 | const int_ty = try pt.intType(.unsigned, value_bit_size); | 5251 | const int_ty = try pt.intType(.unsigned, value_bit_size); |
| 5385 | 5252 | ||
| 5386 | // load our current result on stack so we can perform all transformations | 5253 | // load our current result on stack so we can perform all transformations |
| 5387 | // using only stack values. Saving the cost of loads and stores. | 5254 | // using only stack values. Saving the cost of loads and stores. |
| 5388 | try func.emitWValue(result); | 5255 | try cg.emitWValue(result); |
| 5389 | const bitcasted = try func.bitcast(int_ty, field_ty, value); | 5256 | const bitcasted = try cg.bitcast(int_ty, field_ty, value); |
| 5390 | const extended_val = try func.intcast(bitcasted, int_ty, backing_type); | 5257 | const extended_val = try cg.intcast(bitcasted, int_ty, backing_type); |
| 5391 | // no need to shift any values when the current offset is 0 | 5258 | // no need to shift any values when the current offset is 0 |
| 5392 | const shifted = if (current_bit != 0) shifted: { | 5259 | const shifted = if (current_bit != 0) shifted: { |
| 5393 | break :shifted try func.binOp(extended_val, shift_val, backing_type, .shl); | 5260 | break :shifted try cg.binOp(extended_val, shift_val, backing_type, .shl); |
| 5394 | } else extended_val; | 5261 | } else extended_val; |
| 5395 | // we ignore the result as we keep it on the stack to assign it directly to `result` | 5262 | // we ignore the result as we keep it on the stack to assign it directly to `result` |
| 5396 | _ = try func.binOp(.stack, shifted, backing_type, .@"or"); | 5263 | _ = try cg.binOp(.stack, shifted, backing_type, .@"or"); |
| 5397 | try func.addLabel(.local_set, result.local.value); | 5264 | try cg.addLocal(.local_set, result.local.value); |
| 5398 | current_bit += value_bit_size; | 5265 | current_bit += value_bit_size; |
| 5399 | } | 5266 | } |
| 5400 | break :result_value result; | 5267 | break :result_value result; |
| 5401 | }, | 5268 | }, |
| 5402 | else => { | 5269 | else => { |
| 5403 | const result = try func.allocStack(result_ty); | 5270 | const result = try cg.allocStack(result_ty); |
| 5404 | const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset | 5271 | const offset = try cg.buildPointerOffset(result, 0, .new); // pointer to offset |
| 5405 | var prev_field_offset: u64 = 0; | 5272 | var prev_field_offset: u64 = 0; |
| 5406 | for (elements, 0..) |elem, elem_index| { | 5273 | for (elements, 0..) |elem, elem_index| { |
| 5407 | if (try result_ty.structFieldValueComptime(pt, elem_index) != null) continue; | 5274 | if (try result_ty.structFieldValueComptime(pt, elem_index) != null) continue; |
| 5408 | 5275 | ||
| 5409 | const elem_ty = result_ty.fieldType(elem_index, zcu); | 5276 | const elem_ty = result_ty.fieldType(elem_index, zcu); |
| 5410 | const field_offset = result_ty.structFieldOffset(elem_index, zcu); | 5277 | const field_offset = result_ty.structFieldOffset(elem_index, zcu); |
| 5411 | _ = try func.buildPointerOffset(offset, @intCast(field_offset - prev_field_offset), .modify); | 5278 | _ = try cg.buildPointerOffset(offset, @intCast(field_offset - prev_field_offset), .modify); |
| 5412 | prev_field_offset = field_offset; | 5279 | prev_field_offset = field_offset; |
| 5413 | 5280 | ||
| 5414 | const value = try func.resolveInst(elem); | 5281 | const value = try cg.resolveInst(elem); |
| 5415 | try func.store(offset, value, elem_ty, 0); | 5282 | try cg.store(offset, value, elem_ty, 0); |
| 5416 | } | 5283 | } |
| 5417 | 5284 | ||
| 5418 | break :result_value result; | 5285 | break :result_value result; |
| 5419 | }, | 5286 | }, |
| 5420 | }, | 5287 | }, |
| 5421 | .vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}), | 5288 | .vector => return cg.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}), |
| 5422 | else => unreachable, | 5289 | else => unreachable, |
| 5423 | } | 5290 | } |
| 5424 | }; | 5291 | }; |
| ... | @@ -5426,22 +5293,22 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5426,22 +5293,22 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5426 | if (elements.len <= Liveness.bpi - 1) { | 5293 | if (elements.len <= Liveness.bpi - 1) { |
| 5427 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 5294 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 5428 | @memcpy(buf[0..elements.len], elements); | 5295 | @memcpy(buf[0..elements.len], elements); |
| 5429 | return func.finishAir(inst, result, &buf); | 5296 | return cg.finishAir(inst, result, &buf); |
| 5430 | } | 5297 | } |
| 5431 | var bt = try func.iterateBigTomb(inst, elements.len); | 5298 | var bt = try cg.iterateBigTomb(inst, elements.len); |
| 5432 | for (elements) |arg| bt.feed(arg); | 5299 | for (elements) |arg| bt.feed(arg); |
| 5433 | return bt.finishAir(result); | 5300 | return bt.finishAir(result); |
| 5434 | } | 5301 | } |
| 5435 | 5302 | ||
| 5436 | fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5303 | fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5437 | const pt = func.pt; | 5304 | const pt = cg.pt; |
| 5438 | const zcu = pt.zcu; | 5305 | const zcu = pt.zcu; |
| 5439 | const ip = &zcu.intern_pool; | 5306 | const ip = &zcu.intern_pool; |
| 5440 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5307 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5441 | const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data; | 5308 | const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data; |
| 5442 | 5309 | ||
| 5443 | const result = result: { | 5310 | const result = result: { |
| 5444 | const union_ty = func.typeOfIndex(inst); | 5311 | const union_ty = cg.typeOfIndex(inst); |
| 5445 | const layout = union_ty.unionGetLayout(zcu); | 5312 | const layout = union_ty.unionGetLayout(zcu); |
| 5446 | const union_obj = zcu.typeToUnion(union_ty).?; | 5313 | const union_obj = zcu.typeToUnion(union_ty).?; |
| 5447 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); | 5314 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); |
| ... | @@ -5451,34 +5318,34 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5451,34 +5318,34 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5451 | const tag_ty = union_ty.unionTagTypeHypothetical(zcu); | 5318 | const tag_ty = union_ty.unionTagTypeHypothetical(zcu); |
| 5452 | const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?; | 5319 | const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?; |
| 5453 | const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index); | 5320 | const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index); |
| 5454 | break :blk try func.lowerConstant(tag_val, tag_ty); | 5321 | break :blk try cg.lowerConstant(tag_val, tag_ty); |
| 5455 | }; | 5322 | }; |
| 5456 | if (layout.payload_size == 0) { | 5323 | if (layout.payload_size == 0) { |
| 5457 | if (layout.tag_size == 0) { | 5324 | if (layout.tag_size == 0) { |
| 5458 | break :result .none; | 5325 | break :result .none; |
| 5459 | } | 5326 | } |
| 5460 | assert(!isByRef(union_ty, pt, func.target.*)); | 5327 | assert(!isByRef(union_ty, zcu, cg.target)); |
| 5461 | break :result tag_int; | 5328 | break :result tag_int; |
| 5462 | } | 5329 | } |
| 5463 | 5330 | ||
| 5464 | if (isByRef(union_ty, pt, func.target.*)) { | 5331 | if (isByRef(union_ty, zcu, cg.target)) { |
| 5465 | const result_ptr = try func.allocStack(union_ty); | 5332 | const result_ptr = try cg.allocStack(union_ty); |
| 5466 | const payload = try func.resolveInst(extra.init); | 5333 | const payload = try cg.resolveInst(extra.init); |
| 5467 | if (layout.tag_align.compare(.gte, layout.payload_align)) { | 5334 | if (layout.tag_align.compare(.gte, layout.payload_align)) { |
| 5468 | if (isByRef(field_ty, pt, func.target.*)) { | 5335 | if (isByRef(field_ty, zcu, cg.target)) { |
| 5469 | const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); | 5336 | const payload_ptr = try cg.buildPointerOffset(result_ptr, layout.tag_size, .new); |
| 5470 | try func.store(payload_ptr, payload, field_ty, 0); | 5337 | try cg.store(payload_ptr, payload, field_ty, 0); |
| 5471 | } else { | 5338 | } else { |
| 5472 | try func.store(result_ptr, payload, field_ty, @intCast(layout.tag_size)); | 5339 | try cg.store(result_ptr, payload, field_ty, @intCast(layout.tag_size)); |
| 5473 | } | 5340 | } |
| 5474 | 5341 | ||
| 5475 | if (layout.tag_size > 0) { | 5342 | if (layout.tag_size > 0) { |
| 5476 | try func.store(result_ptr, tag_int, Type.fromInterned(union_obj.enum_tag_ty), 0); | 5343 | try cg.store(result_ptr, tag_int, Type.fromInterned(union_obj.enum_tag_ty), 0); |
| 5477 | } | 5344 | } |
| 5478 | } else { | 5345 | } else { |
| 5479 | try func.store(result_ptr, payload, field_ty, 0); | 5346 | try cg.store(result_ptr, payload, field_ty, 0); |
| 5480 | if (layout.tag_size > 0) { | 5347 | if (layout.tag_size > 0) { |
| 5481 | try func.store( | 5348 | try cg.store( |
| 5482 | result_ptr, | 5349 | result_ptr, |
| 5483 | tag_int, | 5350 | tag_int, |
| 5484 | Type.fromInterned(union_obj.enum_tag_ty), | 5351 | Type.fromInterned(union_obj.enum_tag_ty), |
| ... | @@ -5488,138 +5355,136 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5488,138 +5355,136 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5488 | } | 5355 | } |
| 5489 | break :result result_ptr; | 5356 | break :result result_ptr; |
| 5490 | } else { | 5357 | } else { |
| 5491 | const operand = try func.resolveInst(extra.init); | 5358 | const operand = try cg.resolveInst(extra.init); |
| 5492 | const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(zcu)))); | 5359 | const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(zcu)))); |
| 5493 | if (field_ty.zigTypeTag(zcu) == .float) { | 5360 | if (field_ty.zigTypeTag(zcu) == .float) { |
| 5494 | const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu))); | 5361 | const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu))); |
| 5495 | const bitcasted = try func.bitcast(field_ty, int_type, operand); | 5362 | const bitcasted = try cg.bitcast(field_ty, int_type, operand); |
| 5496 | break :result try func.trunc(bitcasted, int_type, union_int_type); | 5363 | break :result try cg.trunc(bitcasted, int_type, union_int_type); |
| 5497 | } else if (field_ty.isPtrAtRuntime(zcu)) { | 5364 | } else if (field_ty.isPtrAtRuntime(zcu)) { |
| 5498 | const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu))); | 5365 | const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu))); |
| 5499 | break :result try func.intcast(operand, int_type, union_int_type); | 5366 | break :result try cg.intcast(operand, int_type, union_int_type); |
| 5500 | } | 5367 | } |
| 5501 | break :result try func.intcast(operand, field_ty, union_int_type); | 5368 | break :result try cg.intcast(operand, field_ty, union_int_type); |
| 5502 | } | 5369 | } |
| 5503 | }; | 5370 | }; |
| 5504 | 5371 | ||
| 5505 | return func.finishAir(inst, result, &.{extra.init}); | 5372 | return cg.finishAir(inst, result, &.{extra.init}); |
| 5506 | } | 5373 | } |
| 5507 | 5374 | ||
| 5508 | fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5375 | fn airPrefetch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5509 | const prefetch = func.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; | 5376 | const prefetch = cg.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; |
| 5510 | return func.finishAir(inst, .none, &.{prefetch.ptr}); | 5377 | return cg.finishAir(inst, .none, &.{prefetch.ptr}); |
| 5511 | } | 5378 | } |
| 5512 | 5379 | ||
| 5513 | fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5380 | fn airWasmMemorySize(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5514 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 5381 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 5515 | 5382 | ||
| 5516 | try func.addLabel(.memory_size, pl_op.payload); | 5383 | try cg.addLabel(.memory_size, pl_op.payload); |
| 5517 | return func.finishAir(inst, .stack, &.{pl_op.operand}); | 5384 | return cg.finishAir(inst, .stack, &.{pl_op.operand}); |
| 5518 | } | 5385 | } |
| 5519 | 5386 | ||
| 5520 | fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void { | 5387 | fn airWasmMemoryGrow(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 5521 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 5388 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 5522 | 5389 | ||
| 5523 | const operand = try func.resolveInst(pl_op.operand); | 5390 | const operand = try cg.resolveInst(pl_op.operand); |
| 5524 | try func.emitWValue(operand); | 5391 | try cg.emitWValue(operand); |
| 5525 | try func.addLabel(.memory_grow, pl_op.payload); | 5392 | try cg.addLabel(.memory_grow, pl_op.payload); |
| 5526 | return func.finishAir(inst, .stack, &.{pl_op.operand}); | 5393 | return cg.finishAir(inst, .stack, &.{pl_op.operand}); |
| 5527 | } | 5394 | } |
| 5528 | 5395 | ||
| 5529 | fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { | 5396 | fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| 5530 | const pt = func.pt; | 5397 | const zcu = cg.pt.zcu; |
| 5531 | const zcu = pt.zcu; | ||
| 5532 | assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu)); | 5398 | assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu)); |
| 5533 | assert(op == .eq or op == .neq); | 5399 | assert(op == .eq or op == .neq); |
| 5534 | const payload_ty = operand_ty.optionalChild(zcu); | 5400 | const payload_ty = operand_ty.optionalChild(zcu); |
| 5535 | 5401 | ||
| 5536 | // We store the final result in here that will be validated | 5402 | // We store the final result in here that will be validated |
| 5537 | // if the optional is truly equal. | 5403 | // if the optional is truly equal. |
| 5538 | var result = try func.ensureAllocLocal(Type.i32); | 5404 | var result = try cg.ensureAllocLocal(Type.i32); |
| 5539 | defer result.free(func); | 5405 | defer result.free(cg); |
| 5540 | 5406 | ||
| 5541 | try func.startBlock(.block, wasm.block_empty); | 5407 | try cg.startBlock(.block, .empty); |
| 5542 | _ = try func.isNull(lhs, operand_ty, .i32_eq); | 5408 | _ = try cg.isNull(lhs, operand_ty, .i32_eq); |
| 5543 | _ = try func.isNull(rhs, operand_ty, .i32_eq); | 5409 | _ = try cg.isNull(rhs, operand_ty, .i32_eq); |
| 5544 | try func.addTag(.i32_ne); // inverse so we can exit early | 5410 | try cg.addTag(.i32_ne); // inverse so we can exit early |
| 5545 | try func.addLabel(.br_if, 0); | 5411 | try cg.addLabel(.br_if, 0); |
| 5546 | 5412 | ||
| 5547 | _ = try func.load(lhs, payload_ty, 0); | 5413 | _ = try cg.load(lhs, payload_ty, 0); |
| 5548 | _ = try func.load(rhs, payload_ty, 0); | 5414 | _ = try cg.load(rhs, payload_ty, 0); |
| 5549 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target.*) }); | 5415 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, zcu, cg.target) }); |
| 5550 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 5416 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 5551 | try func.addLabel(.br_if, 0); | 5417 | try cg.addLabel(.br_if, 0); |
| 5552 | 5418 | ||
| 5553 | try func.addImm32(1); | 5419 | try cg.addImm32(1); |
| 5554 | try func.addLabel(.local_set, result.local.value); | 5420 | try cg.addLocal(.local_set, result.local.value); |
| 5555 | try func.endBlock(); | 5421 | try cg.endBlock(); |
| 5556 | 5422 | ||
| 5557 | try func.emitWValue(result); | 5423 | try cg.emitWValue(result); |
| 5558 | try func.addImm32(0); | 5424 | try cg.addImm32(0); |
| 5559 | try func.addTag(if (op == .eq) .i32_ne else .i32_eq); | 5425 | try cg.addTag(if (op == .eq) .i32_ne else .i32_eq); |
| 5560 | return .stack; | 5426 | return .stack; |
| 5561 | } | 5427 | } |
| 5562 | 5428 | ||
| 5563 | /// Compares big integers by checking both its high bits and low bits. | 5429 | /// Compares big integers by checking both its high bits and low bits. |
| 5564 | /// NOTE: Leaves the result of the comparison on top of the stack. | 5430 | /// NOTE: Leaves the result of the comparison on top of the stack. |
| 5565 | /// TODO: Lower this to compiler_rt call when bitsize > 128 | 5431 | /// TODO: Lower this to compiler_rt call when bitsize > 128 |
| 5566 | fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { | 5432 | fn cmpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| 5567 | const pt = func.pt; | 5433 | const zcu = cg.pt.zcu; |
| 5568 | const zcu = pt.zcu; | ||
| 5569 | assert(operand_ty.abiSize(zcu) >= 16); | 5434 | assert(operand_ty.abiSize(zcu) >= 16); |
| 5570 | assert(!(lhs != .stack and rhs == .stack)); | 5435 | assert(!(lhs != .stack and rhs == .stack)); |
| 5571 | if (operand_ty.bitSize(zcu) > 128) { | 5436 | if (operand_ty.bitSize(zcu) > 128) { |
| 5572 | return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(zcu)}); | 5437 | return cg.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.bitSize(zcu)}); |
| 5573 | } | 5438 | } |
| 5574 | 5439 | ||
| 5575 | var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64); | 5440 | var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64); |
| 5576 | defer lhs_msb.free(func); | 5441 | defer lhs_msb.free(cg); |
| 5577 | var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64); | 5442 | var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64); |
| 5578 | defer rhs_msb.free(func); | 5443 | defer rhs_msb.free(cg); |
| 5579 | 5444 | ||
| 5580 | switch (op) { | 5445 | switch (op) { |
| 5581 | .eq, .neq => { | 5446 | .eq, .neq => { |
| 5582 | const xor_high = try func.binOp(lhs_msb, rhs_msb, Type.u64, .xor); | 5447 | const xor_high = try cg.binOp(lhs_msb, rhs_msb, Type.u64, .xor); |
| 5583 | const lhs_lsb = try func.load(lhs, Type.u64, 0); | 5448 | const lhs_lsb = try cg.load(lhs, Type.u64, 0); |
| 5584 | const rhs_lsb = try func.load(rhs, Type.u64, 0); | 5449 | const rhs_lsb = try cg.load(rhs, Type.u64, 0); |
| 5585 | const xor_low = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor); | 5450 | const xor_low = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor); |
| 5586 | const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or"); | 5451 | const or_result = try cg.binOp(xor_high, xor_low, Type.u64, .@"or"); |
| 5587 | 5452 | ||
| 5588 | switch (op) { | 5453 | switch (op) { |
| 5589 | .eq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq), | 5454 | .eq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq), |
| 5590 | .neq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq), | 5455 | .neq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq), |
| 5591 | else => unreachable, | 5456 | else => unreachable, |
| 5592 | } | 5457 | } |
| 5593 | }, | 5458 | }, |
| 5594 | else => { | 5459 | else => { |
| 5595 | const ty = if (operand_ty.isSignedInt(zcu)) Type.i64 else Type.u64; | 5460 | const ty = if (operand_ty.isSignedInt(zcu)) Type.i64 else Type.u64; |
| 5596 | // leave those value on top of the stack for '.select' | 5461 | // leave those value on top of the stack for '.select' |
| 5597 | const lhs_lsb = try func.load(lhs, Type.u64, 0); | 5462 | const lhs_lsb = try cg.load(lhs, Type.u64, 0); |
| 5598 | const rhs_lsb = try func.load(rhs, Type.u64, 0); | 5463 | const rhs_lsb = try cg.load(rhs, Type.u64, 0); |
| 5599 | _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op); | 5464 | _ = try cg.cmp(lhs_lsb, rhs_lsb, Type.u64, op); |
| 5600 | _ = try func.cmp(lhs_msb, rhs_msb, ty, op); | 5465 | _ = try cg.cmp(lhs_msb, rhs_msb, ty, op); |
| 5601 | _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq); | 5466 | _ = try cg.cmp(lhs_msb, rhs_msb, ty, .eq); |
| 5602 | try func.addTag(.select); | 5467 | try cg.addTag(.select); |
| 5603 | }, | 5468 | }, |
| 5604 | } | 5469 | } |
| 5605 | 5470 | ||
| 5606 | return .stack; | 5471 | return .stack; |
| 5607 | } | 5472 | } |
| 5608 | 5473 | ||
| 5609 | fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5474 | fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5610 | const pt = func.pt; | 5475 | const pt = cg.pt; |
| 5611 | const zcu = pt.zcu; | 5476 | const zcu = pt.zcu; |
| 5612 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 5477 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 5613 | const un_ty = func.typeOf(bin_op.lhs).childType(zcu); | 5478 | const un_ty = cg.typeOf(bin_op.lhs).childType(zcu); |
| 5614 | const tag_ty = func.typeOf(bin_op.rhs); | 5479 | const tag_ty = cg.typeOf(bin_op.rhs); |
| 5615 | const layout = un_ty.unionGetLayout(zcu); | 5480 | const layout = un_ty.unionGetLayout(zcu); |
| 5616 | if (layout.tag_size == 0) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 5481 | if (layout.tag_size == 0) return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 5617 | 5482 | ||
| 5618 | const union_ptr = try func.resolveInst(bin_op.lhs); | 5483 | const union_ptr = try cg.resolveInst(bin_op.lhs); |
| 5619 | const new_tag = try func.resolveInst(bin_op.rhs); | 5484 | const new_tag = try cg.resolveInst(bin_op.rhs); |
| 5620 | if (layout.payload_size == 0) { | 5485 | if (layout.payload_size == 0) { |
| 5621 | try func.store(union_ptr, new_tag, tag_ty, 0); | 5486 | try cg.store(union_ptr, new_tag, tag_ty, 0); |
| 5622 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 5487 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 5623 | } | 5488 | } |
| 5624 | 5489 | ||
| 5625 | // when the tag alignment is smaller than the payload, the field will be stored | 5490 | // when the tag alignment is smaller than the payload, the field will be stored |
| ... | @@ -5627,124 +5492,147 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5627,124 +5492,147 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5627 | const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: { | 5492 | const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: { |
| 5628 | break :blk @intCast(layout.payload_size); | 5493 | break :blk @intCast(layout.payload_size); |
| 5629 | } else 0; | 5494 | } else 0; |
| 5630 | try func.store(union_ptr, new_tag, tag_ty, offset); | 5495 | try cg.store(union_ptr, new_tag, tag_ty, offset); |
| 5631 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 5496 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 5632 | } | 5497 | } |
| 5633 | 5498 | ||
| 5634 | fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5499 | fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5635 | const zcu = func.pt.zcu; | 5500 | const zcu = cg.pt.zcu; |
| 5636 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5501 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5637 | 5502 | ||
| 5638 | const un_ty = func.typeOf(ty_op.operand); | 5503 | const un_ty = cg.typeOf(ty_op.operand); |
| 5639 | const tag_ty = func.typeOfIndex(inst); | 5504 | const tag_ty = cg.typeOfIndex(inst); |
| 5640 | const layout = un_ty.unionGetLayout(zcu); | 5505 | const layout = un_ty.unionGetLayout(zcu); |
| 5641 | if (layout.tag_size == 0) return func.finishAir(inst, .none, &.{ty_op.operand}); | 5506 | if (layout.tag_size == 0) return cg.finishAir(inst, .none, &.{ty_op.operand}); |
| 5642 | 5507 | ||
| 5643 | const operand = try func.resolveInst(ty_op.operand); | 5508 | const operand = try cg.resolveInst(ty_op.operand); |
| 5644 | // when the tag alignment is smaller than the payload, the field will be stored | 5509 | // when the tag alignment is smaller than the payload, the field will be stored |
| 5645 | // after the payload. | 5510 | // after the payload. |
| 5646 | const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) | 5511 | const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) |
| 5647 | @intCast(layout.payload_size) | 5512 | @intCast(layout.payload_size) |
| 5648 | else | 5513 | else |
| 5649 | 0; | 5514 | 0; |
| 5650 | const result = try func.load(operand, tag_ty, offset); | 5515 | const result = try cg.load(operand, tag_ty, offset); |
| 5651 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5516 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5652 | } | 5517 | } |
| 5653 | 5518 | ||
| 5654 | fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5519 | fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5655 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5520 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5656 | 5521 | ||
| 5657 | const dest_ty = func.typeOfIndex(inst); | 5522 | const dest_ty = cg.typeOfIndex(inst); |
| 5658 | const operand = try func.resolveInst(ty_op.operand); | 5523 | const operand = try cg.resolveInst(ty_op.operand); |
| 5659 | const result = try func.fpext(operand, func.typeOf(ty_op.operand), dest_ty); | 5524 | const result = try cg.fpext(operand, cg.typeOf(ty_op.operand), dest_ty); |
| 5660 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5525 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5661 | } | 5526 | } |
| 5662 | 5527 | ||
| 5663 | /// Extends a float from a given `Type` to a larger wanted `Type` | 5528 | /// Extends a float from a given `Type` to a larger wanted `Type`, leaving the |
| 5664 | /// NOTE: Leaves the result on the stack | 5529 | /// result on the stack. |
| 5665 | fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | 5530 | fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 5666 | const given_bits = given.floatBits(func.target.*); | 5531 | const given_bits = given.floatBits(cg.target.*); |
| 5667 | const wanted_bits = wanted.floatBits(func.target.*); | 5532 | const wanted_bits = wanted.floatBits(cg.target.*); |
| 5668 | |||
| 5669 | if (wanted_bits == 64 and given_bits == 32) { | ||
| 5670 | try func.emitWValue(operand); | ||
| 5671 | try func.addTag(.f64_promote_f32); | ||
| 5672 | return .stack; | ||
| 5673 | } else if (given_bits == 16 and wanted_bits <= 64) { | ||
| 5674 | // call __extendhfsf2(f16) f32 | ||
| 5675 | const f32_result = try func.callIntrinsic( | ||
| 5676 | "__extendhfsf2", | ||
| 5677 | &.{.f16_type}, | ||
| 5678 | Type.f32, | ||
| 5679 | &.{operand}, | ||
| 5680 | ); | ||
| 5681 | std.debug.assert(f32_result == .stack); | ||
| 5682 | |||
| 5683 | if (wanted_bits == 64) { | ||
| 5684 | try func.addTag(.f64_promote_f32); | ||
| 5685 | } | ||
| 5686 | return .stack; | ||
| 5687 | } | ||
| 5688 | |||
| 5689 | var fn_name_buf: [13]u8 = undefined; | ||
| 5690 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__extend{s}f{s}f2", .{ | ||
| 5691 | target_util.compilerRtFloatAbbrev(given_bits), | ||
| 5692 | target_util.compilerRtFloatAbbrev(wanted_bits), | ||
| 5693 | }) catch unreachable; | ||
| 5694 | 5533 | ||
| 5695 | return func.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand}); | 5534 | const intrinsic: Mir.Intrinsic = switch (given_bits) { |
| 5535 | 16 => switch (wanted_bits) { | ||
| 5536 | 32 => { | ||
| 5537 | assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand})); | ||
| 5538 | return .stack; | ||
| 5539 | }, | ||
| 5540 | 64 => { | ||
| 5541 | assert(.stack == try cg.callIntrinsic(.__extendhfsf2, &.{.f16_type}, Type.f32, &.{operand})); | ||
| 5542 | try cg.addTag(.f64_promote_f32); | ||
| 5543 | return .stack; | ||
| 5544 | }, | ||
| 5545 | 80 => .__extendhfxf2, | ||
| 5546 | 128 => .__extendhftf2, | ||
| 5547 | else => unreachable, | ||
| 5548 | }, | ||
| 5549 | 32 => switch (wanted_bits) { | ||
| 5550 | 64 => { | ||
| 5551 | try cg.emitWValue(operand); | ||
| 5552 | try cg.addTag(.f64_promote_f32); | ||
| 5553 | return .stack; | ||
| 5554 | }, | ||
| 5555 | 80 => .__extendsfxf2, | ||
| 5556 | 128 => .__extendsftf2, | ||
| 5557 | else => unreachable, | ||
| 5558 | }, | ||
| 5559 | 64 => switch (wanted_bits) { | ||
| 5560 | 80 => .__extenddfxf2, | ||
| 5561 | 128 => .__extenddftf2, | ||
| 5562 | else => unreachable, | ||
| 5563 | }, | ||
| 5564 | 80 => switch (wanted_bits) { | ||
| 5565 | 128 => .__extendxftf2, | ||
| 5566 | else => unreachable, | ||
| 5567 | }, | ||
| 5568 | else => unreachable, | ||
| 5569 | }; | ||
| 5570 | return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand}); | ||
| 5696 | } | 5571 | } |
| 5697 | 5572 | ||
| 5698 | fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5573 | fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5699 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5574 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5700 | 5575 | ||
| 5701 | const dest_ty = func.typeOfIndex(inst); | 5576 | const dest_ty = cg.typeOfIndex(inst); |
| 5702 | const operand = try func.resolveInst(ty_op.operand); | 5577 | const operand = try cg.resolveInst(ty_op.operand); |
| 5703 | const result = try func.fptrunc(operand, func.typeOf(ty_op.operand), dest_ty); | 5578 | const result = try cg.fptrunc(operand, cg.typeOf(ty_op.operand), dest_ty); |
| 5704 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5579 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5705 | } | 5580 | } |
| 5706 | 5581 | ||
| 5707 | /// Truncates a float from a given `Type` to its wanted `Type` | 5582 | /// Truncates a float from a given `Type` to its wanted `Type`, leaving the |
| 5708 | /// NOTE: The result value remains on the stack | 5583 | /// result on the stack. |
| 5709 | fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | 5584 | fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 5710 | const given_bits = given.floatBits(func.target.*); | 5585 | const given_bits = given.floatBits(cg.target.*); |
| 5711 | const wanted_bits = wanted.floatBits(func.target.*); | 5586 | const wanted_bits = wanted.floatBits(cg.target.*); |
| 5712 | |||
| 5713 | if (wanted_bits == 32 and given_bits == 64) { | ||
| 5714 | try func.emitWValue(operand); | ||
| 5715 | try func.addTag(.f32_demote_f64); | ||
| 5716 | return .stack; | ||
| 5717 | } else if (wanted_bits == 16 and given_bits <= 64) { | ||
| 5718 | const op: WValue = if (given_bits == 64) blk: { | ||
| 5719 | try func.emitWValue(operand); | ||
| 5720 | try func.addTag(.f32_demote_f64); | ||
| 5721 | break :blk .stack; | ||
| 5722 | } else operand; | ||
| 5723 | |||
| 5724 | // call __truncsfhf2(f32) f16 | ||
| 5725 | return func.callIntrinsic("__truncsfhf2", &.{.f32_type}, Type.f16, &.{op}); | ||
| 5726 | } | ||
| 5727 | |||
| 5728 | var fn_name_buf: [12]u8 = undefined; | ||
| 5729 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__trunc{s}f{s}f2", .{ | ||
| 5730 | target_util.compilerRtFloatAbbrev(given_bits), | ||
| 5731 | target_util.compilerRtFloatAbbrev(wanted_bits), | ||
| 5732 | }) catch unreachable; | ||
| 5733 | 5587 | ||
| 5734 | return func.callIntrinsic(fn_name, &.{given.ip_index}, wanted, &.{operand}); | 5588 | const intrinsic: Mir.Intrinsic = switch (given_bits) { |
| 5589 | 32 => switch (wanted_bits) { | ||
| 5590 | 16 => { | ||
| 5591 | return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{operand}); | ||
| 5592 | }, | ||
| 5593 | else => unreachable, | ||
| 5594 | }, | ||
| 5595 | 64 => switch (wanted_bits) { | ||
| 5596 | 16 => { | ||
| 5597 | try cg.emitWValue(operand); | ||
| 5598 | try cg.addTag(.f32_demote_f64); | ||
| 5599 | return cg.callIntrinsic(.__truncsfhf2, &.{.f32_type}, Type.f16, &.{.stack}); | ||
| 5600 | }, | ||
| 5601 | 32 => { | ||
| 5602 | try cg.emitWValue(operand); | ||
| 5603 | try cg.addTag(.f32_demote_f64); | ||
| 5604 | return .stack; | ||
| 5605 | }, | ||
| 5606 | else => unreachable, | ||
| 5607 | }, | ||
| 5608 | 80 => switch (wanted_bits) { | ||
| 5609 | 16 => .__truncxfhf2, | ||
| 5610 | 32 => .__truncxfsf2, | ||
| 5611 | 64 => .__truncxfdf2, | ||
| 5612 | else => unreachable, | ||
| 5613 | }, | ||
| 5614 | 128 => switch (wanted_bits) { | ||
| 5615 | 16 => .__trunctfhf2, | ||
| 5616 | 32 => .__trunctfsf2, | ||
| 5617 | 64 => .__trunctfdf2, | ||
| 5618 | 80 => .__trunctfxf2, | ||
| 5619 | else => unreachable, | ||
| 5620 | }, | ||
| 5621 | else => unreachable, | ||
| 5622 | }; | ||
| 5623 | return cg.callIntrinsic(intrinsic, &.{given.ip_index}, wanted, &.{operand}); | ||
| 5735 | } | 5624 | } |
| 5736 | 5625 | ||
| 5737 | fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5626 | fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5738 | const pt = func.pt; | 5627 | const zcu = cg.pt.zcu; |
| 5739 | const zcu = pt.zcu; | 5628 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5740 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 5741 | 5629 | ||
| 5742 | const err_set_ty = func.typeOf(ty_op.operand).childType(zcu); | 5630 | const err_set_ty = cg.typeOf(ty_op.operand).childType(zcu); |
| 5743 | const payload_ty = err_set_ty.errorUnionPayload(zcu); | 5631 | const payload_ty = err_set_ty.errorUnionPayload(zcu); |
| 5744 | const operand = try func.resolveInst(ty_op.operand); | 5632 | const operand = try cg.resolveInst(ty_op.operand); |
| 5745 | 5633 | ||
| 5746 | // set error-tag to '0' to annotate error union is non-error | 5634 | // set error-tag to '0' to annotate error union is non-error |
| 5747 | try func.store( | 5635 | try cg.store( |
| 5748 | operand, | 5636 | operand, |
| 5749 | .{ .imm32 = 0 }, | 5637 | .{ .imm32 = 0 }, |
| 5750 | Type.anyerror, | 5638 | Type.anyerror, |
| ... | @@ -5753,63 +5641,60 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi | ... | @@ -5753,63 +5641,60 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi |
| 5753 | 5641 | ||
| 5754 | const result = result: { | 5642 | const result = result: { |
| 5755 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 5643 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 5756 | break :result func.reuseOperand(ty_op.operand, operand); | 5644 | break :result cg.reuseOperand(ty_op.operand, operand); |
| 5757 | } | 5645 | } |
| 5758 | 5646 | ||
| 5759 | break :result try func.buildPointerOffset(operand, @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))), .new); | 5647 | break :result try cg.buildPointerOffset(operand, @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))), .new); |
| 5760 | }; | 5648 | }; |
| 5761 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5649 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5762 | } | 5650 | } |
| 5763 | 5651 | ||
| 5764 | fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5652 | fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5765 | const pt = func.pt; | 5653 | const zcu = cg.pt.zcu; |
| 5766 | const zcu = pt.zcu; | 5654 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5767 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5655 | const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; |
| 5768 | const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; | ||
| 5769 | 5656 | ||
| 5770 | const field_ptr = try func.resolveInst(extra.field_ptr); | 5657 | const field_ptr = try cg.resolveInst(extra.field_ptr); |
| 5771 | const parent_ty = ty_pl.ty.toType().childType(zcu); | 5658 | const parent_ty = ty_pl.ty.toType().childType(zcu); |
| 5772 | const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu); | 5659 | const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu); |
| 5773 | 5660 | ||
| 5774 | const result = if (field_offset != 0) result: { | 5661 | const result = if (field_offset != 0) result: { |
| 5775 | const base = try func.buildPointerOffset(field_ptr, 0, .new); | 5662 | const base = try cg.buildPointerOffset(field_ptr, 0, .new); |
| 5776 | try func.addLabel(.local_get, base.local.value); | 5663 | try cg.addLocal(.local_get, base.local.value); |
| 5777 | try func.addImm32(@intCast(field_offset)); | 5664 | try cg.addImm32(@intCast(field_offset)); |
| 5778 | try func.addTag(.i32_sub); | 5665 | try cg.addTag(.i32_sub); |
| 5779 | try func.addLabel(.local_set, base.local.value); | 5666 | try cg.addLocal(.local_set, base.local.value); |
| 5780 | break :result base; | 5667 | break :result base; |
| 5781 | } else func.reuseOperand(extra.field_ptr, field_ptr); | 5668 | } else cg.reuseOperand(extra.field_ptr, field_ptr); |
| 5782 | 5669 | ||
| 5783 | return func.finishAir(inst, result, &.{extra.field_ptr}); | 5670 | return cg.finishAir(inst, result, &.{extra.field_ptr}); |
| 5784 | } | 5671 | } |
| 5785 | 5672 | ||
| 5786 | fn sliceOrArrayPtr(func: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue { | 5673 | fn sliceOrArrayPtr(cg: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue { |
| 5787 | const pt = func.pt; | 5674 | const zcu = cg.pt.zcu; |
| 5788 | const zcu = pt.zcu; | ||
| 5789 | if (ptr_ty.isSlice(zcu)) { | 5675 | if (ptr_ty.isSlice(zcu)) { |
| 5790 | return func.slicePtr(ptr); | 5676 | return cg.slicePtr(ptr); |
| 5791 | } else { | 5677 | } else { |
| 5792 | return ptr; | 5678 | return ptr; |
| 5793 | } | 5679 | } |
| 5794 | } | 5680 | } |
| 5795 | 5681 | ||
| 5796 | fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5682 | fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5797 | const pt = func.pt; | 5683 | const zcu = cg.pt.zcu; |
| 5798 | const zcu = pt.zcu; | 5684 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 5799 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 5685 | const dst = try cg.resolveInst(bin_op.lhs); |
| 5800 | const dst = try func.resolveInst(bin_op.lhs); | 5686 | const dst_ty = cg.typeOf(bin_op.lhs); |
| 5801 | const dst_ty = func.typeOf(bin_op.lhs); | ||
| 5802 | const ptr_elem_ty = dst_ty.childType(zcu); | 5687 | const ptr_elem_ty = dst_ty.childType(zcu); |
| 5803 | const src = try func.resolveInst(bin_op.rhs); | 5688 | const src = try cg.resolveInst(bin_op.rhs); |
| 5804 | const src_ty = func.typeOf(bin_op.rhs); | 5689 | const src_ty = cg.typeOf(bin_op.rhs); |
| 5805 | const len = switch (dst_ty.ptrSize(zcu)) { | 5690 | const len = switch (dst_ty.ptrSize(zcu)) { |
| 5806 | .Slice => blk: { | 5691 | .Slice => blk: { |
| 5807 | const slice_len = try func.sliceLen(dst); | 5692 | const slice_len = try cg.sliceLen(dst); |
| 5808 | if (ptr_elem_ty.abiSize(zcu) != 1) { | 5693 | if (ptr_elem_ty.abiSize(zcu) != 1) { |
| 5809 | try func.emitWValue(slice_len); | 5694 | try cg.emitWValue(slice_len); |
| 5810 | try func.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); | 5695 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); |
| 5811 | try func.addTag(.i32_mul); | 5696 | try cg.addTag(.i32_mul); |
| 5812 | try func.addLabel(.local_set, slice_len.local.value); | 5697 | try cg.addLocal(.local_set, slice_len.local.value); |
| 5813 | } | 5698 | } |
| 5814 | break :blk slice_len; | 5699 | break :blk slice_len; |
| 5815 | }, | 5700 | }, |
| ... | @@ -5818,96 +5703,94 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5818,96 +5703,94 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5818 | }), | 5703 | }), |
| 5819 | .C, .Many => unreachable, | 5704 | .C, .Many => unreachable, |
| 5820 | }; | 5705 | }; |
| 5821 | const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty); | 5706 | const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty); |
| 5822 | const src_ptr = try func.sliceOrArrayPtr(src, src_ty); | 5707 | const src_ptr = try cg.sliceOrArrayPtr(src, src_ty); |
| 5823 | try func.memcpy(dst_ptr, src_ptr, len); | 5708 | try cg.memcpy(dst_ptr, src_ptr, len); |
| 5824 | 5709 | ||
| 5825 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 5710 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 5826 | } | 5711 | } |
| 5827 | 5712 | ||
| 5828 | fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5713 | fn airRetAddr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5829 | // TODO: Implement this properly once stack serialization is solved | 5714 | // TODO: Implement this properly once stack serialization is solved |
| 5830 | return func.finishAir(inst, switch (func.arch()) { | 5715 | return cg.finishAir(inst, switch (cg.ptr_size) { |
| 5831 | .wasm32 => .{ .imm32 = 0 }, | 5716 | .wasm32 => .{ .imm32 = 0 }, |
| 5832 | .wasm64 => .{ .imm64 = 0 }, | 5717 | .wasm64 => .{ .imm64 = 0 }, |
| 5833 | else => unreachable, | ||
| 5834 | }, &.{}); | 5718 | }, &.{}); |
| 5835 | } | 5719 | } |
| 5836 | 5720 | ||
| 5837 | fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5721 | fn airPopcount(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5838 | const pt = func.pt; | 5722 | const pt = cg.pt; |
| 5839 | const zcu = pt.zcu; | 5723 | const zcu = pt.zcu; |
| 5840 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5724 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5841 | 5725 | ||
| 5842 | const operand = try func.resolveInst(ty_op.operand); | 5726 | const operand = try cg.resolveInst(ty_op.operand); |
| 5843 | const op_ty = func.typeOf(ty_op.operand); | 5727 | const op_ty = cg.typeOf(ty_op.operand); |
| 5844 | 5728 | ||
| 5845 | if (op_ty.zigTypeTag(zcu) == .vector) { | 5729 | if (op_ty.zigTypeTag(zcu) == .vector) { |
| 5846 | return func.fail("TODO: Implement @popCount for vectors", .{}); | 5730 | return cg.fail("TODO: Implement @popCount for vectors", .{}); |
| 5847 | } | 5731 | } |
| 5848 | 5732 | ||
| 5849 | const int_info = op_ty.intInfo(zcu); | 5733 | const int_info = op_ty.intInfo(zcu); |
| 5850 | const bits = int_info.bits; | 5734 | const bits = int_info.bits; |
| 5851 | const wasm_bits = toWasmBits(bits) orelse { | 5735 | const wasm_bits = toWasmBits(bits) orelse { |
| 5852 | return func.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits}); | 5736 | return cg.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits}); |
| 5853 | }; | 5737 | }; |
| 5854 | 5738 | ||
| 5855 | switch (wasm_bits) { | 5739 | switch (wasm_bits) { |
| 5856 | 32 => { | 5740 | 32 => { |
| 5857 | try func.emitWValue(operand); | 5741 | try cg.emitWValue(operand); |
| 5858 | if (op_ty.isSignedInt(zcu) and bits != wasm_bits) { | 5742 | if (op_ty.isSignedInt(zcu) and bits != wasm_bits) { |
| 5859 | _ = try func.wrapOperand(.stack, try pt.intType(.unsigned, bits)); | 5743 | _ = try cg.wrapOperand(.stack, try pt.intType(.unsigned, bits)); |
| 5860 | } | 5744 | } |
| 5861 | try func.addTag(.i32_popcnt); | 5745 | try cg.addTag(.i32_popcnt); |
| 5862 | }, | 5746 | }, |
| 5863 | 64 => { | 5747 | 64 => { |
| 5864 | try func.emitWValue(operand); | 5748 | try cg.emitWValue(operand); |
| 5865 | if (op_ty.isSignedInt(zcu) and bits != wasm_bits) { | 5749 | if (op_ty.isSignedInt(zcu) and bits != wasm_bits) { |
| 5866 | _ = try func.wrapOperand(.stack, try pt.intType(.unsigned, bits)); | 5750 | _ = try cg.wrapOperand(.stack, try pt.intType(.unsigned, bits)); |
| 5867 | } | 5751 | } |
| 5868 | try func.addTag(.i64_popcnt); | 5752 | try cg.addTag(.i64_popcnt); |
| 5869 | try func.addTag(.i32_wrap_i64); | 5753 | try cg.addTag(.i32_wrap_i64); |
| 5870 | try func.emitWValue(operand); | 5754 | try cg.emitWValue(operand); |
| 5871 | }, | 5755 | }, |
| 5872 | 128 => { | 5756 | 128 => { |
| 5873 | _ = try func.load(operand, Type.u64, 0); | 5757 | _ = try cg.load(operand, Type.u64, 0); |
| 5874 | try func.addTag(.i64_popcnt); | 5758 | try cg.addTag(.i64_popcnt); |
| 5875 | _ = try func.load(operand, Type.u64, 8); | 5759 | _ = try cg.load(operand, Type.u64, 8); |
| 5876 | if (op_ty.isSignedInt(zcu) and bits != wasm_bits) { | 5760 | if (op_ty.isSignedInt(zcu) and bits != wasm_bits) { |
| 5877 | _ = try func.wrapOperand(.stack, try pt.intType(.unsigned, bits - 64)); | 5761 | _ = try cg.wrapOperand(.stack, try pt.intType(.unsigned, bits - 64)); |
| 5878 | } | 5762 | } |
| 5879 | try func.addTag(.i64_popcnt); | 5763 | try cg.addTag(.i64_popcnt); |
| 5880 | try func.addTag(.i64_add); | 5764 | try cg.addTag(.i64_add); |
| 5881 | try func.addTag(.i32_wrap_i64); | 5765 | try cg.addTag(.i32_wrap_i64); |
| 5882 | }, | 5766 | }, |
| 5883 | else => unreachable, | 5767 | else => unreachable, |
| 5884 | } | 5768 | } |
| 5885 | 5769 | ||
| 5886 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 5770 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 5887 | } | 5771 | } |
| 5888 | 5772 | ||
| 5889 | fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5773 | fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5890 | const pt = func.pt; | 5774 | const zcu = cg.pt.zcu; |
| 5891 | const zcu = pt.zcu; | 5775 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5892 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 5893 | 5776 | ||
| 5894 | const operand = try func.resolveInst(ty_op.operand); | 5777 | const operand = try cg.resolveInst(ty_op.operand); |
| 5895 | const ty = func.typeOf(ty_op.operand); | 5778 | const ty = cg.typeOf(ty_op.operand); |
| 5896 | 5779 | ||
| 5897 | if (ty.zigTypeTag(zcu) == .vector) { | 5780 | if (ty.zigTypeTag(zcu) == .vector) { |
| 5898 | return func.fail("TODO: Implement @bitReverse for vectors", .{}); | 5781 | return cg.fail("TODO: Implement @bitReverse for vectors", .{}); |
| 5899 | } | 5782 | } |
| 5900 | 5783 | ||
| 5901 | const int_info = ty.intInfo(zcu); | 5784 | const int_info = ty.intInfo(zcu); |
| 5902 | const bits = int_info.bits; | 5785 | const bits = int_info.bits; |
| 5903 | const wasm_bits = toWasmBits(bits) orelse { | 5786 | const wasm_bits = toWasmBits(bits) orelse { |
| 5904 | return func.fail("TODO: Implement @bitReverse for integers with bitsize '{d}'", .{bits}); | 5787 | return cg.fail("TODO: Implement @bitReverse for integers with bitsize '{d}'", .{bits}); |
| 5905 | }; | 5788 | }; |
| 5906 | 5789 | ||
| 5907 | switch (wasm_bits) { | 5790 | switch (wasm_bits) { |
| 5908 | 32 => { | 5791 | 32 => { |
| 5909 | const intrin_ret = try func.callIntrinsic( | 5792 | const intrin_ret = try cg.callIntrinsic( |
| 5910 | "__bitreversesi2", | 5793 | .__bitreversesi2, |
| 5911 | &.{.u32_type}, | 5794 | &.{.u32_type}, |
| 5912 | Type.u32, | 5795 | Type.u32, |
| 5913 | &.{operand}, | 5796 | &.{operand}, |
| ... | @@ -5915,12 +5798,12 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5915,12 +5798,12 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5915 | const result = if (bits == 32) | 5798 | const result = if (bits == 32) |
| 5916 | intrin_ret | 5799 | intrin_ret |
| 5917 | else | 5800 | else |
| 5918 | try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr); | 5801 | try cg.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr); |
| 5919 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5802 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5920 | }, | 5803 | }, |
| 5921 | 64 => { | 5804 | 64 => { |
| 5922 | const intrin_ret = try func.callIntrinsic( | 5805 | const intrin_ret = try cg.callIntrinsic( |
| 5923 | "__bitreversedi2", | 5806 | .__bitreversedi2, |
| 5924 | &.{.u64_type}, | 5807 | &.{.u64_type}, |
| 5925 | Type.u64, | 5808 | Type.u64, |
| 5926 | &.{operand}, | 5809 | &.{operand}, |
| ... | @@ -5928,68 +5811,63 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5928,68 +5811,63 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5928 | const result = if (bits == 64) | 5811 | const result = if (bits == 64) |
| 5929 | intrin_ret | 5812 | intrin_ret |
| 5930 | else | 5813 | else |
| 5931 | try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr); | 5814 | try cg.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr); |
| 5932 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5815 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5933 | }, | 5816 | }, |
| 5934 | 128 => { | 5817 | 128 => { |
| 5935 | const result = try func.allocStack(ty); | 5818 | const result = try cg.allocStack(ty); |
| 5936 | 5819 | ||
| 5937 | try func.emitWValue(result); | 5820 | try cg.emitWValue(result); |
| 5938 | const first_half = try func.load(operand, Type.u64, 8); | 5821 | const first_half = try cg.load(operand, Type.u64, 8); |
| 5939 | const intrin_ret_first = try func.callIntrinsic( | 5822 | const intrin_ret_first = try cg.callIntrinsic( |
| 5940 | "__bitreversedi2", | 5823 | .__bitreversedi2, |
| 5941 | &.{.u64_type}, | 5824 | &.{.u64_type}, |
| 5942 | Type.u64, | 5825 | Type.u64, |
| 5943 | &.{first_half}, | 5826 | &.{first_half}, |
| 5944 | ); | 5827 | ); |
| 5945 | try func.emitWValue(intrin_ret_first); | 5828 | try cg.emitWValue(intrin_ret_first); |
| 5946 | if (bits < 128) { | 5829 | if (bits < 128) { |
| 5947 | try func.emitWValue(.{ .imm64 = 128 - bits }); | 5830 | try cg.emitWValue(.{ .imm64 = 128 - bits }); |
| 5948 | try func.addTag(.i64_shr_u); | 5831 | try cg.addTag(.i64_shr_u); |
| 5949 | } | 5832 | } |
| 5950 | try func.emitWValue(result); | 5833 | try cg.emitWValue(result); |
| 5951 | const second_half = try func.load(operand, Type.u64, 0); | 5834 | const second_half = try cg.load(operand, Type.u64, 0); |
| 5952 | const intrin_ret_second = try func.callIntrinsic( | 5835 | const intrin_ret_second = try cg.callIntrinsic( |
| 5953 | "__bitreversedi2", | 5836 | .__bitreversedi2, |
| 5954 | &.{.u64_type}, | 5837 | &.{.u64_type}, |
| 5955 | Type.u64, | 5838 | Type.u64, |
| 5956 | &.{second_half}, | 5839 | &.{second_half}, |
| 5957 | ); | 5840 | ); |
| 5958 | try func.emitWValue(intrin_ret_second); | 5841 | try cg.emitWValue(intrin_ret_second); |
| 5959 | if (bits == 128) { | 5842 | if (bits == 128) { |
| 5960 | try func.store(.stack, .stack, Type.u64, result.offset() + 8); | 5843 | try cg.store(.stack, .stack, Type.u64, result.offset() + 8); |
| 5961 | try func.store(.stack, .stack, Type.u64, result.offset()); | 5844 | try cg.store(.stack, .stack, Type.u64, result.offset()); |
| 5962 | } else { | 5845 | } else { |
| 5963 | var tmp = try func.allocLocal(Type.u64); | 5846 | var tmp = try cg.allocLocal(Type.u64); |
| 5964 | defer tmp.free(func); | 5847 | defer tmp.free(cg); |
| 5965 | try func.addLabel(.local_tee, tmp.local.value); | 5848 | try cg.addLocal(.local_tee, tmp.local.value); |
| 5966 | try func.emitWValue(.{ .imm64 = 128 - bits }); | 5849 | try cg.emitWValue(.{ .imm64 = 128 - bits }); |
| 5967 | if (ty.isSignedInt(zcu)) { | 5850 | if (ty.isSignedInt(zcu)) { |
| 5968 | try func.addTag(.i64_shr_s); | 5851 | try cg.addTag(.i64_shr_s); |
| 5969 | } else { | 5852 | } else { |
| 5970 | try func.addTag(.i64_shr_u); | 5853 | try cg.addTag(.i64_shr_u); |
| 5971 | } | 5854 | } |
| 5972 | try func.store(.stack, .stack, Type.u64, result.offset() + 8); | 5855 | try cg.store(.stack, .stack, Type.u64, result.offset() + 8); |
| 5973 | try func.addLabel(.local_get, tmp.local.value); | 5856 | try cg.addLocal(.local_get, tmp.local.value); |
| 5974 | try func.emitWValue(.{ .imm64 = bits - 64 }); | 5857 | try cg.emitWValue(.{ .imm64 = bits - 64 }); |
| 5975 | try func.addTag(.i64_shl); | 5858 | try cg.addTag(.i64_shl); |
| 5976 | try func.addTag(.i64_or); | 5859 | try cg.addTag(.i64_or); |
| 5977 | try func.store(.stack, .stack, Type.u64, result.offset()); | 5860 | try cg.store(.stack, .stack, Type.u64, result.offset()); |
| 5978 | } | 5861 | } |
| 5979 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5862 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5980 | }, | 5863 | }, |
| 5981 | else => unreachable, | 5864 | else => unreachable, |
| 5982 | } | 5865 | } |
| 5983 | } | 5866 | } |
| 5984 | 5867 | ||
| 5985 | fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5868 | fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5986 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 5869 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5987 | 5870 | const operand = try cg.resolveInst(un_op); | |
| 5988 | const operand = try func.resolveInst(un_op); | ||
| 5989 | // First retrieve the symbol index to the error name table | ||
| 5990 | // that will be used to emit a relocation for the pointer | ||
| 5991 | // to the error name table. | ||
| 5992 | // | ||
| 5993 | // Each entry to this table is a slice (ptr+len). | 5871 | // Each entry to this table is a slice (ptr+len). |
| 5994 | // The operand in this instruction represents the index within this table. | 5872 | // The operand in this instruction represents the index within this table. |
| 5995 | // This means to get the final name, we emit the base pointer and then perform | 5873 | // This means to get the final name, we emit the base pointer and then perform |
| ... | @@ -5997,82 +5875,82 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5997,82 +5875,82 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5997 | // | 5875 | // |
| 5998 | // As the names are global and the slice elements are constant, we do not have | 5876 | // As the names are global and the slice elements are constant, we do not have |
| 5999 | // to make a copy of the ptr+value but can point towards them directly. | 5877 | // to make a copy of the ptr+value but can point towards them directly. |
| 6000 | const pt = func.pt; | 5878 | const pt = cg.pt; |
| 6001 | const error_table_symbol = try func.bin_file.getErrorTableSymbol(pt); | ||
| 6002 | const name_ty = Type.slice_const_u8_sentinel_0; | 5879 | const name_ty = Type.slice_const_u8_sentinel_0; |
| 6003 | const abi_size = name_ty.abiSize(pt.zcu); | 5880 | const abi_size = name_ty.abiSize(pt.zcu); |
| 6004 | 5881 | ||
| 6005 | const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation | 5882 | cg.wasm.error_name_table_ref_count += 1; |
| 6006 | try func.emitWValue(error_name_value); | 5883 | |
| 6007 | try func.emitWValue(operand); | 5884 | // Lowers to a i32.const or i64.const with the error table memory address. |
| 6008 | switch (func.arch()) { | 5885 | try cg.addTag(.error_name_table_ref); |
| 5886 | try cg.emitWValue(operand); | ||
| 5887 | switch (cg.ptr_size) { | ||
| 6009 | .wasm32 => { | 5888 | .wasm32 => { |
| 6010 | try func.addImm32(@intCast(abi_size)); | 5889 | try cg.addImm32(@intCast(abi_size)); |
| 6011 | try func.addTag(.i32_mul); | 5890 | try cg.addTag(.i32_mul); |
| 6012 | try func.addTag(.i32_add); | 5891 | try cg.addTag(.i32_add); |
| 6013 | }, | 5892 | }, |
| 6014 | .wasm64 => { | 5893 | .wasm64 => { |
| 6015 | try func.addImm64(abi_size); | 5894 | try cg.addImm64(abi_size); |
| 6016 | try func.addTag(.i64_mul); | 5895 | try cg.addTag(.i64_mul); |
| 6017 | try func.addTag(.i64_add); | 5896 | try cg.addTag(.i64_add); |
| 6018 | }, | 5897 | }, |
| 6019 | else => unreachable, | ||
| 6020 | } | 5898 | } |
| 6021 | 5899 | ||
| 6022 | return func.finishAir(inst, .stack, &.{un_op}); | 5900 | return cg.finishAir(inst, .stack, &.{un_op}); |
| 6023 | } | 5901 | } |
| 6024 | 5902 | ||
| 6025 | fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void { | 5903 | fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void { |
| 6026 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 5904 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6027 | const slice_ptr = try func.resolveInst(ty_op.operand); | 5905 | const slice_ptr = try cg.resolveInst(ty_op.operand); |
| 6028 | const result = try func.buildPointerOffset(slice_ptr, offset, .new); | 5906 | const result = try cg.buildPointerOffset(slice_ptr, offset, .new); |
| 6029 | return func.finishAir(inst, result, &.{ty_op.operand}); | 5907 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 6030 | } | 5908 | } |
| 6031 | 5909 | ||
| 6032 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits | 5910 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits |
| 6033 | fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue { | 5911 | fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 6034 | const zcu = func.bin_file.base.comp.zcu.?; | 5912 | const zcu = cg.wasm.base.comp.zcu.?; |
| 6035 | const int_info = ty.intInfo(zcu); | 5913 | const int_info = ty.intInfo(zcu); |
| 6036 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 5914 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6037 | return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); | 5915 | return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); |
| 6038 | }; | 5916 | }; |
| 6039 | switch (wasm_bits) { | 5917 | switch (wasm_bits) { |
| 6040 | 32 => return .{ .imm32 = 0 }, | 5918 | 32 => return .{ .imm32 = 0 }, |
| 6041 | 64 => return .{ .imm64 = 0 }, | 5919 | 64 => return .{ .imm64 = 0 }, |
| 6042 | 128 => { | 5920 | 128 => { |
| 6043 | const result = try func.allocStack(ty); | 5921 | const result = try cg.allocStack(ty); |
| 6044 | try func.store(result, .{ .imm64 = 0 }, Type.u64, 0); | 5922 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 6045 | try func.store(result, .{ .imm64 = 0 }, Type.u64, 8); | 5923 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); |
| 6046 | return result; | 5924 | return result; |
| 6047 | }, | 5925 | }, |
| 6048 | else => unreachable, | 5926 | else => unreachable, |
| 6049 | } | 5927 | } |
| 6050 | } | 5928 | } |
| 6051 | 5929 | ||
| 6052 | fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 5930 | fn airAddSubWithOverflow(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 6053 | assert(op == .add or op == .sub); | 5931 | assert(op == .add or op == .sub); |
| 6054 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5932 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6055 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; | 5933 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 6056 | 5934 | ||
| 6057 | const lhs = try func.resolveInst(extra.lhs); | 5935 | const lhs = try cg.resolveInst(extra.lhs); |
| 6058 | const rhs = try func.resolveInst(extra.rhs); | 5936 | const rhs = try cg.resolveInst(extra.rhs); |
| 6059 | const ty = func.typeOf(extra.lhs); | 5937 | const ty = cg.typeOf(extra.lhs); |
| 6060 | const pt = func.pt; | 5938 | const pt = cg.pt; |
| 6061 | const zcu = pt.zcu; | 5939 | const zcu = pt.zcu; |
| 6062 | 5940 | ||
| 6063 | if (ty.zigTypeTag(zcu) == .vector) { | 5941 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6064 | return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); | 5942 | return cg.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 6065 | } | 5943 | } |
| 6066 | 5944 | ||
| 6067 | const int_info = ty.intInfo(zcu); | 5945 | const int_info = ty.intInfo(zcu); |
| 6068 | const is_signed = int_info.signedness == .signed; | 5946 | const is_signed = int_info.signedness == .signed; |
| 6069 | if (int_info.bits > 128) { | 5947 | if (int_info.bits > 128) { |
| 6070 | return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); | 5948 | return cg.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 6071 | } | 5949 | } |
| 6072 | 5950 | ||
| 6073 | const op_result = try func.wrapBinOp(lhs, rhs, ty, op); | 5951 | const op_result = try cg.wrapBinOp(lhs, rhs, ty, op); |
| 6074 | var op_tmp = try op_result.toLocal(func, ty); | 5952 | var op_tmp = try op_result.toLocal(cg, ty); |
| 6075 | defer op_tmp.free(func); | 5953 | defer op_tmp.free(cg); |
| 6076 | 5954 | ||
| 6077 | const cmp_op: std.math.CompareOperator = switch (op) { | 5955 | const cmp_op: std.math.CompareOperator = switch (op) { |
| 6078 | .add => .lt, | 5956 | .add => .lt, |
| ... | @@ -6080,40 +5958,40 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro | ... | @@ -6080,40 +5958,40 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro |
| 6080 | else => unreachable, | 5958 | else => unreachable, |
| 6081 | }; | 5959 | }; |
| 6082 | const overflow_bit = if (is_signed) blk: { | 5960 | const overflow_bit = if (is_signed) blk: { |
| 6083 | const zero = try intZeroValue(func, ty); | 5961 | const zero = try intZeroValue(cg, ty); |
| 6084 | const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt); | 5962 | const rhs_is_neg = try cg.cmp(rhs, zero, ty, .lt); |
| 6085 | const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op); | 5963 | const overflow_cmp = try cg.cmp(op_tmp, lhs, ty, cmp_op); |
| 6086 | break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq); | 5964 | break :blk try cg.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq); |
| 6087 | } else try func.cmp(op_tmp, lhs, ty, cmp_op); | 5965 | } else try cg.cmp(op_tmp, lhs, ty, cmp_op); |
| 6088 | var bit_tmp = try overflow_bit.toLocal(func, Type.u1); | 5966 | var bit_tmp = try overflow_bit.toLocal(cg, Type.u1); |
| 6089 | defer bit_tmp.free(func); | 5967 | defer bit_tmp.free(cg); |
| 6090 | 5968 | ||
| 6091 | const result = try func.allocStack(func.typeOfIndex(inst)); | 5969 | const result = try cg.allocStack(cg.typeOfIndex(inst)); |
| 6092 | const offset: u32 = @intCast(ty.abiSize(zcu)); | 5970 | const offset: u32 = @intCast(ty.abiSize(zcu)); |
| 6093 | try func.store(result, op_tmp, ty, 0); | 5971 | try cg.store(result, op_tmp, ty, 0); |
| 6094 | try func.store(result, bit_tmp, Type.u1, offset); | 5972 | try cg.store(result, bit_tmp, Type.u1, offset); |
| 6095 | 5973 | ||
| 6096 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); | 5974 | return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); |
| 6097 | } | 5975 | } |
| 6098 | 5976 | ||
| 6099 | fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 5977 | fn airShlWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6100 | const pt = func.pt; | 5978 | const pt = cg.pt; |
| 6101 | const zcu = pt.zcu; | 5979 | const zcu = pt.zcu; |
| 6102 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5980 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6103 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; | 5981 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 6104 | 5982 | ||
| 6105 | const lhs = try func.resolveInst(extra.lhs); | 5983 | const lhs = try cg.resolveInst(extra.lhs); |
| 6106 | const rhs = try func.resolveInst(extra.rhs); | 5984 | const rhs = try cg.resolveInst(extra.rhs); |
| 6107 | const ty = func.typeOf(extra.lhs); | 5985 | const ty = cg.typeOf(extra.lhs); |
| 6108 | const rhs_ty = func.typeOf(extra.rhs); | 5986 | const rhs_ty = cg.typeOf(extra.rhs); |
| 6109 | 5987 | ||
| 6110 | if (ty.zigTypeTag(zcu) == .vector) { | 5988 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6111 | return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); | 5989 | return cg.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 6112 | } | 5990 | } |
| 6113 | 5991 | ||
| 6114 | const int_info = ty.intInfo(zcu); | 5992 | const int_info = ty.intInfo(zcu); |
| 6115 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 5993 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6116 | return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); | 5994 | return cg.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 6117 | }; | 5995 | }; |
| 6118 | 5996 | ||
| 6119 | // Ensure rhs is coerced to lhs as they must have the same WebAssembly types | 5997 | // Ensure rhs is coerced to lhs as they must have the same WebAssembly types |
| ... | @@ -6121,50 +5999,50 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6121,50 +5999,50 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6121 | const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(zcu).bits).?; | 5999 | const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(zcu).bits).?; |
| 6122 | // If wasm_bits == 128, compiler-rt expects i32 for shift | 6000 | // If wasm_bits == 128, compiler-rt expects i32 for shift |
| 6123 | const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: { | 6001 | const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: { |
| 6124 | const rhs_casted = try func.intcast(rhs, rhs_ty, ty); | 6002 | const rhs_casted = try cg.intcast(rhs, rhs_ty, ty); |
| 6125 | break :blk try rhs_casted.toLocal(func, ty); | 6003 | break :blk try rhs_casted.toLocal(cg, ty); |
| 6126 | } else rhs; | 6004 | } else rhs; |
| 6127 | 6005 | ||
| 6128 | var shl = try (try func.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(func, ty); | 6006 | var shl = try (try cg.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(cg, ty); |
| 6129 | defer shl.free(func); | 6007 | defer shl.free(cg); |
| 6130 | 6008 | ||
| 6131 | const overflow_bit = blk: { | 6009 | const overflow_bit = blk: { |
| 6132 | const shr = try func.binOp(shl, rhs_final, ty, .shr); | 6010 | const shr = try cg.binOp(shl, rhs_final, ty, .shr); |
| 6133 | break :blk try func.cmp(shr, lhs, ty, .neq); | 6011 | break :blk try cg.cmp(shr, lhs, ty, .neq); |
| 6134 | }; | 6012 | }; |
| 6135 | var overflow_local = try overflow_bit.toLocal(func, Type.u1); | 6013 | var overflow_local = try overflow_bit.toLocal(cg, Type.u1); |
| 6136 | defer overflow_local.free(func); | 6014 | defer overflow_local.free(cg); |
| 6137 | 6015 | ||
| 6138 | const result = try func.allocStack(func.typeOfIndex(inst)); | 6016 | const result = try cg.allocStack(cg.typeOfIndex(inst)); |
| 6139 | const offset: u32 = @intCast(ty.abiSize(zcu)); | 6017 | const offset: u32 = @intCast(ty.abiSize(zcu)); |
| 6140 | try func.store(result, shl, ty, 0); | 6018 | try cg.store(result, shl, ty, 0); |
| 6141 | try func.store(result, overflow_local, Type.u1, offset); | 6019 | try cg.store(result, overflow_local, Type.u1, offset); |
| 6142 | 6020 | ||
| 6143 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); | 6021 | return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); |
| 6144 | } | 6022 | } |
| 6145 | 6023 | ||
| 6146 | fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6024 | fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6147 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 6025 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6148 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; | 6026 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 6149 | 6027 | ||
| 6150 | const lhs = try func.resolveInst(extra.lhs); | 6028 | const lhs = try cg.resolveInst(extra.lhs); |
| 6151 | const rhs = try func.resolveInst(extra.rhs); | 6029 | const rhs = try cg.resolveInst(extra.rhs); |
| 6152 | const ty = func.typeOf(extra.lhs); | 6030 | const ty = cg.typeOf(extra.lhs); |
| 6153 | const pt = func.pt; | 6031 | const pt = cg.pt; |
| 6154 | const zcu = pt.zcu; | 6032 | const zcu = pt.zcu; |
| 6155 | 6033 | ||
| 6156 | if (ty.zigTypeTag(zcu) == .vector) { | 6034 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6157 | return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); | 6035 | return cg.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 6158 | } | 6036 | } |
| 6159 | 6037 | ||
| 6160 | // We store the bit if it's overflowed or not in this. As it's zero-initialized | 6038 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 6161 | // we only need to update it if an overflow (or underflow) occurred. | 6039 | // we only need to update it if an overflow (or underflow) occurred. |
| 6162 | var overflow_bit = try func.ensureAllocLocal(Type.u1); | 6040 | var overflow_bit = try cg.ensureAllocLocal(Type.u1); |
| 6163 | defer overflow_bit.free(func); | 6041 | defer overflow_bit.free(cg); |
| 6164 | 6042 | ||
| 6165 | const int_info = ty.intInfo(zcu); | 6043 | const int_info = ty.intInfo(zcu); |
| 6166 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 6044 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6167 | return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); | 6045 | return cg.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); |
| 6168 | }; | 6046 | }; |
| 6169 | 6047 | ||
| 6170 | const zero: WValue = switch (wasm_bits) { | 6048 | const zero: WValue = switch (wasm_bits) { |
| ... | @@ -6176,248 +6054,250 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6176,248 +6054,250 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6176 | // for 32 bit integers we upcast it to a 64bit integer | 6054 | // for 32 bit integers we upcast it to a 64bit integer |
| 6177 | const mul = if (wasm_bits == 32) blk: { | 6055 | const mul = if (wasm_bits == 32) blk: { |
| 6178 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; | 6056 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; |
| 6179 | const lhs_upcast = try func.intcast(lhs, ty, new_ty); | 6057 | const lhs_upcast = try cg.intcast(lhs, ty, new_ty); |
| 6180 | const rhs_upcast = try func.intcast(rhs, ty, new_ty); | 6058 | const rhs_upcast = try cg.intcast(rhs, ty, new_ty); |
| 6181 | const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty); | 6059 | const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty); |
| 6182 | const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty); | 6060 | const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty); |
| 6183 | const res_upcast = try func.intcast(res, ty, new_ty); | 6061 | const res_upcast = try cg.intcast(res, ty, new_ty); |
| 6184 | _ = try func.cmp(res_upcast, bin_op, new_ty, .neq); | 6062 | _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq); |
| 6185 | try func.addLabel(.local_set, overflow_bit.local.value); | 6063 | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 6186 | break :blk res; | 6064 | break :blk res; |
| 6187 | } else if (wasm_bits == 64) blk: { | 6065 | } else if (wasm_bits == 64) blk: { |
| 6188 | const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128; | 6066 | const new_ty = if (int_info.signedness == .signed) Type.i128 else Type.u128; |
| 6189 | const lhs_upcast = try func.intcast(lhs, ty, new_ty); | 6067 | const lhs_upcast = try cg.intcast(lhs, ty, new_ty); |
| 6190 | const rhs_upcast = try func.intcast(rhs, ty, new_ty); | 6068 | const rhs_upcast = try cg.intcast(rhs, ty, new_ty); |
| 6191 | const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty); | 6069 | const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty); |
| 6192 | const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty); | 6070 | const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty); |
| 6193 | const res_upcast = try func.intcast(res, ty, new_ty); | 6071 | const res_upcast = try cg.intcast(res, ty, new_ty); |
| 6194 | _ = try func.cmp(res_upcast, bin_op, new_ty, .neq); | 6072 | _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq); |
| 6195 | try func.addLabel(.local_set, overflow_bit.local.value); | 6073 | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 6196 | break :blk res; | 6074 | break :blk res; |
| 6197 | } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: { | 6075 | } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: { |
| 6198 | var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); | 6076 | var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64); |
| 6199 | defer lhs_lsb.free(func); | 6077 | defer lhs_lsb.free(cg); |
| 6200 | var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64); | 6078 | var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64); |
| 6201 | defer lhs_msb.free(func); | 6079 | defer lhs_msb.free(cg); |
| 6202 | var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); | 6080 | var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64); |
| 6203 | defer rhs_lsb.free(func); | 6081 | defer rhs_lsb.free(cg); |
| 6204 | var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64); | 6082 | var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64); |
| 6205 | defer rhs_msb.free(func); | 6083 | defer rhs_msb.free(cg); |
| 6206 | 6084 | ||
| 6207 | const cross_1 = try func.callIntrinsic( | 6085 | const cross_1 = try cg.callIntrinsic( |
| 6208 | "__multi3", | 6086 | .__multi3, |
| 6209 | &[_]InternPool.Index{.i64_type} ** 4, | 6087 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6210 | Type.i128, | 6088 | Type.i128, |
| 6211 | &.{ lhs_msb, zero, rhs_lsb, zero }, | 6089 | &.{ lhs_msb, zero, rhs_lsb, zero }, |
| 6212 | ); | 6090 | ); |
| 6213 | const cross_2 = try func.callIntrinsic( | 6091 | const cross_2 = try cg.callIntrinsic( |
| 6214 | "__multi3", | 6092 | .__multi3, |
| 6215 | &[_]InternPool.Index{.i64_type} ** 4, | 6093 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6216 | Type.i128, | 6094 | Type.i128, |
| 6217 | &.{ rhs_msb, zero, lhs_lsb, zero }, | 6095 | &.{ rhs_msb, zero, lhs_lsb, zero }, |
| 6218 | ); | 6096 | ); |
| 6219 | const mul_lsb = try func.callIntrinsic( | 6097 | const mul_lsb = try cg.callIntrinsic( |
| 6220 | "__multi3", | 6098 | .__multi3, |
| 6221 | &[_]InternPool.Index{.i64_type} ** 4, | 6099 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6222 | Type.i128, | 6100 | Type.i128, |
| 6223 | &.{ rhs_lsb, zero, lhs_lsb, zero }, | 6101 | &.{ rhs_lsb, zero, lhs_lsb, zero }, |
| 6224 | ); | 6102 | ); |
| 6225 | 6103 | ||
| 6226 | const rhs_msb_not_zero = try func.cmp(rhs_msb, zero, Type.u64, .neq); | 6104 | const rhs_msb_not_zero = try cg.cmp(rhs_msb, zero, Type.u64, .neq); |
| 6227 | const lhs_msb_not_zero = try func.cmp(lhs_msb, zero, Type.u64, .neq); | 6105 | const lhs_msb_not_zero = try cg.cmp(lhs_msb, zero, Type.u64, .neq); |
| 6228 | const both_msb_not_zero = try func.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and"); | 6106 | const both_msb_not_zero = try cg.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and"); |
| 6229 | const cross_1_msb = try func.load(cross_1, Type.u64, 8); | 6107 | const cross_1_msb = try cg.load(cross_1, Type.u64, 8); |
| 6230 | const cross_1_msb_not_zero = try func.cmp(cross_1_msb, zero, Type.u64, .neq); | 6108 | const cross_1_msb_not_zero = try cg.cmp(cross_1_msb, zero, Type.u64, .neq); |
| 6231 | const cond_1 = try func.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or"); | 6109 | const cond_1 = try cg.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or"); |
| 6232 | const cross_2_msb = try func.load(cross_2, Type.u64, 8); | 6110 | const cross_2_msb = try cg.load(cross_2, Type.u64, 8); |
| 6233 | const cross_2_msb_not_zero = try func.cmp(cross_2_msb, zero, Type.u64, .neq); | 6111 | const cross_2_msb_not_zero = try cg.cmp(cross_2_msb, zero, Type.u64, .neq); |
| 6234 | const cond_2 = try func.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or"); | 6112 | const cond_2 = try cg.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or"); |
| 6235 | 6113 | ||
| 6236 | const cross_1_lsb = try func.load(cross_1, Type.u64, 0); | 6114 | const cross_1_lsb = try cg.load(cross_1, Type.u64, 0); |
| 6237 | const cross_2_lsb = try func.load(cross_2, Type.u64, 0); | 6115 | const cross_2_lsb = try cg.load(cross_2, Type.u64, 0); |
| 6238 | const cross_add = try func.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add); | 6116 | const cross_add = try cg.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add); |
| 6239 | 6117 | ||
| 6240 | var mul_lsb_msb = try (try func.load(mul_lsb, Type.u64, 8)).toLocal(func, Type.u64); | 6118 | var mul_lsb_msb = try (try cg.load(mul_lsb, Type.u64, 8)).toLocal(cg, Type.u64); |
| 6241 | defer mul_lsb_msb.free(func); | 6119 | defer mul_lsb_msb.free(cg); |
| 6242 | var all_add = try (try func.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(func, Type.u64); | 6120 | var all_add = try (try cg.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(cg, Type.u64); |
| 6243 | defer all_add.free(func); | 6121 | defer all_add.free(cg); |
| 6244 | const add_overflow = try func.cmp(all_add, mul_lsb_msb, Type.u64, .lt); | 6122 | const add_overflow = try cg.cmp(all_add, mul_lsb_msb, Type.u64, .lt); |
| 6245 | 6123 | ||
| 6246 | // result for overflow bit | 6124 | // result for overflow bit |
| 6247 | _ = try func.binOp(cond_2, add_overflow, Type.bool, .@"or"); | 6125 | _ = try cg.binOp(cond_2, add_overflow, Type.bool, .@"or"); |
| 6248 | try func.addLabel(.local_set, overflow_bit.local.value); | 6126 | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 6249 | 6127 | ||
| 6250 | const tmp_result = try func.allocStack(Type.u128); | 6128 | const tmp_result = try cg.allocStack(Type.u128); |
| 6251 | try func.emitWValue(tmp_result); | 6129 | try cg.emitWValue(tmp_result); |
| 6252 | const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0); | 6130 | const mul_lsb_lsb = try cg.load(mul_lsb, Type.u64, 0); |
| 6253 | try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset()); | 6131 | try cg.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset()); |
| 6254 | try func.store(tmp_result, all_add, Type.u64, 8); | 6132 | try cg.store(tmp_result, all_add, Type.u64, 8); |
| 6255 | break :blk tmp_result; | 6133 | break :blk tmp_result; |
| 6256 | } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: { | 6134 | } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: { |
| 6257 | const overflow_ret = try func.allocStack(Type.i32); | 6135 | const overflow_ret = try cg.allocStack(Type.i32); |
| 6258 | const res = try func.callIntrinsic( | 6136 | const res = try cg.callIntrinsic( |
| 6259 | "__muloti4", | 6137 | .__muloti4, |
| 6260 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, | 6138 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, |
| 6261 | Type.i128, | 6139 | Type.i128, |
| 6262 | &.{ lhs, rhs, overflow_ret }, | 6140 | &.{ lhs, rhs, overflow_ret }, |
| 6263 | ); | 6141 | ); |
| 6264 | _ = try func.load(overflow_ret, Type.i32, 0); | 6142 | _ = try cg.load(overflow_ret, Type.i32, 0); |
| 6265 | try func.addLabel(.local_set, overflow_bit.local.value); | 6143 | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 6266 | break :blk res; | 6144 | break :blk res; |
| 6267 | } else return func.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)}); | 6145 | } else return cg.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)}); |
| 6268 | var bin_op_local = try mul.toLocal(func, ty); | 6146 | var bin_op_local = try mul.toLocal(cg, ty); |
| 6269 | defer bin_op_local.free(func); | 6147 | defer bin_op_local.free(cg); |
| 6270 | 6148 | ||
| 6271 | const result = try func.allocStack(func.typeOfIndex(inst)); | 6149 | const result = try cg.allocStack(cg.typeOfIndex(inst)); |
| 6272 | const offset: u32 = @intCast(ty.abiSize(zcu)); | 6150 | const offset: u32 = @intCast(ty.abiSize(zcu)); |
| 6273 | try func.store(result, bin_op_local, ty, 0); | 6151 | try cg.store(result, bin_op_local, ty, 0); |
| 6274 | try func.store(result, overflow_bit, Type.u1, offset); | 6152 | try cg.store(result, overflow_bit, Type.u1, offset); |
| 6275 | 6153 | ||
| 6276 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); | 6154 | return cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); |
| 6277 | } | 6155 | } |
| 6278 | 6156 | ||
| 6279 | fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 6157 | fn airMaxMin( |
| 6280 | assert(op == .max or op == .min); | 6158 | cg: *CodeGen, |
| 6281 | const pt = func.pt; | 6159 | inst: Air.Inst.Index, |
| 6282 | const zcu = pt.zcu; | 6160 | op: enum { fmax, fmin }, |
| 6283 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6161 | cmp_op: std.math.CompareOperator, |
| 6162 | ) InnerError!void { | ||
| 6163 | const zcu = cg.pt.zcu; | ||
| 6164 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 6284 | 6165 | ||
| 6285 | const ty = func.typeOfIndex(inst); | 6166 | const ty = cg.typeOfIndex(inst); |
| 6286 | if (ty.zigTypeTag(zcu) == .vector) { | 6167 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6287 | return func.fail("TODO: `@maximum` and `@minimum` for vectors", .{}); | 6168 | return cg.fail("TODO: `@maximum` and `@minimum` for vectors", .{}); |
| 6288 | } | 6169 | } |
| 6289 | 6170 | ||
| 6290 | if (ty.abiSize(zcu) > 16) { | 6171 | if (ty.abiSize(zcu) > 16) { |
| 6291 | return func.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{}); | 6172 | return cg.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{}); |
| 6292 | } | 6173 | } |
| 6293 | 6174 | ||
| 6294 | const lhs = try func.resolveInst(bin_op.lhs); | 6175 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6295 | const rhs = try func.resolveInst(bin_op.rhs); | 6176 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6296 | 6177 | ||
| 6297 | if (ty.zigTypeTag(zcu) == .float) { | 6178 | if (ty.zigTypeTag(zcu) == .float) { |
| 6298 | var fn_name_buf: [64]u8 = undefined; | 6179 | const intrinsic = switch (op) { |
| 6299 | const float_bits = ty.floatBits(func.target.*); | 6180 | inline .fmin, .fmax => |ct_op| switch (ty.floatBits(cg.target.*)) { |
| 6300 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{ | 6181 | inline 16, 32, 64, 80, 128 => |bits| @field( |
| 6301 | target_util.libcFloatPrefix(float_bits), | 6182 | Mir.Intrinsic, |
| 6302 | @tagName(op), | 6183 | libcFloatPrefix(bits) ++ @tagName(ct_op) ++ libcFloatSuffix(bits), |
| 6303 | target_util.libcFloatSuffix(float_bits), | 6184 | ), |
| 6304 | }) catch unreachable; | 6185 | else => unreachable, |
| 6305 | const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); | 6186 | }, |
| 6306 | try func.lowerToStack(result); | 6187 | }; |
| 6188 | const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); | ||
| 6189 | try cg.lowerToStack(result); | ||
| 6307 | } else { | 6190 | } else { |
| 6308 | // operands to select from | 6191 | // operands to select from |
| 6309 | try func.lowerToStack(lhs); | 6192 | try cg.lowerToStack(lhs); |
| 6310 | try func.lowerToStack(rhs); | 6193 | try cg.lowerToStack(rhs); |
| 6311 | _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); | 6194 | _ = try cg.cmp(lhs, rhs, ty, cmp_op); |
| 6312 | 6195 | ||
| 6313 | // based on the result from comparison, return operand 0 or 1. | 6196 | // based on the result from comparison, return operand 0 or 1. |
| 6314 | try func.addTag(.select); | 6197 | try cg.addTag(.select); |
| 6315 | } | 6198 | } |
| 6316 | 6199 | ||
| 6317 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 6200 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 6318 | } | 6201 | } |
| 6319 | 6202 | ||
| 6320 | fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6203 | fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6321 | const pt = func.pt; | 6204 | const zcu = cg.pt.zcu; |
| 6322 | const zcu = pt.zcu; | 6205 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6323 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 6206 | const bin_op = cg.air.extraData(Air.Bin, pl_op.payload).data; |
| 6324 | const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 6325 | 6207 | ||
| 6326 | const ty = func.typeOfIndex(inst); | 6208 | const ty = cg.typeOfIndex(inst); |
| 6327 | if (ty.zigTypeTag(zcu) == .vector) { | 6209 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6328 | return func.fail("TODO: `@mulAdd` for vectors", .{}); | 6210 | return cg.fail("TODO: `@mulAdd` for vectors", .{}); |
| 6329 | } | 6211 | } |
| 6330 | 6212 | ||
| 6331 | const addend = try func.resolveInst(pl_op.operand); | 6213 | const addend = try cg.resolveInst(pl_op.operand); |
| 6332 | const lhs = try func.resolveInst(bin_op.lhs); | 6214 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6333 | const rhs = try func.resolveInst(bin_op.rhs); | 6215 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6334 | 6216 | ||
| 6335 | const result = if (ty.floatBits(func.target.*) == 16) fl_result: { | 6217 | const result = if (ty.floatBits(cg.target.*) == 16) fl_result: { |
| 6336 | const rhs_ext = try func.fpext(rhs, ty, Type.f32); | 6218 | const rhs_ext = try cg.fpext(rhs, ty, Type.f32); |
| 6337 | const lhs_ext = try func.fpext(lhs, ty, Type.f32); | 6219 | const lhs_ext = try cg.fpext(lhs, ty, Type.f32); |
| 6338 | const addend_ext = try func.fpext(addend, ty, Type.f32); | 6220 | const addend_ext = try cg.fpext(addend, ty, Type.f32); |
| 6339 | // call to compiler-rt `fn fmaf(f32, f32, f32) f32` | 6221 | // call to compiler-rt `fn fmaf(f32, f32, f32) f32` |
| 6340 | const result = try func.callIntrinsic( | 6222 | const result = try cg.callIntrinsic( |
| 6341 | "fmaf", | 6223 | .fmaf, |
| 6342 | &.{ .f32_type, .f32_type, .f32_type }, | 6224 | &.{ .f32_type, .f32_type, .f32_type }, |
| 6343 | Type.f32, | 6225 | Type.f32, |
| 6344 | &.{ rhs_ext, lhs_ext, addend_ext }, | 6226 | &.{ rhs_ext, lhs_ext, addend_ext }, |
| 6345 | ); | 6227 | ); |
| 6346 | break :fl_result try func.fptrunc(result, Type.f32, ty); | 6228 | break :fl_result try cg.fptrunc(result, Type.f32, ty); |
| 6347 | } else result: { | 6229 | } else result: { |
| 6348 | const mul_result = try func.binOp(lhs, rhs, ty, .mul); | 6230 | const mul_result = try cg.binOp(lhs, rhs, ty, .mul); |
| 6349 | break :result try func.binOp(mul_result, addend, ty, .add); | 6231 | break :result try cg.binOp(mul_result, addend, ty, .add); |
| 6350 | }; | 6232 | }; |
| 6351 | 6233 | ||
| 6352 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); | 6234 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); |
| 6353 | } | 6235 | } |
| 6354 | 6236 | ||
| 6355 | fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6237 | fn airClz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6356 | const pt = func.pt; | 6238 | const zcu = cg.pt.zcu; |
| 6357 | const zcu = pt.zcu; | 6239 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6358 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 6359 | 6240 | ||
| 6360 | const ty = func.typeOf(ty_op.operand); | 6241 | const ty = cg.typeOf(ty_op.operand); |
| 6361 | if (ty.zigTypeTag(zcu) == .vector) { | 6242 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6362 | return func.fail("TODO: `@clz` for vectors", .{}); | 6243 | return cg.fail("TODO: `@clz` for vectors", .{}); |
| 6363 | } | 6244 | } |
| 6364 | 6245 | ||
| 6365 | const operand = try func.resolveInst(ty_op.operand); | 6246 | const operand = try cg.resolveInst(ty_op.operand); |
| 6366 | const int_info = ty.intInfo(zcu); | 6247 | const int_info = ty.intInfo(zcu); |
| 6367 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 6248 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6368 | return func.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); | 6249 | return cg.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); |
| 6369 | }; | 6250 | }; |
| 6370 | 6251 | ||
| 6371 | switch (wasm_bits) { | 6252 | switch (wasm_bits) { |
| 6372 | 32 => { | 6253 | 32 => { |
| 6373 | try func.emitWValue(operand); | 6254 | try cg.emitWValue(operand); |
| 6374 | try func.addTag(.i32_clz); | 6255 | try cg.addTag(.i32_clz); |
| 6375 | }, | 6256 | }, |
| 6376 | 64 => { | 6257 | 64 => { |
| 6377 | try func.emitWValue(operand); | 6258 | try cg.emitWValue(operand); |
| 6378 | try func.addTag(.i64_clz); | 6259 | try cg.addTag(.i64_clz); |
| 6379 | try func.addTag(.i32_wrap_i64); | 6260 | try cg.addTag(.i32_wrap_i64); |
| 6380 | }, | 6261 | }, |
| 6381 | 128 => { | 6262 | 128 => { |
| 6382 | var msb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64); | 6263 | var msb = try (try cg.load(operand, Type.u64, 8)).toLocal(cg, Type.u64); |
| 6383 | defer msb.free(func); | 6264 | defer msb.free(cg); |
| 6384 | 6265 | ||
| 6385 | try func.emitWValue(msb); | 6266 | try cg.emitWValue(msb); |
| 6386 | try func.addTag(.i64_clz); | 6267 | try cg.addTag(.i64_clz); |
| 6387 | _ = try func.load(operand, Type.u64, 0); | 6268 | _ = try cg.load(operand, Type.u64, 0); |
| 6388 | try func.addTag(.i64_clz); | 6269 | try cg.addTag(.i64_clz); |
| 6389 | try func.emitWValue(.{ .imm64 = 64 }); | 6270 | try cg.emitWValue(.{ .imm64 = 64 }); |
| 6390 | try func.addTag(.i64_add); | 6271 | try cg.addTag(.i64_add); |
| 6391 | _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); | 6272 | _ = try cg.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); |
| 6392 | try func.addTag(.select); | 6273 | try cg.addTag(.select); |
| 6393 | try func.addTag(.i32_wrap_i64); | 6274 | try cg.addTag(.i32_wrap_i64); |
| 6394 | }, | 6275 | }, |
| 6395 | else => unreachable, | 6276 | else => unreachable, |
| 6396 | } | 6277 | } |
| 6397 | 6278 | ||
| 6398 | if (wasm_bits != int_info.bits) { | 6279 | if (wasm_bits != int_info.bits) { |
| 6399 | try func.emitWValue(.{ .imm32 = wasm_bits - int_info.bits }); | 6280 | try cg.emitWValue(.{ .imm32 = wasm_bits - int_info.bits }); |
| 6400 | try func.addTag(.i32_sub); | 6281 | try cg.addTag(.i32_sub); |
| 6401 | } | 6282 | } |
| 6402 | 6283 | ||
| 6403 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 6284 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 6404 | } | 6285 | } |
| 6405 | 6286 | ||
| 6406 | fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6287 | fn airCtz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6407 | const pt = func.pt; | 6288 | const zcu = cg.pt.zcu; |
| 6408 | const zcu = pt.zcu; | 6289 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6409 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 6410 | 6290 | ||
| 6411 | const ty = func.typeOf(ty_op.operand); | 6291 | const ty = cg.typeOf(ty_op.operand); |
| 6412 | 6292 | ||
| 6413 | if (ty.zigTypeTag(zcu) == .vector) { | 6293 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6414 | return func.fail("TODO: `@ctz` for vectors", .{}); | 6294 | return cg.fail("TODO: `@ctz` for vectors", .{}); |
| 6415 | } | 6295 | } |
| 6416 | 6296 | ||
| 6417 | const operand = try func.resolveInst(ty_op.operand); | 6297 | const operand = try cg.resolveInst(ty_op.operand); |
| 6418 | const int_info = ty.intInfo(zcu); | 6298 | const int_info = ty.intInfo(zcu); |
| 6419 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 6299 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6420 | return func.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); | 6300 | return cg.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); |
| 6421 | }; | 6301 | }; |
| 6422 | 6302 | ||
| 6423 | switch (wasm_bits) { | 6303 | switch (wasm_bits) { |
| ... | @@ -6425,131 +6305,108 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6425,131 +6305,108 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6425 | if (wasm_bits != int_info.bits) { | 6305 | if (wasm_bits != int_info.bits) { |
| 6426 | const val: u32 = @as(u32, 1) << @as(u5, @intCast(int_info.bits)); | 6306 | const val: u32 = @as(u32, 1) << @as(u5, @intCast(int_info.bits)); |
| 6427 | // leave value on the stack | 6307 | // leave value on the stack |
| 6428 | _ = try func.binOp(operand, .{ .imm32 = val }, ty, .@"or"); | 6308 | _ = try cg.binOp(operand, .{ .imm32 = val }, ty, .@"or"); |
| 6429 | } else try func.emitWValue(operand); | 6309 | } else try cg.emitWValue(operand); |
| 6430 | try func.addTag(.i32_ctz); | 6310 | try cg.addTag(.i32_ctz); |
| 6431 | }, | 6311 | }, |
| 6432 | 64 => { | 6312 | 64 => { |
| 6433 | if (wasm_bits != int_info.bits) { | 6313 | if (wasm_bits != int_info.bits) { |
| 6434 | const val: u64 = @as(u64, 1) << @as(u6, @intCast(int_info.bits)); | 6314 | const val: u64 = @as(u64, 1) << @as(u6, @intCast(int_info.bits)); |
| 6435 | // leave value on the stack | 6315 | // leave value on the stack |
| 6436 | _ = try func.binOp(operand, .{ .imm64 = val }, ty, .@"or"); | 6316 | _ = try cg.binOp(operand, .{ .imm64 = val }, ty, .@"or"); |
| 6437 | } else try func.emitWValue(operand); | 6317 | } else try cg.emitWValue(operand); |
| 6438 | try func.addTag(.i64_ctz); | 6318 | try cg.addTag(.i64_ctz); |
| 6439 | try func.addTag(.i32_wrap_i64); | 6319 | try cg.addTag(.i32_wrap_i64); |
| 6440 | }, | 6320 | }, |
| 6441 | 128 => { | 6321 | 128 => { |
| 6442 | var lsb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64); | 6322 | var lsb = try (try cg.load(operand, Type.u64, 0)).toLocal(cg, Type.u64); |
| 6443 | defer lsb.free(func); | 6323 | defer lsb.free(cg); |
| 6444 | 6324 | ||
| 6445 | try func.emitWValue(lsb); | 6325 | try cg.emitWValue(lsb); |
| 6446 | try func.addTag(.i64_ctz); | 6326 | try cg.addTag(.i64_ctz); |
| 6447 | _ = try func.load(operand, Type.u64, 8); | 6327 | _ = try cg.load(operand, Type.u64, 8); |
| 6448 | if (wasm_bits != int_info.bits) { | 6328 | if (wasm_bits != int_info.bits) { |
| 6449 | try func.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64))); | 6329 | try cg.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64))); |
| 6450 | try func.addTag(.i64_or); | 6330 | try cg.addTag(.i64_or); |
| 6451 | } | 6331 | } |
| 6452 | try func.addTag(.i64_ctz); | 6332 | try cg.addTag(.i64_ctz); |
| 6453 | try func.addImm64(64); | 6333 | try cg.addImm64(64); |
| 6454 | if (wasm_bits != int_info.bits) { | 6334 | if (wasm_bits != int_info.bits) { |
| 6455 | try func.addTag(.i64_or); | 6335 | try cg.addTag(.i64_or); |
| 6456 | } else { | 6336 | } else { |
| 6457 | try func.addTag(.i64_add); | 6337 | try cg.addTag(.i64_add); |
| 6458 | } | 6338 | } |
| 6459 | _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq); | 6339 | _ = try cg.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq); |
| 6460 | try func.addTag(.select); | 6340 | try cg.addTag(.select); |
| 6461 | try func.addTag(.i32_wrap_i64); | 6341 | try cg.addTag(.i32_wrap_i64); |
| 6462 | }, | 6342 | }, |
| 6463 | else => unreachable, | 6343 | else => unreachable, |
| 6464 | } | 6344 | } |
| 6465 | 6345 | ||
| 6466 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | 6346 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| 6467 | } | 6347 | } |
| 6468 | 6348 | ||
| 6469 | fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6349 | fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6470 | if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); | 6350 | const dbg_stmt = cg.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; |
| 6471 | 6351 | try cg.addInst(.{ .tag = .dbg_line, .data = .{ | |
| 6472 | const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; | 6352 | .payload = try cg.addExtra(Mir.DbgLineColumn{ |
| 6473 | try func.addInst(.{ .tag = .dbg_line, .data = .{ | ||
| 6474 | .payload = try func.addExtra(Mir.DbgLineColumn{ | ||
| 6475 | .line = dbg_stmt.line, | 6353 | .line = dbg_stmt.line, |
| 6476 | .column = dbg_stmt.column, | 6354 | .column = dbg_stmt.column, |
| 6477 | }), | 6355 | }), |
| 6478 | } }); | 6356 | } }); |
| 6479 | return func.finishAir(inst, .none, &.{}); | 6357 | return cg.finishAir(inst, .none, &.{}); |
| 6480 | } | 6358 | } |
| 6481 | 6359 | ||
| 6482 | fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6360 | fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6483 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 6361 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6484 | const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload); | 6362 | const extra = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload); |
| 6485 | // TODO | 6363 | // TODO |
| 6486 | try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); | 6364 | try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len])); |
| 6487 | } | 6365 | } |
| 6488 | 6366 | ||
| 6489 | fn airDbgVar( | 6367 | fn airDbgVar( |
| 6490 | func: *CodeGen, | 6368 | cg: *CodeGen, |
| 6491 | inst: Air.Inst.Index, | 6369 | inst: Air.Inst.Index, |
| 6492 | local_tag: link.File.Dwarf.WipNav.LocalTag, | 6370 | local_tag: link.File.Dwarf.WipNav.LocalTag, |
| 6493 | is_ptr: bool, | 6371 | is_ptr: bool, |
| 6494 | ) InnerError!void { | 6372 | ) InnerError!void { |
| 6495 | _ = is_ptr; | 6373 | _ = is_ptr; |
| 6496 | if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); | 6374 | _ = local_tag; |
| 6497 | 6375 | return cg.finishAir(inst, .none, &.{}); | |
| 6498 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | ||
| 6499 | const ty = func.typeOf(pl_op.operand); | ||
| 6500 | const operand = try func.resolveInst(pl_op.operand); | ||
| 6501 | |||
| 6502 | log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), operand }); | ||
| 6503 | |||
| 6504 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); | ||
| 6505 | log.debug(" var name = ({s})", .{name.toSlice(func.air)}); | ||
| 6506 | |||
| 6507 | const loc: link.File.Dwarf.Loc = switch (operand) { | ||
| 6508 | .local => |local| .{ .wasm_ext = .{ .local = local.value } }, | ||
| 6509 | else => blk: { | ||
| 6510 | log.debug("TODO generate debug info for {}", .{operand}); | ||
| 6511 | break :blk .empty; | ||
| 6512 | }, | ||
| 6513 | }; | ||
| 6514 | try func.debug_output.dwarf.genLocalDebugInfo(local_tag, name.toSlice(func.air), ty, loc); | ||
| 6515 | |||
| 6516 | return func.finishAir(inst, .none, &.{}); | ||
| 6517 | } | 6376 | } |
| 6518 | 6377 | ||
| 6519 | fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6378 | fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6520 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 6379 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6521 | const err_union = try func.resolveInst(pl_op.operand); | 6380 | const err_union = try cg.resolveInst(pl_op.operand); |
| 6522 | const extra = func.air.extraData(Air.Try, pl_op.payload); | 6381 | const extra = cg.air.extraData(Air.Try, pl_op.payload); |
| 6523 | const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]); | 6382 | const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]); |
| 6524 | const err_union_ty = func.typeOf(pl_op.operand); | 6383 | const err_union_ty = cg.typeOf(pl_op.operand); |
| 6525 | const result = try lowerTry(func, inst, err_union, body, err_union_ty, false); | 6384 | const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false); |
| 6526 | return func.finishAir(inst, result, &.{pl_op.operand}); | 6385 | return cg.finishAir(inst, result, &.{pl_op.operand}); |
| 6527 | } | 6386 | } |
| 6528 | 6387 | ||
| 6529 | fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6388 | fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6530 | const pt = func.pt; | 6389 | const zcu = cg.pt.zcu; |
| 6531 | const zcu = pt.zcu; | 6390 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6532 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 6391 | const extra = cg.air.extraData(Air.TryPtr, ty_pl.payload); |
| 6533 | const extra = func.air.extraData(Air.TryPtr, ty_pl.payload); | 6392 | const err_union_ptr = try cg.resolveInst(extra.data.ptr); |
| 6534 | const err_union_ptr = try func.resolveInst(extra.data.ptr); | 6393 | const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]); |
| 6535 | const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]); | 6394 | const err_union_ty = cg.typeOf(extra.data.ptr).childType(zcu); |
| 6536 | const err_union_ty = func.typeOf(extra.data.ptr).childType(zcu); | 6395 | const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true); |
| 6537 | const result = try lowerTry(func, inst, err_union_ptr, body, err_union_ty, true); | 6396 | return cg.finishAir(inst, result, &.{extra.data.ptr}); |
| 6538 | return func.finishAir(inst, result, &.{extra.data.ptr}); | ||
| 6539 | } | 6397 | } |
| 6540 | 6398 | ||
| 6541 | fn lowerTry( | 6399 | fn lowerTry( |
| 6542 | func: *CodeGen, | 6400 | cg: *CodeGen, |
| 6543 | inst: Air.Inst.Index, | 6401 | inst: Air.Inst.Index, |
| 6544 | err_union: WValue, | 6402 | err_union: WValue, |
| 6545 | body: []const Air.Inst.Index, | 6403 | body: []const Air.Inst.Index, |
| 6546 | err_union_ty: Type, | 6404 | err_union_ty: Type, |
| 6547 | operand_is_ptr: bool, | 6405 | operand_is_ptr: bool, |
| 6548 | ) InnerError!WValue { | 6406 | ) InnerError!WValue { |
| 6549 | const pt = func.pt; | 6407 | const zcu = cg.pt.zcu; |
| 6550 | const zcu = pt.zcu; | ||
| 6551 | if (operand_is_ptr) { | 6408 | if (operand_is_ptr) { |
| 6552 | return func.fail("TODO: lowerTry for pointers", .{}); | 6409 | return cg.fail("TODO: lowerTry for pointers", .{}); |
| 6553 | } | 6410 | } |
| 6554 | 6411 | ||
| 6555 | const pl_ty = err_union_ty.errorUnionPayload(zcu); | 6412 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| ... | @@ -6557,29 +6414,29 @@ fn lowerTry( | ... | @@ -6557,29 +6414,29 @@ fn lowerTry( |
| 6557 | 6414 | ||
| 6558 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { | 6415 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { |
| 6559 | // Block we can jump out of when error is not set | 6416 | // Block we can jump out of when error is not set |
| 6560 | try func.startBlock(.block, wasm.block_empty); | 6417 | try cg.startBlock(.block, .empty); |
| 6561 | 6418 | ||
| 6562 | // check if the error tag is set for the error union. | 6419 | // check if the error tag is set for the error union. |
| 6563 | try func.emitWValue(err_union); | 6420 | try cg.emitWValue(err_union); |
| 6564 | if (pl_has_bits) { | 6421 | if (pl_has_bits) { |
| 6565 | const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); | 6422 | const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); |
| 6566 | try func.addMemArg(.i32_load16_u, .{ | 6423 | try cg.addMemArg(.i32_load16_u, .{ |
| 6567 | .offset = err_union.offset() + err_offset, | 6424 | .offset = err_union.offset() + err_offset, |
| 6568 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), | 6425 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), |
| 6569 | }); | 6426 | }); |
| 6570 | } | 6427 | } |
| 6571 | try func.addTag(.i32_eqz); | 6428 | try cg.addTag(.i32_eqz); |
| 6572 | try func.addLabel(.br_if, 0); // jump out of block when error is '0' | 6429 | try cg.addLabel(.br_if, 0); // jump out of block when error is '0' |
| 6573 | 6430 | ||
| 6574 | const liveness = func.liveness.getCondBr(inst); | 6431 | const liveness = cg.liveness.getCondBr(inst); |
| 6575 | try func.branches.append(func.gpa, .{}); | 6432 | try cg.branches.append(cg.gpa, .{}); |
| 6576 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len); | 6433 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.else_deaths.len + liveness.then_deaths.len); |
| 6577 | defer { | 6434 | defer { |
| 6578 | var branch = func.branches.pop(); | 6435 | var branch = cg.branches.pop(); |
| 6579 | branch.deinit(func.gpa); | 6436 | branch.deinit(cg.gpa); |
| 6580 | } | 6437 | } |
| 6581 | try func.genBody(body); | 6438 | try cg.genBody(body); |
| 6582 | try func.endBlock(); | 6439 | try cg.endBlock(); |
| 6583 | } | 6440 | } |
| 6584 | 6441 | ||
| 6585 | // if we reach here it means error was not set, and we want the payload | 6442 | // if we reach here it means error was not set, and we want the payload |
| ... | @@ -6588,39 +6445,38 @@ fn lowerTry( | ... | @@ -6588,39 +6445,38 @@ fn lowerTry( |
| 6588 | } | 6445 | } |
| 6589 | 6446 | ||
| 6590 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); | 6447 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); |
| 6591 | if (isByRef(pl_ty, pt, func.target.*)) { | 6448 | if (isByRef(pl_ty, zcu, cg.target)) { |
| 6592 | return buildPointerOffset(func, err_union, pl_offset, .new); | 6449 | return buildPointerOffset(cg, err_union, pl_offset, .new); |
| 6593 | } | 6450 | } |
| 6594 | const payload = try func.load(err_union, pl_ty, pl_offset); | 6451 | const payload = try cg.load(err_union, pl_ty, pl_offset); |
| 6595 | return payload.toLocal(func, pl_ty); | 6452 | return payload.toLocal(cg, pl_ty); |
| 6596 | } | 6453 | } |
| 6597 | 6454 | ||
| 6598 | fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6455 | fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6599 | const pt = func.pt; | 6456 | const zcu = cg.pt.zcu; |
| 6600 | const zcu = pt.zcu; | 6457 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6601 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 6602 | 6458 | ||
| 6603 | const ty = func.typeOfIndex(inst); | 6459 | const ty = cg.typeOfIndex(inst); |
| 6604 | const operand = try func.resolveInst(ty_op.operand); | 6460 | const operand = try cg.resolveInst(ty_op.operand); |
| 6605 | 6461 | ||
| 6606 | if (ty.zigTypeTag(zcu) == .vector) { | 6462 | if (ty.zigTypeTag(zcu) == .vector) { |
| 6607 | return func.fail("TODO: @byteSwap for vectors", .{}); | 6463 | return cg.fail("TODO: @byteSwap for vectors", .{}); |
| 6608 | } | 6464 | } |
| 6609 | const int_info = ty.intInfo(zcu); | 6465 | const int_info = ty.intInfo(zcu); |
| 6610 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 6466 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6611 | return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}); | 6467 | return cg.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}); |
| 6612 | }; | 6468 | }; |
| 6613 | 6469 | ||
| 6614 | // bytes are no-op | 6470 | // bytes are no-op |
| 6615 | if (int_info.bits == 8) { | 6471 | if (int_info.bits == 8) { |
| 6616 | return func.finishAir(inst, func.reuseOperand(ty_op.operand, operand), &.{ty_op.operand}); | 6472 | return cg.finishAir(inst, cg.reuseOperand(ty_op.operand, operand), &.{ty_op.operand}); |
| 6617 | } | 6473 | } |
| 6618 | 6474 | ||
| 6619 | const result = result: { | 6475 | const result = result: { |
| 6620 | switch (wasm_bits) { | 6476 | switch (wasm_bits) { |
| 6621 | 32 => { | 6477 | 32 => { |
| 6622 | const intrin_ret = try func.callIntrinsic( | 6478 | const intrin_ret = try cg.callIntrinsic( |
| 6623 | "__bswapsi2", | 6479 | .__bswapsi2, |
| 6624 | &.{.u32_type}, | 6480 | &.{.u32_type}, |
| 6625 | Type.u32, | 6481 | Type.u32, |
| 6626 | &.{operand}, | 6482 | &.{operand}, |
| ... | @@ -6628,11 +6484,11 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6628,11 +6484,11 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6628 | break :result if (int_info.bits == 32) | 6484 | break :result if (int_info.bits == 32) |
| 6629 | intrin_ret | 6485 | intrin_ret |
| 6630 | else | 6486 | else |
| 6631 | try func.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr); | 6487 | try cg.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr); |
| 6632 | }, | 6488 | }, |
| 6633 | 64 => { | 6489 | 64 => { |
| 6634 | const intrin_ret = try func.callIntrinsic( | 6490 | const intrin_ret = try cg.callIntrinsic( |
| 6635 | "__bswapdi2", | 6491 | .__bswapdi2, |
| 6636 | &.{.u64_type}, | 6492 | &.{.u64_type}, |
| 6637 | Type.u64, | 6493 | Type.u64, |
| 6638 | &.{operand}, | 6494 | &.{operand}, |
| ... | @@ -6640,61 +6496,60 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6640,61 +6496,60 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6640 | break :result if (int_info.bits == 64) | 6496 | break :result if (int_info.bits == 64) |
| 6641 | intrin_ret | 6497 | intrin_ret |
| 6642 | else | 6498 | else |
| 6643 | try func.binOp(intrin_ret, .{ .imm64 = 64 - int_info.bits }, ty, .shr); | 6499 | try cg.binOp(intrin_ret, .{ .imm64 = 64 - int_info.bits }, ty, .shr); |
| 6644 | }, | 6500 | }, |
| 6645 | else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), | 6501 | else => return cg.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), |
| 6646 | } | 6502 | } |
| 6647 | }; | 6503 | }; |
| 6648 | return func.finishAir(inst, result, &.{ty_op.operand}); | 6504 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 6649 | } | 6505 | } |
| 6650 | 6506 | ||
| 6651 | fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6507 | fn airDiv(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6652 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6508 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6653 | 6509 | ||
| 6654 | const ty = func.typeOfIndex(inst); | 6510 | const ty = cg.typeOfIndex(inst); |
| 6655 | const lhs = try func.resolveInst(bin_op.lhs); | 6511 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6656 | const rhs = try func.resolveInst(bin_op.rhs); | 6512 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6657 | 6513 | ||
| 6658 | const result = try func.binOp(lhs, rhs, ty, .div); | 6514 | const result = try cg.binOp(lhs, rhs, ty, .div); |
| 6659 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 6515 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6660 | } | 6516 | } |
| 6661 | 6517 | ||
| 6662 | fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6518 | fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6663 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6519 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6664 | 6520 | ||
| 6665 | const ty = func.typeOfIndex(inst); | 6521 | const ty = cg.typeOfIndex(inst); |
| 6666 | const lhs = try func.resolveInst(bin_op.lhs); | 6522 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6667 | const rhs = try func.resolveInst(bin_op.rhs); | 6523 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6668 | 6524 | ||
| 6669 | const div_result = try func.binOp(lhs, rhs, ty, .div); | 6525 | const div_result = try cg.binOp(lhs, rhs, ty, .div); |
| 6670 | 6526 | ||
| 6671 | if (ty.isAnyFloat()) { | 6527 | if (ty.isAnyFloat()) { |
| 6672 | const trunc_result = try func.floatOp(.trunc, ty, &.{div_result}); | 6528 | const trunc_result = try cg.floatOp(.trunc, ty, &.{div_result}); |
| 6673 | return func.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs }); | 6529 | return cg.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6674 | } | 6530 | } |
| 6675 | 6531 | ||
| 6676 | return func.finishAir(inst, div_result, &.{ bin_op.lhs, bin_op.rhs }); | 6532 | return cg.finishAir(inst, div_result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6677 | } | 6533 | } |
| 6678 | 6534 | ||
| 6679 | fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6535 | fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6680 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6536 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6681 | 6537 | ||
| 6682 | const pt = func.pt; | 6538 | const zcu = cg.pt.zcu; |
| 6683 | const zcu = pt.zcu; | 6539 | const ty = cg.typeOfIndex(inst); |
| 6684 | const ty = func.typeOfIndex(inst); | 6540 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6685 | const lhs = try func.resolveInst(bin_op.lhs); | 6541 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6686 | const rhs = try func.resolveInst(bin_op.rhs); | ||
| 6687 | 6542 | ||
| 6688 | if (ty.isUnsignedInt(zcu)) { | 6543 | if (ty.isUnsignedInt(zcu)) { |
| 6689 | _ = try func.binOp(lhs, rhs, ty, .div); | 6544 | _ = try cg.binOp(lhs, rhs, ty, .div); |
| 6690 | } else if (ty.isSignedInt(zcu)) { | 6545 | } else if (ty.isSignedInt(zcu)) { |
| 6691 | const int_bits = ty.intInfo(zcu).bits; | 6546 | const int_bits = ty.intInfo(zcu).bits; |
| 6692 | const wasm_bits = toWasmBits(int_bits) orelse { | 6547 | const wasm_bits = toWasmBits(int_bits) orelse { |
| 6693 | return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); | 6548 | return cg.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 6694 | }; | 6549 | }; |
| 6695 | 6550 | ||
| 6696 | if (wasm_bits > 64) { | 6551 | if (wasm_bits > 64) { |
| 6697 | return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); | 6552 | return cg.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 6698 | } | 6553 | } |
| 6699 | 6554 | ||
| 6700 | const zero: WValue = switch (wasm_bits) { | 6555 | const zero: WValue = switch (wasm_bits) { |
| ... | @@ -6704,108 +6559,108 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6704,108 +6559,108 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6704 | }; | 6559 | }; |
| 6705 | 6560 | ||
| 6706 | // tee leaves the value on the stack and stores it in a local. | 6561 | // tee leaves the value on the stack and stores it in a local. |
| 6707 | const quotient = try func.allocLocal(ty); | 6562 | const quotient = try cg.allocLocal(ty); |
| 6708 | _ = try func.binOp(lhs, rhs, ty, .div); | 6563 | _ = try cg.binOp(lhs, rhs, ty, .div); |
| 6709 | try func.addLabel(.local_tee, quotient.local.value); | 6564 | try cg.addLocal(.local_tee, quotient.local.value); |
| 6710 | 6565 | ||
| 6711 | // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow | 6566 | // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow |
| 6712 | // the 64 bit value we want to use as the condition to 32 bits. | 6567 | // the 64 bit value we want to use as the condition to 32 bits. |
| 6713 | // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and | 6568 | // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and |
| 6714 | // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits. | 6569 | // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits. |
| 6715 | if (wasm_bits == 64) { | 6570 | if (wasm_bits == 64) { |
| 6716 | try func.emitWValue(quotient); | 6571 | try cg.emitWValue(quotient); |
| 6717 | } | 6572 | } |
| 6718 | 6573 | ||
| 6719 | // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise. | 6574 | // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise. |
| 6720 | _ = try func.binOp(lhs, rhs, ty, .xor); | 6575 | _ = try cg.binOp(lhs, rhs, ty, .xor); |
| 6721 | _ = try func.cmp(.stack, zero, ty, .lt); | 6576 | _ = try cg.cmp(.stack, zero, ty, .lt); |
| 6722 | 6577 | ||
| 6723 | switch (wasm_bits) { | 6578 | switch (wasm_bits) { |
| 6724 | 32 => { | 6579 | 32 => { |
| 6725 | try func.addTag(.i32_sub); | 6580 | try cg.addTag(.i32_sub); |
| 6726 | try func.emitWValue(quotient); | 6581 | try cg.emitWValue(quotient); |
| 6727 | }, | 6582 | }, |
| 6728 | 64 => { | 6583 | 64 => { |
| 6729 | try func.addTag(.i64_extend_i32_u); | 6584 | try cg.addTag(.i64_extend_i32_u); |
| 6730 | try func.addTag(.i64_sub); | 6585 | try cg.addTag(.i64_sub); |
| 6731 | }, | 6586 | }, |
| 6732 | else => unreachable, | 6587 | else => unreachable, |
| 6733 | } | 6588 | } |
| 6734 | 6589 | ||
| 6735 | _ = try func.binOp(lhs, rhs, ty, .rem); | 6590 | _ = try cg.binOp(lhs, rhs, ty, .rem); |
| 6736 | 6591 | ||
| 6737 | if (wasm_bits == 64) { | 6592 | if (wasm_bits == 64) { |
| 6738 | try func.addTag(.i64_eqz); | 6593 | try cg.addTag(.i64_eqz); |
| 6739 | } | 6594 | } |
| 6740 | 6595 | ||
| 6741 | try func.addTag(.select); | 6596 | try cg.addTag(.select); |
| 6742 | 6597 | ||
| 6743 | // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and | 6598 | // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and |
| 6744 | // expect all but the lowest N bits to be 0. | 6599 | // expect all but the lowest N bits to be 0. |
| 6745 | // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits | 6600 | // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits |
| 6746 | // when performing comparisons? | 6601 | // when performing comparisons? |
| 6747 | if (int_bits != wasm_bits) { | 6602 | if (int_bits != wasm_bits) { |
| 6748 | _ = try func.wrapOperand(.stack, ty); | 6603 | _ = try cg.wrapOperand(.stack, ty); |
| 6749 | } | 6604 | } |
| 6750 | } else { | 6605 | } else { |
| 6751 | const float_bits = ty.floatBits(func.target.*); | 6606 | const float_bits = ty.floatBits(cg.target.*); |
| 6752 | if (float_bits > 64) { | 6607 | if (float_bits > 64) { |
| 6753 | return func.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits}); | 6608 | return cg.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits}); |
| 6754 | } | 6609 | } |
| 6755 | const is_f16 = float_bits == 16; | 6610 | const is_f16 = float_bits == 16; |
| 6756 | 6611 | ||
| 6757 | const lhs_wasm = if (is_f16) try func.fpext(lhs, Type.f16, Type.f32) else lhs; | 6612 | const lhs_wasm = if (is_f16) try cg.fpext(lhs, Type.f16, Type.f32) else lhs; |
| 6758 | const rhs_wasm = if (is_f16) try func.fpext(rhs, Type.f16, Type.f32) else rhs; | 6613 | const rhs_wasm = if (is_f16) try cg.fpext(rhs, Type.f16, Type.f32) else rhs; |
| 6759 | 6614 | ||
| 6760 | try func.emitWValue(lhs_wasm); | 6615 | try cg.emitWValue(lhs_wasm); |
| 6761 | try func.emitWValue(rhs_wasm); | 6616 | try cg.emitWValue(rhs_wasm); |
| 6762 | 6617 | ||
| 6763 | switch (float_bits) { | 6618 | switch (float_bits) { |
| 6764 | 16, 32 => { | 6619 | 16, 32 => { |
| 6765 | try func.addTag(.f32_div); | 6620 | try cg.addTag(.f32_div); |
| 6766 | try func.addTag(.f32_floor); | 6621 | try cg.addTag(.f32_floor); |
| 6767 | }, | 6622 | }, |
| 6768 | 64 => { | 6623 | 64 => { |
| 6769 | try func.addTag(.f64_div); | 6624 | try cg.addTag(.f64_div); |
| 6770 | try func.addTag(.f64_floor); | 6625 | try cg.addTag(.f64_floor); |
| 6771 | }, | 6626 | }, |
| 6772 | else => unreachable, | 6627 | else => unreachable, |
| 6773 | } | 6628 | } |
| 6774 | 6629 | ||
| 6775 | if (is_f16) { | 6630 | if (is_f16) { |
| 6776 | _ = try func.fptrunc(.stack, Type.f32, Type.f16); | 6631 | _ = try cg.fptrunc(.stack, Type.f32, Type.f16); |
| 6777 | } | 6632 | } |
| 6778 | } | 6633 | } |
| 6779 | 6634 | ||
| 6780 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 6635 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 6781 | } | 6636 | } |
| 6782 | 6637 | ||
| 6783 | fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6638 | fn airRem(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6784 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6639 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6785 | 6640 | ||
| 6786 | const ty = func.typeOfIndex(inst); | 6641 | const ty = cg.typeOfIndex(inst); |
| 6787 | const lhs = try func.resolveInst(bin_op.lhs); | 6642 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6788 | const rhs = try func.resolveInst(bin_op.rhs); | 6643 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6789 | 6644 | ||
| 6790 | const result = try func.binOp(lhs, rhs, ty, .rem); | 6645 | const result = try cg.binOp(lhs, rhs, ty, .rem); |
| 6791 | 6646 | ||
| 6792 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 6647 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6793 | } | 6648 | } |
| 6794 | 6649 | ||
| 6795 | /// Remainder after floor division, defined by: | 6650 | /// Remainder after floor division, defined by: |
| 6796 | /// @divFloor(a, b) * b + @mod(a, b) = a | 6651 | /// @divFloor(a, b) * b + @mod(a, b) = a |
| 6797 | fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6652 | fn airMod(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6798 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6653 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6799 | 6654 | ||
| 6800 | const pt = func.pt; | 6655 | const pt = cg.pt; |
| 6801 | const zcu = pt.zcu; | 6656 | const zcu = pt.zcu; |
| 6802 | const ty = func.typeOfIndex(inst); | 6657 | const ty = cg.typeOfIndex(inst); |
| 6803 | const lhs = try func.resolveInst(bin_op.lhs); | 6658 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6804 | const rhs = try func.resolveInst(bin_op.rhs); | 6659 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6805 | 6660 | ||
| 6806 | const result = result: { | 6661 | const result = result: { |
| 6807 | if (ty.isUnsignedInt(zcu)) { | 6662 | if (ty.isUnsignedInt(zcu)) { |
| 6808 | break :result try func.binOp(lhs, rhs, ty, .rem); | 6663 | break :result try cg.binOp(lhs, rhs, ty, .rem); |
| 6809 | } | 6664 | } |
| 6810 | if (ty.isSignedInt(zcu)) { | 6665 | if (ty.isSignedInt(zcu)) { |
| 6811 | // The wasm rem instruction gives the remainder after truncating division (rounding towards | 6666 | // The wasm rem instruction gives the remainder after truncating division (rounding towards |
| ... | @@ -6814,153 +6669,152 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6814,153 +6669,152 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6814 | // @mod(a, b) = @rem(@rem(a, b) + b, b) | 6669 | // @mod(a, b) = @rem(@rem(a, b) + b, b) |
| 6815 | const int_bits = ty.intInfo(zcu).bits; | 6670 | const int_bits = ty.intInfo(zcu).bits; |
| 6816 | const wasm_bits = toWasmBits(int_bits) orelse { | 6671 | const wasm_bits = toWasmBits(int_bits) orelse { |
| 6817 | return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); | 6672 | return cg.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 6818 | }; | 6673 | }; |
| 6819 | 6674 | ||
| 6820 | if (wasm_bits > 64) { | 6675 | if (wasm_bits > 64) { |
| 6821 | return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); | 6676 | return cg.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 6822 | } | 6677 | } |
| 6823 | 6678 | ||
| 6824 | _ = try func.binOp(lhs, rhs, ty, .rem); | 6679 | _ = try cg.binOp(lhs, rhs, ty, .rem); |
| 6825 | _ = try func.binOp(.stack, rhs, ty, .add); | 6680 | _ = try cg.binOp(.stack, rhs, ty, .add); |
| 6826 | break :result try func.binOp(.stack, rhs, ty, .rem); | 6681 | break :result try cg.binOp(.stack, rhs, ty, .rem); |
| 6827 | } | 6682 | } |
| 6828 | if (ty.isAnyFloat()) { | 6683 | if (ty.isAnyFloat()) { |
| 6829 | const rem = try func.binOp(lhs, rhs, ty, .rem); | 6684 | const rem = try cg.binOp(lhs, rhs, ty, .rem); |
| 6830 | const add = try func.binOp(rem, rhs, ty, .add); | 6685 | const add = try cg.binOp(rem, rhs, ty, .add); |
| 6831 | break :result try func.binOp(add, rhs, ty, .rem); | 6686 | break :result try cg.binOp(add, rhs, ty, .rem); |
| 6832 | } | 6687 | } |
| 6833 | return func.fail("TODO: @mod for {}", .{ty.fmt(pt)}); | 6688 | return cg.fail("TODO: @mod for {}", .{ty.fmt(pt)}); |
| 6834 | }; | 6689 | }; |
| 6835 | 6690 | ||
| 6836 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 6691 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6837 | } | 6692 | } |
| 6838 | 6693 | ||
| 6839 | fn airSatMul(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6694 | fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6840 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6695 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6841 | 6696 | ||
| 6842 | const pt = func.pt; | 6697 | const pt = cg.pt; |
| 6843 | const zcu = pt.zcu; | 6698 | const zcu = pt.zcu; |
| 6844 | const ty = func.typeOfIndex(inst); | 6699 | const ty = cg.typeOfIndex(inst); |
| 6845 | const int_info = ty.intInfo(zcu); | 6700 | const int_info = ty.intInfo(zcu); |
| 6846 | const is_signed = int_info.signedness == .signed; | 6701 | const is_signed = int_info.signedness == .signed; |
| 6847 | 6702 | ||
| 6848 | const lhs = try func.resolveInst(bin_op.lhs); | 6703 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6849 | const rhs = try func.resolveInst(bin_op.rhs); | 6704 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6850 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 6705 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6851 | return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); | 6706 | return cg.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); |
| 6852 | }; | 6707 | }; |
| 6853 | 6708 | ||
| 6854 | switch (wasm_bits) { | 6709 | switch (wasm_bits) { |
| 6855 | 32 => { | 6710 | 32 => { |
| 6856 | const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64; | 6711 | const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64; |
| 6857 | const lhs_up = try func.intcast(lhs, ty, upcast_ty); | 6712 | const lhs_up = try cg.intcast(lhs, ty, upcast_ty); |
| 6858 | const rhs_up = try func.intcast(rhs, ty, upcast_ty); | 6713 | const rhs_up = try cg.intcast(rhs, ty, upcast_ty); |
| 6859 | var mul_res = try (try func.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(func, upcast_ty); | 6714 | var mul_res = try (try cg.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(cg, upcast_ty); |
| 6860 | defer mul_res.free(func); | 6715 | defer mul_res.free(cg); |
| 6861 | if (is_signed) { | 6716 | if (is_signed) { |
| 6862 | const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) }; | 6717 | const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) }; |
| 6863 | try func.emitWValue(mul_res); | 6718 | try cg.emitWValue(mul_res); |
| 6864 | try func.emitWValue(imm_max); | 6719 | try cg.emitWValue(imm_max); |
| 6865 | _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt); | 6720 | _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt); |
| 6866 | try func.addTag(.select); | 6721 | try cg.addTag(.select); |
| 6867 | 6722 | ||
| 6868 | var tmp = try func.allocLocal(upcast_ty); | 6723 | var tmp = try cg.allocLocal(upcast_ty); |
| 6869 | defer tmp.free(func); | 6724 | defer tmp.free(cg); |
| 6870 | try func.addLabel(.local_set, tmp.local.value); | 6725 | try cg.addLocal(.local_set, tmp.local.value); |
| 6871 | 6726 | ||
| 6872 | const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) }; | 6727 | const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) }; |
| 6873 | try func.emitWValue(tmp); | 6728 | try cg.emitWValue(tmp); |
| 6874 | try func.emitWValue(imm_min); | 6729 | try cg.emitWValue(imm_min); |
| 6875 | _ = try func.cmp(tmp, imm_min, upcast_ty, .gt); | 6730 | _ = try cg.cmp(tmp, imm_min, upcast_ty, .gt); |
| 6876 | try func.addTag(.select); | 6731 | try cg.addTag(.select); |
| 6877 | } else { | 6732 | } else { |
| 6878 | const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) }; | 6733 | const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) }; |
| 6879 | try func.emitWValue(mul_res); | 6734 | try cg.emitWValue(mul_res); |
| 6880 | try func.emitWValue(imm_max); | 6735 | try cg.emitWValue(imm_max); |
| 6881 | _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt); | 6736 | _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt); |
| 6882 | try func.addTag(.select); | 6737 | try cg.addTag(.select); |
| 6883 | } | 6738 | } |
| 6884 | try func.addTag(.i32_wrap_i64); | 6739 | try cg.addTag(.i32_wrap_i64); |
| 6885 | }, | 6740 | }, |
| 6886 | 64 => { | 6741 | 64 => { |
| 6887 | if (!(int_info.bits == 64 and int_info.signedness == .signed)) { | 6742 | if (!(int_info.bits == 64 and int_info.signedness == .signed)) { |
| 6888 | return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); | 6743 | return cg.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); |
| 6889 | } | 6744 | } |
| 6890 | const overflow_ret = try func.allocStack(Type.i32); | 6745 | const overflow_ret = try cg.allocStack(Type.i32); |
| 6891 | _ = try func.callIntrinsic( | 6746 | _ = try cg.callIntrinsic( |
| 6892 | "__mulodi4", | 6747 | .__mulodi4, |
| 6893 | &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type }, | 6748 | &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type }, |
| 6894 | Type.i64, | 6749 | Type.i64, |
| 6895 | &.{ lhs, rhs, overflow_ret }, | 6750 | &.{ lhs, rhs, overflow_ret }, |
| 6896 | ); | 6751 | ); |
| 6897 | const xor = try func.binOp(lhs, rhs, Type.i64, .xor); | 6752 | const xor = try cg.binOp(lhs, rhs, Type.i64, .xor); |
| 6898 | const sign_v = try func.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr); | 6753 | const sign_v = try cg.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr); |
| 6899 | _ = try func.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor); | 6754 | _ = try cg.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor); |
| 6900 | _ = try func.load(overflow_ret, Type.i32, 0); | 6755 | _ = try cg.load(overflow_ret, Type.i32, 0); |
| 6901 | try func.addTag(.i32_eqz); | 6756 | try cg.addTag(.i32_eqz); |
| 6902 | try func.addTag(.select); | 6757 | try cg.addTag(.select); |
| 6903 | }, | 6758 | }, |
| 6904 | 128 => { | 6759 | 128 => { |
| 6905 | if (!(int_info.bits == 128 and int_info.signedness == .signed)) { | 6760 | if (!(int_info.bits == 128 and int_info.signedness == .signed)) { |
| 6906 | return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); | 6761 | return cg.fail("TODO: mul_sat for {}", .{ty.fmt(pt)}); |
| 6907 | } | 6762 | } |
| 6908 | const overflow_ret = try func.allocStack(Type.i32); | 6763 | const overflow_ret = try cg.allocStack(Type.i32); |
| 6909 | const ret = try func.callIntrinsic( | 6764 | const ret = try cg.callIntrinsic( |
| 6910 | "__muloti4", | 6765 | .__muloti4, |
| 6911 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, | 6766 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, |
| 6912 | Type.i128, | 6767 | Type.i128, |
| 6913 | &.{ lhs, rhs, overflow_ret }, | 6768 | &.{ lhs, rhs, overflow_ret }, |
| 6914 | ); | 6769 | ); |
| 6915 | try func.lowerToStack(ret); | 6770 | try cg.lowerToStack(ret); |
| 6916 | const xor = try func.binOp(lhs, rhs, Type.i128, .xor); | 6771 | const xor = try cg.binOp(lhs, rhs, Type.i128, .xor); |
| 6917 | const sign_v = try func.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr); | 6772 | const sign_v = try cg.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr); |
| 6918 | 6773 | ||
| 6919 | // xor ~@as(u127, 0) | 6774 | // xor ~@as(u127, 0) |
| 6920 | try func.emitWValue(sign_v); | 6775 | try cg.emitWValue(sign_v); |
| 6921 | const lsb = try func.load(sign_v, Type.u64, 0); | 6776 | const lsb = try cg.load(sign_v, Type.u64, 0); |
| 6922 | _ = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); | 6777 | _ = try cg.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| 6923 | try func.store(.stack, .stack, Type.u64, sign_v.offset()); | 6778 | try cg.store(.stack, .stack, Type.u64, sign_v.offset()); |
| 6924 | try func.emitWValue(sign_v); | 6779 | try cg.emitWValue(sign_v); |
| 6925 | const msb = try func.load(sign_v, Type.u64, 8); | 6780 | const msb = try cg.load(sign_v, Type.u64, 8); |
| 6926 | _ = try func.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor); | 6781 | _ = try cg.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor); |
| 6927 | try func.store(.stack, .stack, Type.u64, sign_v.offset() + 8); | 6782 | try cg.store(.stack, .stack, Type.u64, sign_v.offset() + 8); |
| 6928 | 6783 | ||
| 6929 | try func.lowerToStack(sign_v); | 6784 | try cg.lowerToStack(sign_v); |
| 6930 | _ = try func.load(overflow_ret, Type.i32, 0); | 6785 | _ = try cg.load(overflow_ret, Type.i32, 0); |
| 6931 | try func.addTag(.i32_eqz); | 6786 | try cg.addTag(.i32_eqz); |
| 6932 | try func.addTag(.select); | 6787 | try cg.addTag(.select); |
| 6933 | }, | 6788 | }, |
| 6934 | else => unreachable, | 6789 | else => unreachable, |
| 6935 | } | 6790 | } |
| 6936 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 6791 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 6937 | } | 6792 | } |
| 6938 | 6793 | ||
| 6939 | fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 6794 | fn airSatBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 6940 | assert(op == .add or op == .sub); | 6795 | assert(op == .add or op == .sub); |
| 6941 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6796 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6942 | 6797 | ||
| 6943 | const pt = func.pt; | 6798 | const zcu = cg.pt.zcu; |
| 6944 | const zcu = pt.zcu; | 6799 | const ty = cg.typeOfIndex(inst); |
| 6945 | const ty = func.typeOfIndex(inst); | 6800 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 6946 | const lhs = try func.resolveInst(bin_op.lhs); | 6801 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 6947 | const rhs = try func.resolveInst(bin_op.rhs); | ||
| 6948 | 6802 | ||
| 6949 | const int_info = ty.intInfo(zcu); | 6803 | const int_info = ty.intInfo(zcu); |
| 6950 | const is_signed = int_info.signedness == .signed; | 6804 | const is_signed = int_info.signedness == .signed; |
| 6951 | 6805 | ||
| 6952 | if (int_info.bits > 64) { | 6806 | if (int_info.bits > 64) { |
| 6953 | return func.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits}); | 6807 | return cg.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits}); |
| 6954 | } | 6808 | } |
| 6955 | 6809 | ||
| 6956 | if (is_signed) { | 6810 | if (is_signed) { |
| 6957 | const result = try signedSat(func, lhs, rhs, ty, op); | 6811 | const result = try signedSat(cg, lhs, rhs, ty, op); |
| 6958 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 6812 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6959 | } | 6813 | } |
| 6960 | 6814 | ||
| 6961 | const wasm_bits = toWasmBits(int_info.bits).?; | 6815 | const wasm_bits = toWasmBits(int_info.bits).?; |
| 6962 | var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty); | 6816 | var bin_result = try (try cg.binOp(lhs, rhs, ty, op)).toLocal(cg, ty); |
| 6963 | defer bin_result.free(func); | 6817 | defer bin_result.free(cg); |
| 6964 | if (wasm_bits != int_info.bits and op == .add) { | 6818 | if (wasm_bits != int_info.bits and op == .add) { |
| 6965 | const val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits))) - 1)); | 6819 | const val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits))) - 1)); |
| 6966 | const imm_val: WValue = switch (wasm_bits) { | 6820 | const imm_val: WValue = switch (wasm_bits) { |
| ... | @@ -6969,25 +6823,25 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | ... | @@ -6969,25 +6823,25 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 6969 | else => unreachable, | 6823 | else => unreachable, |
| 6970 | }; | 6824 | }; |
| 6971 | 6825 | ||
| 6972 | try func.emitWValue(bin_result); | 6826 | try cg.emitWValue(bin_result); |
| 6973 | try func.emitWValue(imm_val); | 6827 | try cg.emitWValue(imm_val); |
| 6974 | _ = try func.cmp(bin_result, imm_val, ty, .lt); | 6828 | _ = try cg.cmp(bin_result, imm_val, ty, .lt); |
| 6975 | } else { | 6829 | } else { |
| 6976 | switch (wasm_bits) { | 6830 | switch (wasm_bits) { |
| 6977 | 32 => try func.addImm32(if (op == .add) std.math.maxInt(u32) else 0), | 6831 | 32 => try cg.addImm32(if (op == .add) std.math.maxInt(u32) else 0), |
| 6978 | 64 => try func.addImm64(if (op == .add) std.math.maxInt(u64) else 0), | 6832 | 64 => try cg.addImm64(if (op == .add) std.math.maxInt(u64) else 0), |
| 6979 | else => unreachable, | 6833 | else => unreachable, |
| 6980 | } | 6834 | } |
| 6981 | try func.emitWValue(bin_result); | 6835 | try cg.emitWValue(bin_result); |
| 6982 | _ = try func.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); | 6836 | _ = try cg.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); |
| 6983 | } | 6837 | } |
| 6984 | 6838 | ||
| 6985 | try func.addTag(.select); | 6839 | try cg.addTag(.select); |
| 6986 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 6840 | return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 6987 | } | 6841 | } |
| 6988 | 6842 | ||
| 6989 | fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 6843 | fn signedSat(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 6990 | const pt = func.pt; | 6844 | const pt = cg.pt; |
| 6991 | const zcu = pt.zcu; | 6845 | const zcu = pt.zcu; |
| 6992 | const int_info = ty.intInfo(zcu); | 6846 | const int_info = ty.intInfo(zcu); |
| 6993 | const wasm_bits = toWasmBits(int_info.bits).?; | 6847 | const wasm_bits = toWasmBits(int_info.bits).?; |
| ... | @@ -7007,92 +6861,92 @@ fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr | ... | @@ -7007,92 +6861,92 @@ fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr |
| 7007 | else => unreachable, | 6861 | else => unreachable, |
| 7008 | }; | 6862 | }; |
| 7009 | 6863 | ||
| 7010 | var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty); | 6864 | var bin_result = try (try cg.binOp(lhs, rhs, ext_ty, op)).toLocal(cg, ext_ty); |
| 7011 | if (!is_wasm_bits) { | 6865 | if (!is_wasm_bits) { |
| 7012 | defer bin_result.free(func); // not returned in this branch | 6866 | defer bin_result.free(cg); // not returned in this branch |
| 7013 | try func.emitWValue(bin_result); | 6867 | try cg.emitWValue(bin_result); |
| 7014 | try func.emitWValue(max_wvalue); | 6868 | try cg.emitWValue(max_wvalue); |
| 7015 | _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt); | 6869 | _ = try cg.cmp(bin_result, max_wvalue, ext_ty, .lt); |
| 7016 | try func.addTag(.select); | 6870 | try cg.addTag(.select); |
| 7017 | try func.addLabel(.local_set, bin_result.local.value); // re-use local | 6871 | try cg.addLocal(.local_set, bin_result.local.value); // re-use local |
| 7018 | 6872 | ||
| 7019 | try func.emitWValue(bin_result); | 6873 | try cg.emitWValue(bin_result); |
| 7020 | try func.emitWValue(min_wvalue); | 6874 | try cg.emitWValue(min_wvalue); |
| 7021 | _ = try func.cmp(bin_result, min_wvalue, ext_ty, .gt); | 6875 | _ = try cg.cmp(bin_result, min_wvalue, ext_ty, .gt); |
| 7022 | try func.addTag(.select); | 6876 | try cg.addTag(.select); |
| 7023 | try func.addLabel(.local_set, bin_result.local.value); // re-use local | 6877 | try cg.addLocal(.local_set, bin_result.local.value); // re-use local |
| 7024 | return (try func.wrapOperand(bin_result, ty)).toLocal(func, ty); | 6878 | return (try cg.wrapOperand(bin_result, ty)).toLocal(cg, ty); |
| 7025 | } else { | 6879 | } else { |
| 7026 | const zero: WValue = switch (wasm_bits) { | 6880 | const zero: WValue = switch (wasm_bits) { |
| 7027 | 32 => .{ .imm32 = 0 }, | 6881 | 32 => .{ .imm32 = 0 }, |
| 7028 | 64 => .{ .imm64 = 0 }, | 6882 | 64 => .{ .imm64 = 0 }, |
| 7029 | else => unreachable, | 6883 | else => unreachable, |
| 7030 | }; | 6884 | }; |
| 7031 | try func.emitWValue(max_wvalue); | 6885 | try cg.emitWValue(max_wvalue); |
| 7032 | try func.emitWValue(min_wvalue); | 6886 | try cg.emitWValue(min_wvalue); |
| 7033 | _ = try func.cmp(bin_result, zero, ty, .lt); | 6887 | _ = try cg.cmp(bin_result, zero, ty, .lt); |
| 7034 | try func.addTag(.select); | 6888 | try cg.addTag(.select); |
| 7035 | try func.emitWValue(bin_result); | 6889 | try cg.emitWValue(bin_result); |
| 7036 | // leave on stack | 6890 | // leave on stack |
| 7037 | const cmp_zero_result = try func.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); | 6891 | const cmp_zero_result = try cg.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); |
| 7038 | const cmp_bin_result = try func.cmp(bin_result, lhs, ty, .lt); | 6892 | const cmp_bin_result = try cg.cmp(bin_result, lhs, ty, .lt); |
| 7039 | _ = try func.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. | 6893 | _ = try cg.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. |
| 7040 | try func.addTag(.select); | 6894 | try cg.addTag(.select); |
| 7041 | try func.addLabel(.local_set, bin_result.local.value); // re-use local | 6895 | try cg.addLocal(.local_set, bin_result.local.value); // re-use local |
| 7042 | return bin_result; | 6896 | return bin_result; |
| 7043 | } | 6897 | } |
| 7044 | } | 6898 | } |
| 7045 | 6899 | ||
| 7046 | fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6900 | fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7047 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6901 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 7048 | 6902 | ||
| 7049 | const pt = func.pt; | 6903 | const pt = cg.pt; |
| 7050 | const zcu = pt.zcu; | 6904 | const zcu = pt.zcu; |
| 7051 | const ty = func.typeOfIndex(inst); | 6905 | const ty = cg.typeOfIndex(inst); |
| 7052 | const int_info = ty.intInfo(zcu); | 6906 | const int_info = ty.intInfo(zcu); |
| 7053 | const is_signed = int_info.signedness == .signed; | 6907 | const is_signed = int_info.signedness == .signed; |
| 7054 | if (int_info.bits > 64) { | 6908 | if (int_info.bits > 64) { |
| 7055 | return func.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits}); | 6909 | return cg.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits}); |
| 7056 | } | 6910 | } |
| 7057 | 6911 | ||
| 7058 | const lhs = try func.resolveInst(bin_op.lhs); | 6912 | const lhs = try cg.resolveInst(bin_op.lhs); |
| 7059 | const rhs = try func.resolveInst(bin_op.rhs); | 6913 | const rhs = try cg.resolveInst(bin_op.rhs); |
| 7060 | const wasm_bits = toWasmBits(int_info.bits).?; | 6914 | const wasm_bits = toWasmBits(int_info.bits).?; |
| 7061 | const result = try func.allocLocal(ty); | 6915 | const result = try cg.allocLocal(ty); |
| 7062 | 6916 | ||
| 7063 | if (wasm_bits == int_info.bits) { | 6917 | if (wasm_bits == int_info.bits) { |
| 7064 | var shl = try (try func.binOp(lhs, rhs, ty, .shl)).toLocal(func, ty); | 6918 | var shl = try (try cg.binOp(lhs, rhs, ty, .shl)).toLocal(cg, ty); |
| 7065 | defer shl.free(func); | 6919 | defer shl.free(cg); |
| 7066 | var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty); | 6920 | var shr = try (try cg.binOp(shl, rhs, ty, .shr)).toLocal(cg, ty); |
| 7067 | defer shr.free(func); | 6921 | defer shr.free(cg); |
| 7068 | 6922 | ||
| 7069 | switch (wasm_bits) { | 6923 | switch (wasm_bits) { |
| 7070 | 32 => blk: { | 6924 | 32 => blk: { |
| 7071 | if (!is_signed) { | 6925 | if (!is_signed) { |
| 7072 | try func.addImm32(std.math.maxInt(u32)); | 6926 | try cg.addImm32(std.math.maxInt(u32)); |
| 7073 | break :blk; | 6927 | break :blk; |
| 7074 | } | 6928 | } |
| 7075 | try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); | 6929 | try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); |
| 7076 | try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); | 6930 | try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); |
| 7077 | _ = try func.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); | 6931 | _ = try cg.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); |
| 7078 | try func.addTag(.select); | 6932 | try cg.addTag(.select); |
| 7079 | }, | 6933 | }, |
| 7080 | 64 => blk: { | 6934 | 64 => blk: { |
| 7081 | if (!is_signed) { | 6935 | if (!is_signed) { |
| 7082 | try func.addImm64(std.math.maxInt(u64)); | 6936 | try cg.addImm64(std.math.maxInt(u64)); |
| 7083 | break :blk; | 6937 | break :blk; |
| 7084 | } | 6938 | } |
| 7085 | try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); | 6939 | try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); |
| 7086 | try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); | 6940 | try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); |
| 7087 | _ = try func.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); | 6941 | _ = try cg.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); |
| 7088 | try func.addTag(.select); | 6942 | try cg.addTag(.select); |
| 7089 | }, | 6943 | }, |
| 7090 | else => unreachable, | 6944 | else => unreachable, |
| 7091 | } | 6945 | } |
| 7092 | try func.emitWValue(shl); | 6946 | try cg.emitWValue(shl); |
| 7093 | _ = try func.cmp(lhs, shr, ty, .neq); | 6947 | _ = try cg.cmp(lhs, shr, ty, .neq); |
| 7094 | try func.addTag(.select); | 6948 | try cg.addTag(.select); |
| 7095 | try func.addLabel(.local_set, result.local.value); | 6949 | try cg.addLocal(.local_set, result.local.value); |
| 7096 | } else { | 6950 | } else { |
| 7097 | const shift_size = wasm_bits - int_info.bits; | 6951 | const shift_size = wasm_bits - int_info.bits; |
| 7098 | const shift_value: WValue = switch (wasm_bits) { | 6952 | const shift_value: WValue = switch (wasm_bits) { |
| ... | @@ -7102,50 +6956,50 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7102,50 +6956,50 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7102 | }; | 6956 | }; |
| 7103 | const ext_ty = try pt.intType(int_info.signedness, wasm_bits); | 6957 | const ext_ty = try pt.intType(int_info.signedness, wasm_bits); |
| 7104 | 6958 | ||
| 7105 | var shl_res = try (try func.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(func, ext_ty); | 6959 | var shl_res = try (try cg.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(cg, ext_ty); |
| 7106 | defer shl_res.free(func); | 6960 | defer shl_res.free(cg); |
| 7107 | var shl = try (try func.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(func, ext_ty); | 6961 | var shl = try (try cg.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(cg, ext_ty); |
| 7108 | defer shl.free(func); | 6962 | defer shl.free(cg); |
| 7109 | var shr = try (try func.binOp(shl, rhs, ext_ty, .shr)).toLocal(func, ext_ty); | 6963 | var shr = try (try cg.binOp(shl, rhs, ext_ty, .shr)).toLocal(cg, ext_ty); |
| 7110 | defer shr.free(func); | 6964 | defer shr.free(cg); |
| 7111 | 6965 | ||
| 7112 | switch (wasm_bits) { | 6966 | switch (wasm_bits) { |
| 7113 | 32 => blk: { | 6967 | 32 => blk: { |
| 7114 | if (!is_signed) { | 6968 | if (!is_signed) { |
| 7115 | try func.addImm32(std.math.maxInt(u32)); | 6969 | try cg.addImm32(std.math.maxInt(u32)); |
| 7116 | break :blk; | 6970 | break :blk; |
| 7117 | } | 6971 | } |
| 7118 | 6972 | ||
| 7119 | try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); | 6973 | try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); |
| 7120 | try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); | 6974 | try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); |
| 7121 | _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt); | 6975 | _ = try cg.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt); |
| 7122 | try func.addTag(.select); | 6976 | try cg.addTag(.select); |
| 7123 | }, | 6977 | }, |
| 7124 | 64 => blk: { | 6978 | 64 => blk: { |
| 7125 | if (!is_signed) { | 6979 | if (!is_signed) { |
| 7126 | try func.addImm64(std.math.maxInt(u64)); | 6980 | try cg.addImm64(std.math.maxInt(u64)); |
| 7127 | break :blk; | 6981 | break :blk; |
| 7128 | } | 6982 | } |
| 7129 | 6983 | ||
| 7130 | try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); | 6984 | try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); |
| 7131 | try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); | 6985 | try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); |
| 7132 | _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt); | 6986 | _ = try cg.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt); |
| 7133 | try func.addTag(.select); | 6987 | try cg.addTag(.select); |
| 7134 | }, | 6988 | }, |
| 7135 | else => unreachable, | 6989 | else => unreachable, |
| 7136 | } | 6990 | } |
| 7137 | try func.emitWValue(shl); | 6991 | try cg.emitWValue(shl); |
| 7138 | _ = try func.cmp(shl_res, shr, ext_ty, .neq); | 6992 | _ = try cg.cmp(shl_res, shr, ext_ty, .neq); |
| 7139 | try func.addTag(.select); | 6993 | try cg.addTag(.select); |
| 7140 | try func.addLabel(.local_set, result.local.value); | 6994 | try cg.addLocal(.local_set, result.local.value); |
| 7141 | var shift_result = try func.binOp(result, shift_value, ext_ty, .shr); | 6995 | var shift_result = try cg.binOp(result, shift_value, ext_ty, .shr); |
| 7142 | if (is_signed) { | 6996 | if (is_signed) { |
| 7143 | shift_result = try func.wrapOperand(shift_result, ty); | 6997 | shift_result = try cg.wrapOperand(shift_result, ty); |
| 7144 | } | 6998 | } |
| 7145 | try func.addLabel(.local_set, result.local.value); | 6999 | try cg.addLocal(.local_set, result.local.value); |
| 7146 | } | 7000 | } |
| 7147 | 7001 | ||
| 7148 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | 7002 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 7149 | } | 7003 | } |
| 7150 | 7004 | ||
| 7151 | /// Calls a compiler-rt intrinsic by creating an undefined symbol, | 7005 | /// Calls a compiler-rt intrinsic by creating an undefined symbol, |
| ... | @@ -7155,31 +7009,23 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7155,31 +7009,23 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7155 | /// passed as the first parameter. | 7009 | /// passed as the first parameter. |
| 7156 | /// May leave the return value on the stack. | 7010 | /// May leave the return value on the stack. |
| 7157 | fn callIntrinsic( | 7011 | fn callIntrinsic( |
| 7158 | func: *CodeGen, | 7012 | cg: *CodeGen, |
| 7159 | name: []const u8, | 7013 | intrinsic: Mir.Intrinsic, |
| 7160 | param_types: []const InternPool.Index, | 7014 | param_types: []const InternPool.Index, |
| 7161 | return_type: Type, | 7015 | return_type: Type, |
| 7162 | args: []const WValue, | 7016 | args: []const WValue, |
| 7163 | ) InnerError!WValue { | 7017 | ) InnerError!WValue { |
| 7164 | assert(param_types.len == args.len); | 7018 | assert(param_types.len == args.len); |
| 7165 | const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| { | 7019 | const zcu = cg.pt.zcu; |
| 7166 | return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); | ||
| 7167 | }; | ||
| 7168 | 7020 | ||
| 7169 | // Always pass over C-ABI | 7021 | // Always pass over C-ABI |
| 7170 | const pt = func.pt; | ||
| 7171 | const zcu = pt.zcu; | ||
| 7172 | var func_type = try genFunctype(func.gpa, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*); | ||
| 7173 | defer func_type.deinit(func.gpa); | ||
| 7174 | const func_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, func_type); | ||
| 7175 | try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); | ||
| 7176 | 7022 | ||
| 7177 | const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*); | 7023 | const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, zcu, cg.target); |
| 7178 | // if we want return as first param, we allocate a pointer to stack, | 7024 | // if we want return as first param, we allocate a pointer to stack, |
| 7179 | // and emit it as our first argument | 7025 | // and emit it as our first argument |
| 7180 | const sret = if (want_sret_param) blk: { | 7026 | const sret = if (want_sret_param) blk: { |
| 7181 | const sret_local = try func.allocStack(return_type); | 7027 | const sret_local = try cg.allocStack(return_type); |
| 7182 | try func.lowerToStack(sret_local); | 7028 | try cg.lowerToStack(sret_local); |
| 7183 | break :blk sret_local; | 7029 | break :blk sret_local; |
| 7184 | } else .none; | 7030 | } else .none; |
| 7185 | 7031 | ||
| ... | @@ -7187,16 +7033,15 @@ fn callIntrinsic( | ... | @@ -7187,16 +7033,15 @@ fn callIntrinsic( |
| 7187 | for (args, 0..) |arg, arg_i| { | 7033 | for (args, 0..) |arg, arg_i| { |
| 7188 | assert(!(want_sret_param and arg == .stack)); | 7034 | assert(!(want_sret_param and arg == .stack)); |
| 7189 | assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBitsIgnoreComptime(zcu)); | 7035 | assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBitsIgnoreComptime(zcu)); |
| 7190 | try func.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg); | 7036 | try cg.lowerArg(.{ .wasm_watc = .{} }, Type.fromInterned(param_types[arg_i]), arg); |
| 7191 | } | 7037 | } |
| 7192 | 7038 | ||
| 7193 | // Actually call our intrinsic | 7039 | try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } }); |
| 7194 | try func.addLabel(.call, @intFromEnum(symbol_index)); | ||
| 7195 | 7040 | ||
| 7196 | if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { | 7041 | if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 7197 | return .none; | 7042 | return .none; |
| 7198 | } else if (return_type.isNoReturn(zcu)) { | 7043 | } else if (return_type.isNoReturn(zcu)) { |
| 7199 | try func.addTag(.@"unreachable"); | 7044 | try cg.addTag(.@"unreachable"); |
| 7200 | return .none; | 7045 | return .none; |
| 7201 | } else if (want_sret_param) { | 7046 | } else if (want_sret_param) { |
| 7202 | return sret; | 7047 | return sret; |
| ... | @@ -7205,194 +7050,30 @@ fn callIntrinsic( | ... | @@ -7205,194 +7050,30 @@ fn callIntrinsic( |
| 7205 | } | 7050 | } |
| 7206 | } | 7051 | } |
| 7207 | 7052 | ||
| 7208 | fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7053 | fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7209 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 7054 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 7210 | const operand = try func.resolveInst(un_op); | 7055 | const operand = try cg.resolveInst(un_op); |
| 7211 | const enum_ty = func.typeOf(un_op); | 7056 | const enum_ty = cg.typeOf(un_op); |
| 7212 | |||
| 7213 | const func_sym_index = try func.getTagNameFunction(enum_ty); | ||
| 7214 | |||
| 7215 | const result_ptr = try func.allocStack(func.typeOfIndex(inst)); | ||
| 7216 | try func.lowerToStack(result_ptr); | ||
| 7217 | try func.emitWValue(operand); | ||
| 7218 | try func.addLabel(.call, func_sym_index); | ||
| 7219 | |||
| 7220 | return func.finishAir(inst, result_ptr, &.{un_op}); | ||
| 7221 | } | ||
| 7222 | |||
| 7223 | fn 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 | 7057 | ||
| 7372 | try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name | 7058 | const result_ptr = try cg.allocStack(cg.typeOfIndex(inst)); |
| 7373 | // finish outer block | 7059 | try cg.lowerToStack(result_ptr); |
| 7374 | try writer.writeByte(std.wasm.opcode(.end)); | 7060 | try cg.emitWValue(operand); |
| 7375 | // finish function body | 7061 | try cg.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = enum_ty.toIntern() } }); |
| 7376 | try writer.writeByte(std.wasm.opcode(.end)); | ||
| 7377 | 7062 | ||
| 7378 | const slice_ty = Type.slice_const_u8_sentinel_0; | 7063 | return cg.finishAir(inst, result_ptr, &.{un_op}); |
| 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 | } | 7064 | } |
| 7383 | 7065 | ||
| 7384 | fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7066 | fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7385 | const pt = func.pt; | 7067 | const zcu = cg.pt.zcu; |
| 7386 | const zcu = pt.zcu; | ||
| 7387 | const ip = &zcu.intern_pool; | 7068 | const ip = &zcu.intern_pool; |
| 7388 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 7069 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 7389 | 7070 | ||
| 7390 | const operand = try func.resolveInst(ty_op.operand); | 7071 | const operand = try cg.resolveInst(ty_op.operand); |
| 7391 | const error_set_ty = ty_op.ty.toType(); | 7072 | const error_set_ty = ty_op.ty.toType(); |
| 7392 | const result = try func.allocLocal(Type.bool); | 7073 | const result = try cg.allocLocal(Type.bool); |
| 7393 | 7074 | ||
| 7394 | const names = error_set_ty.errorSetNames(zcu); | 7075 | const names = error_set_ty.errorSetNames(zcu); |
| 7395 | var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len); | 7076 | var values = try std.ArrayList(u32).initCapacity(cg.gpa, names.len); |
| 7396 | defer values.deinit(); | 7077 | defer values.deinit(); |
| 7397 | 7078 | ||
| 7398 | var lowest: ?u32 = null; | 7079 | var lowest: ?u32 = null; |
| ... | @@ -7418,23 +7099,23 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7418,23 +7099,23 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7418 | } | 7099 | } |
| 7419 | 7100 | ||
| 7420 | // start block for 'true' branch | 7101 | // start block for 'true' branch |
| 7421 | try func.startBlock(.block, wasm.block_empty); | 7102 | try cg.startBlock(.block, .empty); |
| 7422 | // start block for 'false' branch | 7103 | // start block for 'false' branch |
| 7423 | try func.startBlock(.block, wasm.block_empty); | 7104 | try cg.startBlock(.block, .empty); |
| 7424 | // block for the jump table itself | 7105 | // block for the jump table itself |
| 7425 | try func.startBlock(.block, wasm.block_empty); | 7106 | try cg.startBlock(.block, .empty); |
| 7426 | 7107 | ||
| 7427 | // lower operand to determine jump table target | 7108 | // lower operand to determine jump table target |
| 7428 | try func.emitWValue(operand); | 7109 | try cg.emitWValue(operand); |
| 7429 | try func.addImm32(lowest.?); | 7110 | try cg.addImm32(lowest.?); |
| 7430 | try func.addTag(.i32_sub); | 7111 | try cg.addTag(.i32_sub); |
| 7431 | 7112 | ||
| 7432 | // Account for default branch so always add '1' | 7113 | // Account for default branch so always add '1' |
| 7433 | const depth = @as(u32, @intCast(highest.? - lowest.? + 1)); | 7114 | const depth = @as(u32, @intCast(highest.? - lowest.? + 1)); |
| 7434 | const jump_table: Mir.JumpTable = .{ .length = depth }; | 7115 | const jump_table: Mir.JumpTable = .{ .length = depth }; |
| 7435 | const table_extra_index = try func.addExtra(jump_table); | 7116 | const table_extra_index = try cg.addExtra(jump_table); |
| 7436 | try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | 7117 | try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); |
| 7437 | try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); | 7118 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth); |
| 7438 | 7119 | ||
| 7439 | var value: u32 = lowest.?; | 7120 | var value: u32 = lowest.?; |
| 7440 | while (value <= highest.?) : (value += 1) { | 7121 | while (value <= highest.?) : (value += 1) { |
| ... | @@ -7444,202 +7125,200 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7444,202 +7125,200 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7444 | } | 7125 | } |
| 7445 | break :blk 0; | 7126 | break :blk 0; |
| 7446 | }; | 7127 | }; |
| 7447 | func.mir_extra.appendAssumeCapacity(idx); | 7128 | cg.mir_extra.appendAssumeCapacity(idx); |
| 7448 | } | 7129 | } |
| 7449 | try func.endBlock(); | 7130 | try cg.endBlock(); |
| 7450 | 7131 | ||
| 7451 | // 'false' branch (i.e. error set does not have value | 7132 | // 'false' branch (i.e. error set does not have value |
| 7452 | // ensure we set local to 0 in case the local was re-used. | 7133 | // ensure we set local to 0 in case the local was re-used. |
| 7453 | try func.addImm32(0); | 7134 | try cg.addImm32(0); |
| 7454 | try func.addLabel(.local_set, result.local.value); | 7135 | try cg.addLocal(.local_set, result.local.value); |
| 7455 | try func.addLabel(.br, 1); | 7136 | try cg.addLabel(.br, 1); |
| 7456 | try func.endBlock(); | 7137 | try cg.endBlock(); |
| 7457 | 7138 | ||
| 7458 | // 'true' branch | 7139 | // 'true' branch |
| 7459 | try func.addImm32(1); | 7140 | try cg.addImm32(1); |
| 7460 | try func.addLabel(.local_set, result.local.value); | 7141 | try cg.addLocal(.local_set, result.local.value); |
| 7461 | try func.addLabel(.br, 0); | 7142 | try cg.addLabel(.br, 0); |
| 7462 | try func.endBlock(); | 7143 | try cg.endBlock(); |
| 7463 | 7144 | ||
| 7464 | return func.finishAir(inst, result, &.{ty_op.operand}); | 7145 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 7465 | } | 7146 | } |
| 7466 | 7147 | ||
| 7467 | inline fn useAtomicFeature(func: *const CodeGen) bool { | 7148 | inline fn useAtomicFeature(cg: *const CodeGen) bool { |
| 7468 | return std.Target.wasm.featureSetHas(func.target.cpu.features, .atomics); | 7149 | return std.Target.wasm.featureSetHas(cg.target.cpu.features, .atomics); |
| 7469 | } | 7150 | } |
| 7470 | 7151 | ||
| 7471 | fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7152 | fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7472 | const pt = func.pt; | 7153 | const zcu = cg.pt.zcu; |
| 7473 | const zcu = pt.zcu; | 7154 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 7474 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 7155 | const extra = cg.air.extraData(Air.Cmpxchg, ty_pl.payload).data; |
| 7475 | const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data; | ||
| 7476 | 7156 | ||
| 7477 | const ptr_ty = func.typeOf(extra.ptr); | 7157 | const ptr_ty = cg.typeOf(extra.ptr); |
| 7478 | const ty = ptr_ty.childType(zcu); | 7158 | const ty = ptr_ty.childType(zcu); |
| 7479 | const result_ty = func.typeOfIndex(inst); | 7159 | const result_ty = cg.typeOfIndex(inst); |
| 7480 | 7160 | ||
| 7481 | const ptr_operand = try func.resolveInst(extra.ptr); | 7161 | const ptr_operand = try cg.resolveInst(extra.ptr); |
| 7482 | const expected_val = try func.resolveInst(extra.expected_value); | 7162 | const expected_val = try cg.resolveInst(extra.expected_value); |
| 7483 | const new_val = try func.resolveInst(extra.new_value); | 7163 | const new_val = try cg.resolveInst(extra.new_value); |
| 7484 | 7164 | ||
| 7485 | const cmp_result = try func.allocLocal(Type.bool); | 7165 | const cmp_result = try cg.allocLocal(Type.bool); |
| 7486 | 7166 | ||
| 7487 | const ptr_val = if (func.useAtomicFeature()) val: { | 7167 | const ptr_val = if (cg.useAtomicFeature()) val: { |
| 7488 | const val_local = try func.allocLocal(ty); | 7168 | const val_local = try cg.allocLocal(ty); |
| 7489 | try func.emitWValue(ptr_operand); | 7169 | try cg.emitWValue(ptr_operand); |
| 7490 | try func.lowerToStack(expected_val); | 7170 | try cg.lowerToStack(expected_val); |
| 7491 | try func.lowerToStack(new_val); | 7171 | try cg.lowerToStack(new_val); |
| 7492 | try func.addAtomicMemArg(switch (ty.abiSize(zcu)) { | 7172 | try cg.addAtomicMemArg(switch (ty.abiSize(zcu)) { |
| 7493 | 1 => .i32_atomic_rmw8_cmpxchg_u, | 7173 | 1 => .i32_atomic_rmw8_cmpxchg_u, |
| 7494 | 2 => .i32_atomic_rmw16_cmpxchg_u, | 7174 | 2 => .i32_atomic_rmw16_cmpxchg_u, |
| 7495 | 4 => .i32_atomic_rmw_cmpxchg, | 7175 | 4 => .i32_atomic_rmw_cmpxchg, |
| 7496 | 8 => .i32_atomic_rmw_cmpxchg, | 7176 | 8 => .i32_atomic_rmw_cmpxchg, |
| 7497 | else => |size| return func.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}), | 7177 | else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}), |
| 7498 | }, .{ | 7178 | }, .{ |
| 7499 | .offset = ptr_operand.offset(), | 7179 | .offset = ptr_operand.offset(), |
| 7500 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), | 7180 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7501 | }); | 7181 | }); |
| 7502 | try func.addLabel(.local_tee, val_local.local.value); | 7182 | try cg.addLocal(.local_tee, val_local.local.value); |
| 7503 | _ = try func.cmp(.stack, expected_val, ty, .eq); | 7183 | _ = try cg.cmp(.stack, expected_val, ty, .eq); |
| 7504 | try func.addLabel(.local_set, cmp_result.local.value); | 7184 | try cg.addLocal(.local_set, cmp_result.local.value); |
| 7505 | break :val val_local; | 7185 | break :val val_local; |
| 7506 | } else val: { | 7186 | } else val: { |
| 7507 | if (ty.abiSize(zcu) > 8) { | 7187 | if (ty.abiSize(zcu) > 8) { |
| 7508 | return func.fail("TODO: Implement `@cmpxchg` for types larger than abi size of 8 bytes", .{}); | 7188 | return cg.fail("TODO: Implement `@cmpxchg` for types larger than abi size of 8 bytes", .{}); |
| 7509 | } | 7189 | } |
| 7510 | const ptr_val = try WValue.toLocal(try func.load(ptr_operand, ty, 0), func, ty); | 7190 | const ptr_val = try WValue.toLocal(try cg.load(ptr_operand, ty, 0), cg, ty); |
| 7511 | 7191 | ||
| 7512 | try func.lowerToStack(ptr_operand); | 7192 | try cg.lowerToStack(ptr_operand); |
| 7513 | try func.lowerToStack(new_val); | 7193 | try cg.lowerToStack(new_val); |
| 7514 | try func.emitWValue(ptr_val); | 7194 | try cg.emitWValue(ptr_val); |
| 7515 | _ = try func.cmp(ptr_val, expected_val, ty, .eq); | 7195 | _ = try cg.cmp(ptr_val, expected_val, ty, .eq); |
| 7516 | try func.addLabel(.local_tee, cmp_result.local.value); | 7196 | try cg.addLocal(.local_tee, cmp_result.local.value); |
| 7517 | try func.addTag(.select); | 7197 | try cg.addTag(.select); |
| 7518 | try func.store(.stack, .stack, ty, 0); | 7198 | try cg.store(.stack, .stack, ty, 0); |
| 7519 | 7199 | ||
| 7520 | break :val ptr_val; | 7200 | break :val ptr_val; |
| 7521 | }; | 7201 | }; |
| 7522 | 7202 | ||
| 7523 | const result = if (isByRef(result_ty, pt, func.target.*)) val: { | 7203 | const result = if (isByRef(result_ty, zcu, cg.target)) val: { |
| 7524 | try func.emitWValue(cmp_result); | 7204 | try cg.emitWValue(cmp_result); |
| 7525 | try func.addImm32(~@as(u32, 0)); | 7205 | try cg.addImm32(~@as(u32, 0)); |
| 7526 | try func.addTag(.i32_xor); | 7206 | try cg.addTag(.i32_xor); |
| 7527 | try func.addImm32(1); | 7207 | try cg.addImm32(1); |
| 7528 | try func.addTag(.i32_and); | 7208 | try cg.addTag(.i32_and); |
| 7529 | const and_result = try WValue.toLocal(.stack, func, Type.bool); | 7209 | const and_result = try WValue.toLocal(.stack, cg, Type.bool); |
| 7530 | const result_ptr = try func.allocStack(result_ty); | 7210 | const result_ptr = try cg.allocStack(result_ty); |
| 7531 | try func.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu)))); | 7211 | try cg.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu)))); |
| 7532 | try func.store(result_ptr, ptr_val, ty, 0); | 7212 | try cg.store(result_ptr, ptr_val, ty, 0); |
| 7533 | break :val result_ptr; | 7213 | break :val result_ptr; |
| 7534 | } else val: { | 7214 | } else val: { |
| 7535 | try func.addImm32(0); | 7215 | try cg.addImm32(0); |
| 7536 | try func.emitWValue(ptr_val); | 7216 | try cg.emitWValue(ptr_val); |
| 7537 | try func.emitWValue(cmp_result); | 7217 | try cg.emitWValue(cmp_result); |
| 7538 | try func.addTag(.select); | 7218 | try cg.addTag(.select); |
| 7539 | break :val .stack; | 7219 | break :val .stack; |
| 7540 | }; | 7220 | }; |
| 7541 | 7221 | ||
| 7542 | return func.finishAir(inst, result, &.{ extra.ptr, extra.expected_value, extra.new_value }); | 7222 | return cg.finishAir(inst, result, &.{ extra.ptr, extra.expected_value, extra.new_value }); |
| 7543 | } | 7223 | } |
| 7544 | 7224 | ||
| 7545 | fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7225 | fn airAtomicLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7546 | const pt = func.pt; | 7226 | const zcu = cg.pt.zcu; |
| 7547 | const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; | 7227 | const atomic_load = cg.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; |
| 7548 | const ptr = try func.resolveInst(atomic_load.ptr); | 7228 | const ptr = try cg.resolveInst(atomic_load.ptr); |
| 7549 | const ty = func.typeOfIndex(inst); | 7229 | const ty = cg.typeOfIndex(inst); |
| 7550 | 7230 | ||
| 7551 | if (func.useAtomicFeature()) { | 7231 | if (cg.useAtomicFeature()) { |
| 7552 | const tag: wasm.AtomicsOpcode = switch (ty.abiSize(pt.zcu)) { | 7232 | const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { |
| 7553 | 1 => .i32_atomic_load8_u, | 7233 | 1 => .i32_atomic_load8_u, |
| 7554 | 2 => .i32_atomic_load16_u, | 7234 | 2 => .i32_atomic_load16_u, |
| 7555 | 4 => .i32_atomic_load, | 7235 | 4 => .i32_atomic_load, |
| 7556 | 8 => .i64_atomic_load, | 7236 | 8 => .i64_atomic_load, |
| 7557 | else => |size| return func.fail("TODO: @atomicLoad for types with abi size {d}", .{size}), | 7237 | else => |size| return cg.fail("TODO: @atomicLoad for types with abi size {d}", .{size}), |
| 7558 | }; | 7238 | }; |
| 7559 | try func.emitWValue(ptr); | 7239 | try cg.emitWValue(ptr); |
| 7560 | try func.addAtomicMemArg(tag, .{ | 7240 | try cg.addAtomicMemArg(tag, .{ |
| 7561 | .offset = ptr.offset(), | 7241 | .offset = ptr.offset(), |
| 7562 | .alignment = @intCast(ty.abiAlignment(pt.zcu).toByteUnits().?), | 7242 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7563 | }); | 7243 | }); |
| 7564 | } else { | 7244 | } else { |
| 7565 | _ = try func.load(ptr, ty, 0); | 7245 | _ = try cg.load(ptr, ty, 0); |
| 7566 | } | 7246 | } |
| 7567 | 7247 | ||
| 7568 | return func.finishAir(inst, .stack, &.{atomic_load.ptr}); | 7248 | return cg.finishAir(inst, .stack, &.{atomic_load.ptr}); |
| 7569 | } | 7249 | } |
| 7570 | 7250 | ||
| 7571 | fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7251 | fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7572 | const pt = func.pt; | 7252 | const zcu = cg.pt.zcu; |
| 7573 | const zcu = pt.zcu; | 7253 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 7574 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 7254 | const extra = cg.air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| 7575 | const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data; | ||
| 7576 | 7255 | ||
| 7577 | const ptr = try func.resolveInst(pl_op.operand); | 7256 | const ptr = try cg.resolveInst(pl_op.operand); |
| 7578 | const operand = try func.resolveInst(extra.operand); | 7257 | const operand = try cg.resolveInst(extra.operand); |
| 7579 | const ty = func.typeOfIndex(inst); | 7258 | const ty = cg.typeOfIndex(inst); |
| 7580 | const op: std.builtin.AtomicRmwOp = extra.op(); | 7259 | const op: std.builtin.AtomicRmwOp = extra.op(); |
| 7581 | 7260 | ||
| 7582 | if (func.useAtomicFeature()) { | 7261 | if (cg.useAtomicFeature()) { |
| 7583 | switch (op) { | 7262 | switch (op) { |
| 7584 | .Max, | 7263 | .Max, |
| 7585 | .Min, | 7264 | .Min, |
| 7586 | .Nand, | 7265 | .Nand, |
| 7587 | => { | 7266 | => { |
| 7588 | const tmp = try func.load(ptr, ty, 0); | 7267 | const tmp = try cg.load(ptr, ty, 0); |
| 7589 | const value = try tmp.toLocal(func, ty); | 7268 | const value = try tmp.toLocal(cg, ty); |
| 7590 | 7269 | ||
| 7591 | // create a loop to cmpxchg the new value | 7270 | // create a loop to cmpxchg the new value |
| 7592 | try func.startBlock(.loop, wasm.block_empty); | 7271 | try cg.startBlock(.loop, .empty); |
| 7593 | 7272 | ||
| 7594 | try func.emitWValue(ptr); | 7273 | try cg.emitWValue(ptr); |
| 7595 | try func.emitWValue(value); | 7274 | try cg.emitWValue(value); |
| 7596 | if (op == .Nand) { | 7275 | if (op == .Nand) { |
| 7597 | const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; | 7276 | const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; |
| 7598 | 7277 | ||
| 7599 | const and_res = try func.binOp(value, operand, ty, .@"and"); | 7278 | const and_res = try cg.binOp(value, operand, ty, .@"and"); |
| 7600 | if (wasm_bits == 32) | 7279 | if (wasm_bits == 32) |
| 7601 | try func.addImm32(~@as(u32, 0)) | 7280 | try cg.addImm32(~@as(u32, 0)) |
| 7602 | else if (wasm_bits == 64) | 7281 | else if (wasm_bits == 64) |
| 7603 | try func.addImm64(~@as(u64, 0)) | 7282 | try cg.addImm64(~@as(u64, 0)) |
| 7604 | else | 7283 | else |
| 7605 | return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); | 7284 | return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); |
| 7606 | _ = try func.binOp(and_res, .stack, ty, .xor); | 7285 | _ = try cg.binOp(and_res, .stack, ty, .xor); |
| 7607 | } else { | 7286 | } else { |
| 7608 | try func.emitWValue(value); | 7287 | try cg.emitWValue(value); |
| 7609 | try func.emitWValue(operand); | 7288 | try cg.emitWValue(operand); |
| 7610 | _ = try func.cmp(value, operand, ty, if (op == .Max) .gt else .lt); | 7289 | _ = try cg.cmp(value, operand, ty, if (op == .Max) .gt else .lt); |
| 7611 | try func.addTag(.select); | 7290 | try cg.addTag(.select); |
| 7612 | } | 7291 | } |
| 7613 | try func.addAtomicMemArg( | 7292 | try cg.addAtomicMemArg( |
| 7614 | switch (ty.abiSize(zcu)) { | 7293 | switch (ty.abiSize(zcu)) { |
| 7615 | 1 => .i32_atomic_rmw8_cmpxchg_u, | 7294 | 1 => .i32_atomic_rmw8_cmpxchg_u, |
| 7616 | 2 => .i32_atomic_rmw16_cmpxchg_u, | 7295 | 2 => .i32_atomic_rmw16_cmpxchg_u, |
| 7617 | 4 => .i32_atomic_rmw_cmpxchg, | 7296 | 4 => .i32_atomic_rmw_cmpxchg, |
| 7618 | 8 => .i64_atomic_rmw_cmpxchg, | 7297 | 8 => .i64_atomic_rmw_cmpxchg, |
| 7619 | else => return func.fail("TODO: implement `@atomicRmw` with operation `{s}` for types larger than 64 bits", .{@tagName(op)}), | 7298 | else => return cg.fail("TODO: implement `@atomicRmw` with operation `{s}` for types larger than 64 bits", .{@tagName(op)}), |
| 7620 | }, | 7299 | }, |
| 7621 | .{ | 7300 | .{ |
| 7622 | .offset = ptr.offset(), | 7301 | .offset = ptr.offset(), |
| 7623 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), | 7302 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7624 | }, | 7303 | }, |
| 7625 | ); | 7304 | ); |
| 7626 | const select_res = try func.allocLocal(ty); | 7305 | const select_res = try cg.allocLocal(ty); |
| 7627 | try func.addLabel(.local_tee, select_res.local.value); | 7306 | try cg.addLocal(.local_tee, select_res.local.value); |
| 7628 | _ = try func.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if | 7307 | _ = try cg.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if |
| 7629 | 7308 | ||
| 7630 | try func.emitWValue(select_res); | 7309 | try cg.emitWValue(select_res); |
| 7631 | try func.addLabel(.local_set, value.local.value); | 7310 | try cg.addLocal(.local_set, value.local.value); |
| 7632 | 7311 | ||
| 7633 | try func.addLabel(.br_if, 0); | 7312 | try cg.addLabel(.br_if, 0); |
| 7634 | try func.endBlock(); | 7313 | try cg.endBlock(); |
| 7635 | return func.finishAir(inst, value, &.{ pl_op.operand, extra.operand }); | 7314 | return cg.finishAir(inst, value, &.{ pl_op.operand, extra.operand }); |
| 7636 | }, | 7315 | }, |
| 7637 | 7316 | ||
| 7638 | // the other operations have their own instructions for Wasm. | 7317 | // the other operations have their own instructions for Wasm. |
| 7639 | else => { | 7318 | else => { |
| 7640 | try func.emitWValue(ptr); | 7319 | try cg.emitWValue(ptr); |
| 7641 | try func.emitWValue(operand); | 7320 | try cg.emitWValue(operand); |
| 7642 | const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | 7321 | const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { |
| 7643 | 1 => switch (op) { | 7322 | 1 => switch (op) { |
| 7644 | .Xchg => .i32_atomic_rmw8_xchg_u, | 7323 | .Xchg => .i32_atomic_rmw8_xchg_u, |
| 7645 | .Add => .i32_atomic_rmw8_add_u, | 7324 | .Add => .i32_atomic_rmw8_add_u, |
| ... | @@ -7676,22 +7355,22 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7676,22 +7355,22 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7676 | .Xor => .i64_atomic_rmw_xor, | 7355 | .Xor => .i64_atomic_rmw_xor, |
| 7677 | else => unreachable, | 7356 | else => unreachable, |
| 7678 | }, | 7357 | }, |
| 7679 | else => |size| return func.fail("TODO: Implement `@atomicRmw` for types with abi size {d}", .{size}), | 7358 | else => |size| return cg.fail("TODO: Implement `@atomicRmw` for types with abi size {d}", .{size}), |
| 7680 | }; | 7359 | }; |
| 7681 | try func.addAtomicMemArg(tag, .{ | 7360 | try cg.addAtomicMemArg(tag, .{ |
| 7682 | .offset = ptr.offset(), | 7361 | .offset = ptr.offset(), |
| 7683 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), | 7362 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7684 | }); | 7363 | }); |
| 7685 | return func.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand }); | 7364 | return cg.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand }); |
| 7686 | }, | 7365 | }, |
| 7687 | } | 7366 | } |
| 7688 | } else { | 7367 | } else { |
| 7689 | const loaded = try func.load(ptr, ty, 0); | 7368 | const loaded = try cg.load(ptr, ty, 0); |
| 7690 | const result = try loaded.toLocal(func, ty); | 7369 | const result = try loaded.toLocal(cg, ty); |
| 7691 | 7370 | ||
| 7692 | switch (op) { | 7371 | switch (op) { |
| 7693 | .Xchg => { | 7372 | .Xchg => { |
| 7694 | try func.store(ptr, operand, ty, 0); | 7373 | try cg.store(ptr, operand, ty, 0); |
| 7695 | }, | 7374 | }, |
| 7696 | .Add, | 7375 | .Add, |
| 7697 | .Sub, | 7376 | .Sub, |
| ... | @@ -7699,8 +7378,8 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7699,8 +7378,8 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7699 | .Or, | 7378 | .Or, |
| 7700 | .Xor, | 7379 | .Xor, |
| 7701 | => { | 7380 | => { |
| 7702 | try func.emitWValue(ptr); | 7381 | try cg.emitWValue(ptr); |
| 7703 | _ = try func.binOp(result, operand, ty, switch (op) { | 7382 | _ = try cg.binOp(result, operand, ty, switch (op) { |
| 7704 | .Add => .add, | 7383 | .Add => .add, |
| 7705 | .Sub => .sub, | 7384 | .Sub => .sub, |
| 7706 | .And => .@"and", | 7385 | .And => .@"and", |
| ... | @@ -7709,87 +7388,123 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7709,87 +7388,123 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7709 | else => unreachable, | 7388 | else => unreachable, |
| 7710 | }); | 7389 | }); |
| 7711 | if (ty.isInt(zcu) and (op == .Add or op == .Sub)) { | 7390 | if (ty.isInt(zcu) and (op == .Add or op == .Sub)) { |
| 7712 | _ = try func.wrapOperand(.stack, ty); | 7391 | _ = try cg.wrapOperand(.stack, ty); |
| 7713 | } | 7392 | } |
| 7714 | try func.store(.stack, .stack, ty, ptr.offset()); | 7393 | try cg.store(.stack, .stack, ty, ptr.offset()); |
| 7715 | }, | 7394 | }, |
| 7716 | .Max, | 7395 | .Max, |
| 7717 | .Min, | 7396 | .Min, |
| 7718 | => { | 7397 | => { |
| 7719 | try func.emitWValue(ptr); | 7398 | try cg.emitWValue(ptr); |
| 7720 | try func.emitWValue(result); | 7399 | try cg.emitWValue(result); |
| 7721 | try func.emitWValue(operand); | 7400 | try cg.emitWValue(operand); |
| 7722 | _ = try func.cmp(result, operand, ty, if (op == .Max) .gt else .lt); | 7401 | _ = try cg.cmp(result, operand, ty, if (op == .Max) .gt else .lt); |
| 7723 | try func.addTag(.select); | 7402 | try cg.addTag(.select); |
| 7724 | try func.store(.stack, .stack, ty, ptr.offset()); | 7403 | try cg.store(.stack, .stack, ty, ptr.offset()); |
| 7725 | }, | 7404 | }, |
| 7726 | .Nand => { | 7405 | .Nand => { |
| 7727 | const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; | 7406 | const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; |
| 7728 | 7407 | ||
| 7729 | try func.emitWValue(ptr); | 7408 | try cg.emitWValue(ptr); |
| 7730 | const and_res = try func.binOp(result, operand, ty, .@"and"); | 7409 | const and_res = try cg.binOp(result, operand, ty, .@"and"); |
| 7731 | if (wasm_bits == 32) | 7410 | if (wasm_bits == 32) |
| 7732 | try func.addImm32(~@as(u32, 0)) | 7411 | try cg.addImm32(~@as(u32, 0)) |
| 7733 | else if (wasm_bits == 64) | 7412 | else if (wasm_bits == 64) |
| 7734 | try func.addImm64(~@as(u64, 0)) | 7413 | try cg.addImm64(~@as(u64, 0)) |
| 7735 | else | 7414 | else |
| 7736 | return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); | 7415 | return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); |
| 7737 | _ = try func.binOp(and_res, .stack, ty, .xor); | 7416 | _ = try cg.binOp(and_res, .stack, ty, .xor); |
| 7738 | try func.store(.stack, .stack, ty, ptr.offset()); | 7417 | try cg.store(.stack, .stack, ty, ptr.offset()); |
| 7739 | }, | 7418 | }, |
| 7740 | } | 7419 | } |
| 7741 | 7420 | ||
| 7742 | return func.finishAir(inst, result, &.{ pl_op.operand, extra.operand }); | 7421 | return cg.finishAir(inst, result, &.{ pl_op.operand, extra.operand }); |
| 7743 | } | 7422 | } |
| 7744 | } | 7423 | } |
| 7745 | 7424 | ||
| 7746 | fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7425 | fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7747 | const pt = func.pt; | 7426 | const zcu = cg.pt.zcu; |
| 7748 | const zcu = pt.zcu; | 7427 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 7749 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 7750 | 7428 | ||
| 7751 | const ptr = try func.resolveInst(bin_op.lhs); | 7429 | const ptr = try cg.resolveInst(bin_op.lhs); |
| 7752 | const operand = try func.resolveInst(bin_op.rhs); | 7430 | const operand = try cg.resolveInst(bin_op.rhs); |
| 7753 | const ptr_ty = func.typeOf(bin_op.lhs); | 7431 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 7754 | const ty = ptr_ty.childType(zcu); | 7432 | const ty = ptr_ty.childType(zcu); |
| 7755 | 7433 | ||
| 7756 | if (func.useAtomicFeature()) { | 7434 | if (cg.useAtomicFeature()) { |
| 7757 | const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | 7435 | const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { |
| 7758 | 1 => .i32_atomic_store8, | 7436 | 1 => .i32_atomic_store8, |
| 7759 | 2 => .i32_atomic_store16, | 7437 | 2 => .i32_atomic_store16, |
| 7760 | 4 => .i32_atomic_store, | 7438 | 4 => .i32_atomic_store, |
| 7761 | 8 => .i64_atomic_store, | 7439 | 8 => .i64_atomic_store, |
| 7762 | else => |size| return func.fail("TODO: @atomicLoad for types with abi size {d}", .{size}), | 7440 | else => |size| return cg.fail("TODO: @atomicLoad for types with abi size {d}", .{size}), |
| 7763 | }; | 7441 | }; |
| 7764 | try func.emitWValue(ptr); | 7442 | try cg.emitWValue(ptr); |
| 7765 | try func.lowerToStack(operand); | 7443 | try cg.lowerToStack(operand); |
| 7766 | try func.addAtomicMemArg(tag, .{ | 7444 | try cg.addAtomicMemArg(tag, .{ |
| 7767 | .offset = ptr.offset(), | 7445 | .offset = ptr.offset(), |
| 7768 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), | 7446 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7769 | }); | 7447 | }); |
| 7770 | } else { | 7448 | } else { |
| 7771 | try func.store(ptr, operand, ty, 0); | 7449 | try cg.store(ptr, operand, ty, 0); |
| 7772 | } | 7450 | } |
| 7773 | 7451 | ||
| 7774 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 7452 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 7775 | } | 7453 | } |
| 7776 | 7454 | ||
| 7777 | fn airFrameAddress(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7455 | fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7778 | if (func.initial_stack_value == .none) { | 7456 | if (cg.initial_stack_value == .none) { |
| 7779 | try func.initializeStack(); | 7457 | try cg.initializeStack(); |
| 7780 | } | 7458 | } |
| 7781 | try func.emitWValue(func.bottom_stack_value); | 7459 | try cg.emitWValue(cg.bottom_stack_value); |
| 7782 | return func.finishAir(inst, .stack, &.{}); | 7460 | return cg.finishAir(inst, .stack, &.{}); |
| 7783 | } | 7461 | } |
| 7784 | 7462 | ||
| 7785 | fn typeOf(func: *CodeGen, inst: Air.Inst.Ref) Type { | 7463 | fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type { |
| 7786 | const pt = func.pt; | 7464 | const zcu = cg.pt.zcu; |
| 7787 | const zcu = pt.zcu; | 7465 | return cg.air.typeOf(inst, &zcu.intern_pool); |
| 7788 | return func.air.typeOf(inst, &zcu.intern_pool); | ||
| 7789 | } | 7466 | } |
| 7790 | 7467 | ||
| 7791 | fn typeOfIndex(func: *CodeGen, inst: Air.Inst.Index) Type { | 7468 | fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type { |
| 7792 | const pt = func.pt; | 7469 | const zcu = cg.pt.zcu; |
| 7793 | const zcu = pt.zcu; | 7470 | return cg.air.typeOfIndex(inst, &zcu.intern_pool); |
| 7794 | return func.air.typeOfIndex(inst, &zcu.intern_pool); | 7471 | } |
| 7472 | |||
| 7473 | fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic { | ||
| 7474 | return switch (op) { | ||
| 7475 | .lt => switch (bits) { | ||
| 7476 | 80 => .__ltxf2, | ||
| 7477 | 128 => .__lttf2, | ||
| 7478 | else => unreachable, | ||
| 7479 | }, | ||
| 7480 | .lte => switch (bits) { | ||
| 7481 | 80 => .__lexf2, | ||
| 7482 | 128 => .__letf2, | ||
| 7483 | else => unreachable, | ||
| 7484 | }, | ||
| 7485 | .eq => switch (bits) { | ||
| 7486 | 80 => .__eqxf2, | ||
| 7487 | 128 => .__eqtf2, | ||
| 7488 | else => unreachable, | ||
| 7489 | }, | ||
| 7490 | .neq => switch (bits) { | ||
| 7491 | 80 => .__nexf2, | ||
| 7492 | 128 => .__netf2, | ||
| 7493 | else => unreachable, | ||
| 7494 | }, | ||
| 7495 | .gte => switch (bits) { | ||
| 7496 | 80 => .__gexf2, | ||
| 7497 | 128 => .__getf2, | ||
| 7498 | else => unreachable, | ||
| 7499 | }, | ||
| 7500 | .gt => switch (bits) { | ||
| 7501 | 80 => .__gtxf2, | ||
| 7502 | 128 => .__gttf2, | ||
| 7503 | else => unreachable, | ||
| 7504 | }, | ||
| 7505 | }; | ||
| 7506 | } | ||
| 7507 | |||
| 7508 | fn extraLen(cg: *const CodeGen) u32 { | ||
| 7509 | return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off); | ||
| 7795 | } | 7510 | } |
src/arch/wasm/Emit.zig+920-620| ... | @@ -1,673 +1,973 @@ | ... | @@ -1,673 +1,973 @@ |
| 1 | //! Contains all logic to lower wasm MIR into its binary | ||
| 2 | //! or textual representation. | ||
| 3 | |||
| 4 | const Emit = @This(); | 1 | const Emit = @This(); |
| 2 | |||
| 5 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const assert = std.debug.assert; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | const leb = std.leb; | ||
| 7 | |||
| 8 | const Wasm = link.File.Wasm; | ||
| 6 | const Mir = @import("Mir.zig"); | 9 | const Mir = @import("Mir.zig"); |
| 7 | const link = @import("../../link.zig"); | 10 | const link = @import("../../link.zig"); |
| 8 | const Zcu = @import("../../Zcu.zig"); | 11 | const Zcu = @import("../../Zcu.zig"); |
| 9 | const InternPool = @import("../../InternPool.zig"); | 12 | const InternPool = @import("../../InternPool.zig"); |
| 10 | const codegen = @import("../../codegen.zig"); | 13 | const codegen = @import("../../codegen.zig"); |
| 11 | const leb128 = std.leb; | ||
| 12 | 14 | ||
| 13 | /// Contains our list of instructions | ||
| 14 | mir: Mir, | 15 | mir: Mir, |
| 15 | /// Reference to the Wasm module linker | 16 | wasm: *Wasm, |
| 16 | bin_file: *link.File.Wasm, | 17 | /// The binary representation that will be emitted by this module. |
| 17 | /// Possible error message. When set, the value is allocated and | 18 | code: *std.ArrayListUnmanaged(u8), |
| 18 | /// must be freed manually. | 19 | |
| 19 | error_msg: ?*Zcu.ErrorMsg = null, | 20 | pub const Error = error{ |
| 20 | /// The binary representation that will be emit by this module. | ||
| 21 | code: *std.ArrayList(u8), | ||
| 22 | /// List of allocated locals. | ||
| 23 | locals: []const u8, | ||
| 24 | /// The declaration that code is being generated for. | ||
| 25 | owner_nav: InternPool.Nav.Index, | ||
| 26 | |||
| 27 | // Debug information | ||
| 28 | /// Holds the debug information for this emission | ||
| 29 | dbg_output: link.File.DebugInfoOutput, | ||
| 30 | /// Previous debug info line | ||
| 31 | prev_di_line: u32, | ||
| 32 | /// Previous debug info column | ||
| 33 | prev_di_column: u32, | ||
| 34 | /// Previous offset relative to code section | ||
| 35 | prev_di_offset: u32, | ||
| 36 | |||
| 37 | const InnerError = error{ | ||
| 38 | OutOfMemory, | 21 | OutOfMemory, |
| 39 | EmitFail, | ||
| 40 | }; | 22 | }; |
| 41 | 23 | ||
| 42 | pub fn emitMir(emit: *Emit) InnerError!void { | 24 | pub fn lowerToCode(emit: *Emit) Error!void { |
| 43 | const mir_tags = emit.mir.instructions.items(.tag); | 25 | const mir = &emit.mir; |
| 44 | // write the locals in the prologue of the function body | 26 | const code = emit.code; |
| 45 | // before we emit the function body when lowering MIR | 27 | const wasm = emit.wasm; |
| 46 | try emit.emitLocals(); | 28 | const comp = wasm.base.comp; |
| 47 | |||
| 48 | for (mir_tags, 0..) |tag, index| { | ||
| 49 | const inst = @as(u32, @intCast(index)); | ||
| 50 | switch (tag) { | ||
| 51 | // block instructions | ||
| 52 | .block => try emit.emitBlock(tag, inst), | ||
| 53 | .loop => try emit.emitBlock(tag, inst), | ||
| 54 | |||
| 55 | .dbg_line => try emit.emitDbgLine(inst), | ||
| 56 | .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(), | ||
| 57 | .dbg_prologue_end => try emit.emitDbgPrologueEnd(), | ||
| 58 | |||
| 59 | // branch instructions | ||
| 60 | .br_if => try emit.emitLabel(tag, inst), | ||
| 61 | .br_table => try emit.emitBrTable(inst), | ||
| 62 | .br => try emit.emitLabel(tag, inst), | ||
| 63 | |||
| 64 | // relocatables | ||
| 65 | .call => try emit.emitCall(inst), | ||
| 66 | .call_indirect => try emit.emitCallIndirect(inst), | ||
| 67 | .global_get => try emit.emitGlobal(tag, inst), | ||
| 68 | .global_set => try emit.emitGlobal(tag, inst), | ||
| 69 | .function_index => try emit.emitFunctionIndex(inst), | ||
| 70 | .memory_address => try emit.emitMemAddress(inst), | ||
| 71 | |||
| 72 | // immediates | ||
| 73 | .f32_const => try emit.emitFloat32(inst), | ||
| 74 | .f64_const => try emit.emitFloat64(inst), | ||
| 75 | .i32_const => try emit.emitImm32(inst), | ||
| 76 | .i64_const => try emit.emitImm64(inst), | ||
| 77 | |||
| 78 | // memory instructions | ||
| 79 | .i32_load => try emit.emitMemArg(tag, inst), | ||
| 80 | .i64_load => try emit.emitMemArg(tag, inst), | ||
| 81 | .f32_load => try emit.emitMemArg(tag, inst), | ||
| 82 | .f64_load => try emit.emitMemArg(tag, inst), | ||
| 83 | .i32_load8_s => try emit.emitMemArg(tag, inst), | ||
| 84 | .i32_load8_u => try emit.emitMemArg(tag, inst), | ||
| 85 | .i32_load16_s => try emit.emitMemArg(tag, inst), | ||
| 86 | .i32_load16_u => try emit.emitMemArg(tag, inst), | ||
| 87 | .i64_load8_s => try emit.emitMemArg(tag, inst), | ||
| 88 | .i64_load8_u => try emit.emitMemArg(tag, inst), | ||
| 89 | .i64_load16_s => try emit.emitMemArg(tag, inst), | ||
| 90 | .i64_load16_u => try emit.emitMemArg(tag, inst), | ||
| 91 | .i64_load32_s => try emit.emitMemArg(tag, inst), | ||
| 92 | .i64_load32_u => try emit.emitMemArg(tag, inst), | ||
| 93 | .i32_store => try emit.emitMemArg(tag, inst), | ||
| 94 | .i64_store => try emit.emitMemArg(tag, inst), | ||
| 95 | .f32_store => try emit.emitMemArg(tag, inst), | ||
| 96 | .f64_store => try emit.emitMemArg(tag, inst), | ||
| 97 | .i32_store8 => try emit.emitMemArg(tag, inst), | ||
| 98 | .i32_store16 => try emit.emitMemArg(tag, inst), | ||
| 99 | .i64_store8 => try emit.emitMemArg(tag, inst), | ||
| 100 | .i64_store16 => try emit.emitMemArg(tag, inst), | ||
| 101 | .i64_store32 => try emit.emitMemArg(tag, inst), | ||
| 102 | |||
| 103 | // Instructions with an index that do not require relocations | ||
| 104 | .local_get => try emit.emitLabel(tag, inst), | ||
| 105 | .local_set => try emit.emitLabel(tag, inst), | ||
| 106 | .local_tee => try emit.emitLabel(tag, inst), | ||
| 107 | .memory_grow => try emit.emitLabel(tag, inst), | ||
| 108 | .memory_size => try emit.emitLabel(tag, inst), | ||
| 109 | |||
| 110 | // no-ops | ||
| 111 | .end => try emit.emitTag(tag), | ||
| 112 | .@"return" => try emit.emitTag(tag), | ||
| 113 | .@"unreachable" => try emit.emitTag(tag), | ||
| 114 | |||
| 115 | .select => try emit.emitTag(tag), | ||
| 116 | |||
| 117 | // arithmetic | ||
| 118 | .i32_eqz => try emit.emitTag(tag), | ||
| 119 | .i32_eq => try emit.emitTag(tag), | ||
| 120 | .i32_ne => try emit.emitTag(tag), | ||
| 121 | .i32_lt_s => try emit.emitTag(tag), | ||
| 122 | .i32_lt_u => try emit.emitTag(tag), | ||
| 123 | .i32_gt_s => try emit.emitTag(tag), | ||
| 124 | .i32_gt_u => try emit.emitTag(tag), | ||
| 125 | .i32_le_s => try emit.emitTag(tag), | ||
| 126 | .i32_le_u => try emit.emitTag(tag), | ||
| 127 | .i32_ge_s => try emit.emitTag(tag), | ||
| 128 | .i32_ge_u => try emit.emitTag(tag), | ||
| 129 | .i64_eqz => try emit.emitTag(tag), | ||
| 130 | .i64_eq => try emit.emitTag(tag), | ||
| 131 | .i64_ne => try emit.emitTag(tag), | ||
| 132 | .i64_lt_s => try emit.emitTag(tag), | ||
| 133 | .i64_lt_u => try emit.emitTag(tag), | ||
| 134 | .i64_gt_s => try emit.emitTag(tag), | ||
| 135 | .i64_gt_u => try emit.emitTag(tag), | ||
| 136 | .i64_le_s => try emit.emitTag(tag), | ||
| 137 | .i64_le_u => try emit.emitTag(tag), | ||
| 138 | .i64_ge_s => try emit.emitTag(tag), | ||
| 139 | .i64_ge_u => try emit.emitTag(tag), | ||
| 140 | .f32_eq => try emit.emitTag(tag), | ||
| 141 | .f32_ne => try emit.emitTag(tag), | ||
| 142 | .f32_lt => try emit.emitTag(tag), | ||
| 143 | .f32_gt => try emit.emitTag(tag), | ||
| 144 | .f32_le => try emit.emitTag(tag), | ||
| 145 | .f32_ge => try emit.emitTag(tag), | ||
| 146 | .f64_eq => try emit.emitTag(tag), | ||
| 147 | .f64_ne => try emit.emitTag(tag), | ||
| 148 | .f64_lt => try emit.emitTag(tag), | ||
| 149 | .f64_gt => try emit.emitTag(tag), | ||
| 150 | .f64_le => try emit.emitTag(tag), | ||
| 151 | .f64_ge => try emit.emitTag(tag), | ||
| 152 | .i32_add => try emit.emitTag(tag), | ||
| 153 | .i32_sub => try emit.emitTag(tag), | ||
| 154 | .i32_mul => try emit.emitTag(tag), | ||
| 155 | .i32_div_s => try emit.emitTag(tag), | ||
| 156 | .i32_div_u => try emit.emitTag(tag), | ||
| 157 | .i32_and => try emit.emitTag(tag), | ||
| 158 | .i32_or => try emit.emitTag(tag), | ||
| 159 | .i32_xor => try emit.emitTag(tag), | ||
| 160 | .i32_shl => try emit.emitTag(tag), | ||
| 161 | .i32_shr_s => try emit.emitTag(tag), | ||
| 162 | .i32_shr_u => try emit.emitTag(tag), | ||
| 163 | .i64_add => try emit.emitTag(tag), | ||
| 164 | .i64_sub => try emit.emitTag(tag), | ||
| 165 | .i64_mul => try emit.emitTag(tag), | ||
| 166 | .i64_div_s => try emit.emitTag(tag), | ||
| 167 | .i64_div_u => try emit.emitTag(tag), | ||
| 168 | .i64_and => try emit.emitTag(tag), | ||
| 169 | .i64_or => try emit.emitTag(tag), | ||
| 170 | .i64_xor => try emit.emitTag(tag), | ||
| 171 | .i64_shl => try emit.emitTag(tag), | ||
| 172 | .i64_shr_s => try emit.emitTag(tag), | ||
| 173 | .i64_shr_u => try emit.emitTag(tag), | ||
| 174 | .f32_abs => try emit.emitTag(tag), | ||
| 175 | .f32_neg => try emit.emitTag(tag), | ||
| 176 | .f32_ceil => try emit.emitTag(tag), | ||
| 177 | .f32_floor => try emit.emitTag(tag), | ||
| 178 | .f32_trunc => try emit.emitTag(tag), | ||
| 179 | .f32_nearest => try emit.emitTag(tag), | ||
| 180 | .f32_sqrt => try emit.emitTag(tag), | ||
| 181 | .f32_add => try emit.emitTag(tag), | ||
| 182 | .f32_sub => try emit.emitTag(tag), | ||
| 183 | .f32_mul => try emit.emitTag(tag), | ||
| 184 | .f32_div => try emit.emitTag(tag), | ||
| 185 | .f32_min => try emit.emitTag(tag), | ||
| 186 | .f32_max => try emit.emitTag(tag), | ||
| 187 | .f32_copysign => try emit.emitTag(tag), | ||
| 188 | .f64_abs => try emit.emitTag(tag), | ||
| 189 | .f64_neg => try emit.emitTag(tag), | ||
| 190 | .f64_ceil => try emit.emitTag(tag), | ||
| 191 | .f64_floor => try emit.emitTag(tag), | ||
| 192 | .f64_trunc => try emit.emitTag(tag), | ||
| 193 | .f64_nearest => try emit.emitTag(tag), | ||
| 194 | .f64_sqrt => try emit.emitTag(tag), | ||
| 195 | .f64_add => try emit.emitTag(tag), | ||
| 196 | .f64_sub => try emit.emitTag(tag), | ||
| 197 | .f64_mul => try emit.emitTag(tag), | ||
| 198 | .f64_div => try emit.emitTag(tag), | ||
| 199 | .f64_min => try emit.emitTag(tag), | ||
| 200 | .f64_max => try emit.emitTag(tag), | ||
| 201 | .f64_copysign => try emit.emitTag(tag), | ||
| 202 | .i32_wrap_i64 => try emit.emitTag(tag), | ||
| 203 | .i64_extend_i32_s => try emit.emitTag(tag), | ||
| 204 | .i64_extend_i32_u => try emit.emitTag(tag), | ||
| 205 | .i32_extend8_s => try emit.emitTag(tag), | ||
| 206 | .i32_extend16_s => try emit.emitTag(tag), | ||
| 207 | .i64_extend8_s => try emit.emitTag(tag), | ||
| 208 | .i64_extend16_s => try emit.emitTag(tag), | ||
| 209 | .i64_extend32_s => try emit.emitTag(tag), | ||
| 210 | .f32_demote_f64 => try emit.emitTag(tag), | ||
| 211 | .f64_promote_f32 => try emit.emitTag(tag), | ||
| 212 | .i32_reinterpret_f32 => try emit.emitTag(tag), | ||
| 213 | .i64_reinterpret_f64 => try emit.emitTag(tag), | ||
| 214 | .f32_reinterpret_i32 => try emit.emitTag(tag), | ||
| 215 | .f64_reinterpret_i64 => try emit.emitTag(tag), | ||
| 216 | .i32_trunc_f32_s => try emit.emitTag(tag), | ||
| 217 | .i32_trunc_f32_u => try emit.emitTag(tag), | ||
| 218 | .i32_trunc_f64_s => try emit.emitTag(tag), | ||
| 219 | .i32_trunc_f64_u => try emit.emitTag(tag), | ||
| 220 | .i64_trunc_f32_s => try emit.emitTag(tag), | ||
| 221 | .i64_trunc_f32_u => try emit.emitTag(tag), | ||
| 222 | .i64_trunc_f64_s => try emit.emitTag(tag), | ||
| 223 | .i64_trunc_f64_u => try emit.emitTag(tag), | ||
| 224 | .f32_convert_i32_s => try emit.emitTag(tag), | ||
| 225 | .f32_convert_i32_u => try emit.emitTag(tag), | ||
| 226 | .f32_convert_i64_s => try emit.emitTag(tag), | ||
| 227 | .f32_convert_i64_u => try emit.emitTag(tag), | ||
| 228 | .f64_convert_i32_s => try emit.emitTag(tag), | ||
| 229 | .f64_convert_i32_u => try emit.emitTag(tag), | ||
| 230 | .f64_convert_i64_s => try emit.emitTag(tag), | ||
| 231 | .f64_convert_i64_u => try emit.emitTag(tag), | ||
| 232 | .i32_rem_s => try emit.emitTag(tag), | ||
| 233 | .i32_rem_u => try emit.emitTag(tag), | ||
| 234 | .i64_rem_s => try emit.emitTag(tag), | ||
| 235 | .i64_rem_u => try emit.emitTag(tag), | ||
| 236 | .i32_popcnt => try emit.emitTag(tag), | ||
| 237 | .i64_popcnt => try emit.emitTag(tag), | ||
| 238 | .i32_clz => try emit.emitTag(tag), | ||
| 239 | .i32_ctz => try emit.emitTag(tag), | ||
| 240 | .i64_clz => try emit.emitTag(tag), | ||
| 241 | .i64_ctz => try emit.emitTag(tag), | ||
| 242 | |||
| 243 | .misc_prefix => try emit.emitExtended(inst), | ||
| 244 | .simd_prefix => try emit.emitSimd(inst), | ||
| 245 | .atomics_prefix => try emit.emitAtomic(inst), | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | fn offset(self: Emit) u32 { | ||
| 251 | return @as(u32, @intCast(self.code.items.len)); | ||
| 252 | } | ||
| 253 | |||
| 254 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { | ||
| 255 | @branchHint(.cold); | ||
| 256 | std.debug.assert(emit.error_msg == null); | ||
| 257 | const comp = emit.bin_file.base.comp; | ||
| 258 | const zcu = comp.zcu.?; | ||
| 259 | const gpa = comp.gpa; | 29 | const gpa = comp.gpa; |
| 260 | emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args); | 30 | const is_obj = comp.config.output_mode == .Obj; |
| 261 | return error.EmitFail; | 31 | const target = &comp.root_mod.resolved_target.result; |
| 262 | } | 32 | const is_wasm32 = target.cpu.arch == .wasm32; |
| 263 | |||
| 264 | fn emitLocals(emit: *Emit) !void { | ||
| 265 | const writer = emit.code.writer(); | ||
| 266 | try leb128.writeUleb128(writer, @as(u32, @intCast(emit.locals.len))); | ||
| 267 | // emit the actual locals amount | ||
| 268 | for (emit.locals) |local| { | ||
| 269 | try leb128.writeUleb128(writer, @as(u32, 1)); | ||
| 270 | try writer.writeByte(local); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | fn emitTag(emit: *Emit, tag: Mir.Inst.Tag) !void { | ||
| 275 | try emit.code.append(@intFromEnum(tag)); | ||
| 276 | } | ||
| 277 | |||
| 278 | fn emitBlock(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { | ||
| 279 | const block_type = emit.mir.instructions.items(.data)[inst].block_type; | ||
| 280 | try emit.code.append(@intFromEnum(tag)); | ||
| 281 | try emit.code.append(block_type); | ||
| 282 | } | ||
| 283 | |||
| 284 | fn emitBrTable(emit: *Emit, inst: Mir.Inst.Index) !void { | ||
| 285 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | ||
| 286 | const extra = emit.mir.extraData(Mir.JumpTable, extra_index); | ||
| 287 | const labels = emit.mir.extra[extra.end..][0..extra.data.length]; | ||
| 288 | const writer = emit.code.writer(); | ||
| 289 | 33 | ||
| 290 | try emit.code.append(std.wasm.opcode(.br_table)); | 34 | const tags = mir.instruction_tags; |
| 291 | try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth | 35 | const datas = mir.instruction_datas; |
| 292 | for (labels) |label| { | 36 | var inst: u32 = 0; |
| 293 | try leb128.writeUleb128(writer, label); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | 37 | ||
| 297 | fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { | 38 | loop: switch (tags[inst]) { |
| 298 | const label = emit.mir.instructions.items(.data)[inst].label; | 39 | .dbg_epilogue_begin => { |
| 299 | try emit.code.append(@intFromEnum(tag)); | 40 | return; |
| 300 | try leb128.writeUleb128(emit.code.writer(), label); | 41 | }, |
| 301 | } | 42 | .block, .loop => { |
| 43 | const block_type = datas[inst].block_type; | ||
| 44 | try code.ensureUnusedCapacity(gpa, 2); | ||
| 45 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); | ||
| 46 | code.appendAssumeCapacity(@intFromEnum(block_type)); | ||
| 47 | |||
| 48 | inst += 1; | ||
| 49 | continue :loop tags[inst]; | ||
| 50 | }, | ||
| 51 | .uav_ref => { | ||
| 52 | if (is_obj) { | ||
| 53 | try uavRefOffObj(wasm, code, .{ .uav_obj = datas[inst].uav_obj, .offset = 0 }, is_wasm32); | ||
| 54 | } else { | ||
| 55 | try uavRefOffExe(wasm, code, .{ .uav_exe = datas[inst].uav_exe, .offset = 0 }, is_wasm32); | ||
| 56 | } | ||
| 57 | inst += 1; | ||
| 58 | continue :loop tags[inst]; | ||
| 59 | }, | ||
| 60 | .uav_ref_off => { | ||
| 61 | if (is_obj) { | ||
| 62 | try uavRefOffObj(wasm, code, mir.extraData(Mir.UavRefOffObj, datas[inst].payload).data, is_wasm32); | ||
| 63 | } else { | ||
| 64 | try uavRefOffExe(wasm, code, mir.extraData(Mir.UavRefOffExe, datas[inst].payload).data, is_wasm32); | ||
| 65 | } | ||
| 66 | inst += 1; | ||
| 67 | continue :loop tags[inst]; | ||
| 68 | }, | ||
| 69 | .nav_ref => { | ||
| 70 | try navRefOff(wasm, code, .{ .nav_index = datas[inst].nav_index, .offset = 0 }, is_wasm32); | ||
| 71 | inst += 1; | ||
| 72 | continue :loop tags[inst]; | ||
| 73 | }, | ||
| 74 | .nav_ref_off => { | ||
| 75 | try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data, is_wasm32); | ||
| 76 | inst += 1; | ||
| 77 | continue :loop tags[inst]; | ||
| 78 | }, | ||
| 79 | .func_ref => { | ||
| 80 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 81 | if (is_obj) { | ||
| 82 | @panic("TODO"); | ||
| 83 | } else { | ||
| 84 | leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(datas[inst].indirect_function_table_index)) catch unreachable; | ||
| 85 | } | ||
| 86 | inst += 1; | ||
| 87 | continue :loop tags[inst]; | ||
| 88 | }, | ||
| 89 | .dbg_line => { | ||
| 90 | inst += 1; | ||
| 91 | continue :loop tags[inst]; | ||
| 92 | }, | ||
| 93 | .errors_len => { | ||
| 94 | try code.ensureUnusedCapacity(gpa, 6); | ||
| 95 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 96 | // MIR is lowered during flush, so there is indeed only one thread at this time. | ||
| 97 | const errors_len = 1 + comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len; | ||
| 98 | leb.writeIleb128(code.fixedWriter(), errors_len) catch unreachable; | ||
| 99 | |||
| 100 | inst += 1; | ||
| 101 | continue :loop tags[inst]; | ||
| 102 | }, | ||
| 103 | .error_name_table_ref => { | ||
| 104 | try code.ensureUnusedCapacity(gpa, 11); | ||
| 105 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | ||
| 106 | code.appendAssumeCapacity(@intFromEnum(opcode)); | ||
| 107 | if (is_obj) { | ||
| 108 | try wasm.out_relocs.append(gpa, .{ | ||
| 109 | .offset = @intCast(code.items.len), | ||
| 110 | .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() }, | ||
| 111 | .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, | ||
| 112 | .addend = 0, | ||
| 113 | }); | ||
| 114 | code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); | ||
| 115 | |||
| 116 | inst += 1; | ||
| 117 | continue :loop tags[inst]; | ||
| 118 | } else { | ||
| 119 | const addr: u32 = wasm.errorNameTableAddr(); | ||
| 120 | leb.writeIleb128(code.fixedWriter(), addr) catch unreachable; | ||
| 121 | |||
| 122 | inst += 1; | ||
| 123 | continue :loop tags[inst]; | ||
| 124 | } | ||
| 125 | }, | ||
| 126 | .br_if, .br, .memory_grow, .memory_size => { | ||
| 127 | try code.ensureUnusedCapacity(gpa, 11); | ||
| 128 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); | ||
| 129 | leb.writeUleb128(code.fixedWriter(), datas[inst].label) catch unreachable; | ||
| 302 | 130 | ||
| 303 | fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { | 131 | inst += 1; |
| 304 | const comp = emit.bin_file.base.comp; | 132 | continue :loop tags[inst]; |
| 305 | const gpa = comp.gpa; | 133 | }, |
| 306 | const label = emit.mir.instructions.items(.data)[inst].label; | ||
| 307 | try emit.code.append(@intFromEnum(tag)); | ||
| 308 | var buf: [5]u8 = undefined; | ||
| 309 | leb128.writeUnsignedFixed(5, &buf, label); | ||
| 310 | const global_offset = emit.offset(); | ||
| 311 | try emit.code.appendSlice(&buf); | ||
| 312 | |||
| 313 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | ||
| 314 | const atom = emit.bin_file.getAtomPtr(atom_index); | ||
| 315 | try atom.relocs.append(gpa, .{ | ||
| 316 | .index = label, | ||
| 317 | .offset = global_offset, | ||
| 318 | .relocation_type = .R_WASM_GLOBAL_INDEX_LEB, | ||
| 319 | }); | ||
| 320 | } | ||
| 321 | 134 | ||
| 322 | fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void { | 135 | .local_get, .local_set, .local_tee => { |
| 323 | const value: i32 = emit.mir.instructions.items(.data)[inst].imm32; | 136 | try code.ensureUnusedCapacity(gpa, 11); |
| 324 | try emit.code.append(std.wasm.opcode(.i32_const)); | 137 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); |
| 325 | try leb128.writeIleb128(emit.code.writer(), value); | 138 | leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable; |
| 326 | } | ||
| 327 | 139 | ||
| 328 | fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void { | 140 | inst += 1; |
| 329 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 141 | continue :loop tags[inst]; |
| 330 | const value = emit.mir.extraData(Mir.Imm64, extra_index); | 142 | }, |
| 331 | try emit.code.append(std.wasm.opcode(.i64_const)); | ||
| 332 | try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64()))); | ||
| 333 | } | ||
| 334 | 143 | ||
| 335 | fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void { | 144 | .br_table => { |
| 336 | const value: f32 = emit.mir.instructions.items(.data)[inst].float32; | 145 | const extra_index = datas[inst].payload; |
| 337 | try emit.code.append(std.wasm.opcode(.f32_const)); | 146 | const extra = mir.extraData(Mir.JumpTable, extra_index); |
| 338 | try emit.code.writer().writeInt(u32, @bitCast(value), .little); | 147 | const labels = mir.extra[extra.end..][0..extra.data.length]; |
| 339 | } | 148 | try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len); |
| 149 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table)); | ||
| 150 | // -1 because default label is not part of length/depth. | ||
| 151 | leb.writeUleb128(code.fixedWriter(), extra.data.length - 1) catch unreachable; | ||
| 152 | for (labels) |label| leb.writeUleb128(code.fixedWriter(), label) catch unreachable; | ||
| 153 | |||
| 154 | inst += 1; | ||
| 155 | continue :loop tags[inst]; | ||
| 156 | }, | ||
| 340 | 157 | ||
| 341 | fn emitFloat64(emit: *Emit, inst: Mir.Inst.Index) !void { | 158 | .call_nav => { |
| 342 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 159 | try code.ensureUnusedCapacity(gpa, 6); |
| 343 | const value = emit.mir.extraData(Mir.Float64, extra_index); | 160 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); |
| 344 | try emit.code.append(std.wasm.opcode(.f64_const)); | 161 | if (is_obj) { |
| 345 | try emit.code.writer().writeInt(u64, value.data.toU64(), .little); | 162 | try wasm.out_relocs.append(gpa, .{ |
| 346 | } | 163 | .offset = @intCast(code.items.len), |
| 164 | .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) }, | ||
| 165 | .tag = .function_index_leb, | ||
| 166 | .addend = 0, | ||
| 167 | }); | ||
| 168 | code.appendNTimesAssumeCapacity(0, 5); | ||
| 169 | } else { | ||
| 170 | appendOutputFunctionIndex(code, .fromIpNav(wasm, datas[inst].nav_index)); | ||
| 171 | } | ||
| 172 | |||
| 173 | inst += 1; | ||
| 174 | continue :loop tags[inst]; | ||
| 175 | }, | ||
| 347 | 176 | ||
| 348 | fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { | 177 | .call_indirect => { |
| 349 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 178 | try code.ensureUnusedCapacity(gpa, 11); |
| 350 | const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data; | 179 | const func_ty_index = datas[inst].func_ty; |
| 351 | try emit.code.append(@intFromEnum(tag)); | 180 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect)); |
| 352 | try encodeMemArg(mem_arg, emit.code.writer()); | 181 | if (is_obj) { |
| 353 | } | 182 | try wasm.out_relocs.append(gpa, .{ |
| 183 | .offset = @intCast(code.items.len), | ||
| 184 | .pointee = .{ .type_index = func_ty_index }, | ||
| 185 | .tag = .type_index_leb, | ||
| 186 | .addend = 0, | ||
| 187 | }); | ||
| 188 | code.appendNTimesAssumeCapacity(0, 5); | ||
| 189 | } else { | ||
| 190 | const index: Wasm.Flush.FuncTypeIndex = .fromTypeIndex(func_ty_index, &wasm.flush_buffer); | ||
| 191 | leb.writeUleb128(code.fixedWriter(), @intFromEnum(index)) catch unreachable; | ||
| 192 | } | ||
| 193 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // table index | ||
| 194 | |||
| 195 | inst += 1; | ||
| 196 | continue :loop tags[inst]; | ||
| 197 | }, | ||
| 354 | 198 | ||
| 355 | fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void { | 199 | .call_tag_name => { |
| 356 | // wasm encodes alignment as power of 2, rather than natural alignment | 200 | try code.ensureUnusedCapacity(gpa, 6); |
| 357 | const encoded_alignment = @ctz(mem_arg.alignment); | 201 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); |
| 358 | try leb128.writeUleb128(writer, encoded_alignment); | 202 | if (is_obj) { |
| 359 | try leb128.writeUleb128(writer, mem_arg.offset); | 203 | try wasm.out_relocs.append(gpa, .{ |
| 360 | } | 204 | .offset = @intCast(code.items.len), |
| 205 | .pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) }, | ||
| 206 | .tag = .function_index_leb, | ||
| 207 | .addend = 0, | ||
| 208 | }); | ||
| 209 | code.appendNTimesAssumeCapacity(0, 5); | ||
| 210 | } else { | ||
| 211 | appendOutputFunctionIndex(code, .fromTagNameType(wasm, datas[inst].ip_index)); | ||
| 212 | } | ||
| 213 | |||
| 214 | inst += 1; | ||
| 215 | continue :loop tags[inst]; | ||
| 216 | }, | ||
| 361 | 217 | ||
| 362 | fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { | 218 | .call_intrinsic => { |
| 363 | const comp = emit.bin_file.base.comp; | 219 | // Although this currently uses `wasm.internString`, note that it |
| 364 | const gpa = comp.gpa; | 220 | // *could* be changed to directly index into a preloaded strings |
| 365 | const label = emit.mir.instructions.items(.data)[inst].label; | 221 | // table initialized based on the `Mir.Intrinsic` enum. |
| 366 | try emit.code.append(std.wasm.opcode(.call)); | 222 | const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic)); |
| 367 | const call_offset = emit.offset(); | 223 | |
| 368 | var buf: [5]u8 = undefined; | 224 | try code.ensureUnusedCapacity(gpa, 6); |
| 369 | leb128.writeUnsignedFixed(5, &buf, label); | 225 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); |
| 370 | try emit.code.appendSlice(&buf); | 226 | if (is_obj) { |
| 371 | 227 | try wasm.out_relocs.append(gpa, .{ | |
| 372 | if (label != 0) { | 228 | .offset = @intCast(code.items.len), |
| 373 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | 229 | .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) }, |
| 374 | const atom = emit.bin_file.getAtomPtr(atom_index); | 230 | .tag = .function_index_leb, |
| 375 | try atom.relocs.append(gpa, .{ | 231 | .addend = 0, |
| 376 | .offset = call_offset, | 232 | }); |
| 377 | .index = label, | 233 | code.appendNTimesAssumeCapacity(0, 5); |
| 378 | .relocation_type = .R_WASM_FUNCTION_INDEX_LEB, | 234 | } else { |
| 379 | }); | 235 | appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name)); |
| 380 | } | 236 | } |
| 381 | } | 237 | |
| 238 | inst += 1; | ||
| 239 | continue :loop tags[inst]; | ||
| 240 | }, | ||
| 382 | 241 | ||
| 383 | fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void { | 242 | .global_set_sp => { |
| 384 | const type_index = emit.mir.instructions.items(.data)[inst].label; | 243 | try code.ensureUnusedCapacity(gpa, 6); |
| 385 | try emit.code.append(std.wasm.opcode(.call_indirect)); | 244 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); |
| 386 | // NOTE: If we remove unused function types in the future for incremental | 245 | if (is_obj) { |
| 387 | // linking, we must also emit a relocation for this `type_index` | 246 | try wasm.out_relocs.append(gpa, .{ |
| 388 | const call_offset = emit.offset(); | 247 | .offset = @intCast(code.items.len), |
| 389 | var buf: [5]u8 = undefined; | 248 | .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() }, |
| 390 | leb128.writeUnsignedFixed(5, &buf, type_index); | 249 | .tag = .global_index_leb, |
| 391 | try emit.code.appendSlice(&buf); | 250 | .addend = 0, |
| 392 | if (type_index != 0) { | 251 | }); |
| 393 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | 252 | code.appendNTimesAssumeCapacity(0, 5); |
| 394 | const atom = emit.bin_file.getAtomPtr(atom_index); | 253 | } else { |
| 395 | try atom.relocs.append(emit.bin_file.base.comp.gpa, .{ | 254 | const sp_global: Wasm.GlobalIndex = .stack_pointer; |
| 396 | .offset = call_offset, | 255 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; |
| 397 | .index = type_index, | 256 | } |
| 398 | .relocation_type = .R_WASM_TYPE_INDEX_LEB, | 257 | |
| 399 | }); | 258 | inst += 1; |
| 400 | } | 259 | continue :loop tags[inst]; |
| 401 | try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index | 260 | }, |
| 402 | } | ||
| 403 | 261 | ||
| 404 | fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { | 262 | .f32_const => { |
| 405 | const comp = emit.bin_file.base.comp; | 263 | try code.ensureUnusedCapacity(gpa, 5); |
| 406 | const gpa = comp.gpa; | 264 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const)); |
| 407 | const symbol_index = emit.mir.instructions.items(.data)[inst].label; | 265 | std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little); |
| 408 | try emit.code.append(std.wasm.opcode(.i32_const)); | ||
| 409 | const index_offset = emit.offset(); | ||
| 410 | var buf: [5]u8 = undefined; | ||
| 411 | leb128.writeUnsignedFixed(5, &buf, symbol_index); | ||
| 412 | try emit.code.appendSlice(&buf); | ||
| 413 | |||
| 414 | if (symbol_index != 0) { | ||
| 415 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | ||
| 416 | const atom = emit.bin_file.getAtomPtr(atom_index); | ||
| 417 | try atom.relocs.append(gpa, .{ | ||
| 418 | .offset = index_offset, | ||
| 419 | .index = symbol_index, | ||
| 420 | .relocation_type = .R_WASM_TABLE_INDEX_SLEB, | ||
| 421 | }); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | 266 | ||
| 425 | fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { | 267 | inst += 1; |
| 426 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 268 | continue :loop tags[inst]; |
| 427 | const mem = emit.mir.extraData(Mir.Memory, extra_index).data; | 269 | }, |
| 428 | const mem_offset = emit.offset() + 1; | ||
| 429 | const comp = emit.bin_file.base.comp; | ||
| 430 | const gpa = comp.gpa; | ||
| 431 | const target = comp.root_mod.resolved_target.result; | ||
| 432 | const is_wasm32 = target.cpu.arch == .wasm32; | ||
| 433 | if (is_wasm32) { | ||
| 434 | try emit.code.append(std.wasm.opcode(.i32_const)); | ||
| 435 | var buf: [5]u8 = undefined; | ||
| 436 | leb128.writeUnsignedFixed(5, &buf, mem.pointer); | ||
| 437 | try emit.code.appendSlice(&buf); | ||
| 438 | } else { | ||
| 439 | try emit.code.append(std.wasm.opcode(.i64_const)); | ||
| 440 | var buf: [10]u8 = undefined; | ||
| 441 | leb128.writeUnsignedFixed(10, &buf, mem.pointer); | ||
| 442 | try emit.code.appendSlice(&buf); | ||
| 443 | } | ||
| 444 | 270 | ||
| 445 | if (mem.pointer != 0) { | 271 | .f64_const => { |
| 446 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | 272 | try code.ensureUnusedCapacity(gpa, 9); |
| 447 | const atom = emit.bin_file.getAtomPtr(atom_index); | 273 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const)); |
| 448 | try atom.relocs.append(gpa, .{ | 274 | const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data; |
| 449 | .offset = mem_offset, | 275 | std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little); |
| 450 | .index = mem.pointer, | ||
| 451 | .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, | ||
| 452 | .addend = @as(i32, @intCast(mem.offset)), | ||
| 453 | }); | ||
| 454 | } | ||
| 455 | } | ||
| 456 | 276 | ||
| 457 | fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void { | 277 | inst += 1; |
| 458 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 278 | continue :loop tags[inst]; |
| 459 | const opcode = emit.mir.extra[extra_index]; | ||
| 460 | const writer = emit.code.writer(); | ||
| 461 | try emit.code.append(std.wasm.opcode(.misc_prefix)); | ||
| 462 | try leb128.writeUleb128(writer, opcode); | ||
| 463 | switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) { | ||
| 464 | // bulk-memory opcodes | ||
| 465 | .data_drop => { | ||
| 466 | const segment = emit.mir.extra[extra_index + 1]; | ||
| 467 | try leb128.writeUleb128(writer, segment); | ||
| 468 | }, | ||
| 469 | .memory_init => { | ||
| 470 | const segment = emit.mir.extra[extra_index + 1]; | ||
| 471 | try leb128.writeUleb128(writer, segment); | ||
| 472 | try leb128.writeUleb128(writer, @as(u32, 0)); // memory index | ||
| 473 | }, | 279 | }, |
| 474 | .memory_fill => { | 280 | .i32_const => { |
| 475 | try leb128.writeUleb128(writer, @as(u32, 0)); // memory index | 281 | try code.ensureUnusedCapacity(gpa, 6); |
| 282 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 283 | leb.writeIleb128(code.fixedWriter(), datas[inst].imm32) catch unreachable; | ||
| 284 | |||
| 285 | inst += 1; | ||
| 286 | continue :loop tags[inst]; | ||
| 476 | }, | 287 | }, |
| 477 | .memory_copy => { | 288 | .i64_const => { |
| 478 | try leb128.writeUleb128(writer, @as(u32, 0)); // dst memory index | 289 | try code.ensureUnusedCapacity(gpa, 11); |
| 479 | try leb128.writeUleb128(writer, @as(u32, 0)); // src memory index | 290 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); |
| 291 | const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt()); | ||
| 292 | leb.writeIleb128(code.fixedWriter(), int64) catch unreachable; | ||
| 293 | |||
| 294 | inst += 1; | ||
| 295 | continue :loop tags[inst]; | ||
| 480 | }, | 296 | }, |
| 481 | 297 | ||
| 482 | // nontrapping-float-to-int-conversion opcodes | 298 | .i32_load, |
| 483 | .i32_trunc_sat_f32_s, | 299 | .i64_load, |
| 484 | .i32_trunc_sat_f32_u, | 300 | .f32_load, |
| 485 | .i32_trunc_sat_f64_s, | 301 | .f64_load, |
| 486 | .i32_trunc_sat_f64_u, | 302 | .i32_load8_s, |
| 487 | .i64_trunc_sat_f32_s, | 303 | .i32_load8_u, |
| 488 | .i64_trunc_sat_f32_u, | 304 | .i32_load16_s, |
| 489 | .i64_trunc_sat_f64_s, | 305 | .i32_load16_u, |
| 490 | .i64_trunc_sat_f64_u, | 306 | .i64_load8_s, |
| 491 | => {}, // opcode already written | 307 | .i64_load8_u, |
| 492 | else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}), | 308 | .i64_load16_s, |
| 493 | } | 309 | .i64_load16_u, |
| 494 | } | 310 | .i64_load32_s, |
| 495 | 311 | .i64_load32_u, | |
| 496 | fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void { | 312 | .i32_store, |
| 497 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 313 | .i64_store, |
| 498 | const opcode = emit.mir.extra[extra_index]; | 314 | .f32_store, |
| 499 | const writer = emit.code.writer(); | 315 | .f64_store, |
| 500 | try emit.code.append(std.wasm.opcode(.simd_prefix)); | 316 | .i32_store8, |
| 501 | try leb128.writeUleb128(writer, opcode); | 317 | .i32_store16, |
| 502 | switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) { | 318 | .i64_store8, |
| 503 | .v128_store, | 319 | .i64_store16, |
| 504 | .v128_load, | 320 | .i64_store32, |
| 505 | .v128_load8_splat, | ||
| 506 | .v128_load16_splat, | ||
| 507 | .v128_load32_splat, | ||
| 508 | .v128_load64_splat, | ||
| 509 | => { | 321 | => { |
| 510 | const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data; | 322 | try code.ensureUnusedCapacity(gpa, 1 + 20); |
| 511 | try encodeMemArg(mem_arg, writer); | 323 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); |
| 324 | encodeMemArg(code, mir.extraData(Mir.MemArg, datas[inst].payload).data); | ||
| 325 | inst += 1; | ||
| 326 | continue :loop tags[inst]; | ||
| 512 | }, | 327 | }, |
| 513 | .v128_const, | 328 | |
| 514 | .i8x16_shuffle, | 329 | .end, |
| 515 | => { | 330 | .@"return", |
| 516 | const simd_value = emit.mir.extra[extra_index + 1 ..][0..4]; | 331 | .@"unreachable", |
| 517 | try writer.writeAll(std.mem.asBytes(simd_value)); | 332 | .select, |
| 518 | }, | 333 | .i32_eqz, |
| 519 | .i8x16_extract_lane_s, | 334 | .i32_eq, |
| 520 | .i8x16_extract_lane_u, | 335 | .i32_ne, |
| 521 | .i8x16_replace_lane, | 336 | .i32_lt_s, |
| 522 | .i16x8_extract_lane_s, | 337 | .i32_lt_u, |
| 523 | .i16x8_extract_lane_u, | 338 | .i32_gt_s, |
| 524 | .i16x8_replace_lane, | 339 | .i32_gt_u, |
| 525 | .i32x4_extract_lane, | 340 | .i32_le_s, |
| 526 | .i32x4_replace_lane, | 341 | .i32_le_u, |
| 527 | .i64x2_extract_lane, | 342 | .i32_ge_s, |
| 528 | .i64x2_replace_lane, | 343 | .i32_ge_u, |
| 529 | .f32x4_extract_lane, | 344 | .i64_eqz, |
| 530 | .f32x4_replace_lane, | 345 | .i64_eq, |
| 531 | .f64x2_extract_lane, | 346 | .i64_ne, |
| 532 | .f64x2_replace_lane, | 347 | .i64_lt_s, |
| 348 | .i64_lt_u, | ||
| 349 | .i64_gt_s, | ||
| 350 | .i64_gt_u, | ||
| 351 | .i64_le_s, | ||
| 352 | .i64_le_u, | ||
| 353 | .i64_ge_s, | ||
| 354 | .i64_ge_u, | ||
| 355 | .f32_eq, | ||
| 356 | .f32_ne, | ||
| 357 | .f32_lt, | ||
| 358 | .f32_gt, | ||
| 359 | .f32_le, | ||
| 360 | .f32_ge, | ||
| 361 | .f64_eq, | ||
| 362 | .f64_ne, | ||
| 363 | .f64_lt, | ||
| 364 | .f64_gt, | ||
| 365 | .f64_le, | ||
| 366 | .f64_ge, | ||
| 367 | .i32_add, | ||
| 368 | .i32_sub, | ||
| 369 | .i32_mul, | ||
| 370 | .i32_div_s, | ||
| 371 | .i32_div_u, | ||
| 372 | .i32_and, | ||
| 373 | .i32_or, | ||
| 374 | .i32_xor, | ||
| 375 | .i32_shl, | ||
| 376 | .i32_shr_s, | ||
| 377 | .i32_shr_u, | ||
| 378 | .i64_add, | ||
| 379 | .i64_sub, | ||
| 380 | .i64_mul, | ||
| 381 | .i64_div_s, | ||
| 382 | .i64_div_u, | ||
| 383 | .i64_and, | ||
| 384 | .i64_or, | ||
| 385 | .i64_xor, | ||
| 386 | .i64_shl, | ||
| 387 | .i64_shr_s, | ||
| 388 | .i64_shr_u, | ||
| 389 | .f32_abs, | ||
| 390 | .f32_neg, | ||
| 391 | .f32_ceil, | ||
| 392 | .f32_floor, | ||
| 393 | .f32_trunc, | ||
| 394 | .f32_nearest, | ||
| 395 | .f32_sqrt, | ||
| 396 | .f32_add, | ||
| 397 | .f32_sub, | ||
| 398 | .f32_mul, | ||
| 399 | .f32_div, | ||
| 400 | .f32_min, | ||
| 401 | .f32_max, | ||
| 402 | .f32_copysign, | ||
| 403 | .f64_abs, | ||
| 404 | .f64_neg, | ||
| 405 | .f64_ceil, | ||
| 406 | .f64_floor, | ||
| 407 | .f64_trunc, | ||
| 408 | .f64_nearest, | ||
| 409 | .f64_sqrt, | ||
| 410 | .f64_add, | ||
| 411 | .f64_sub, | ||
| 412 | .f64_mul, | ||
| 413 | .f64_div, | ||
| 414 | .f64_min, | ||
| 415 | .f64_max, | ||
| 416 | .f64_copysign, | ||
| 417 | .i32_wrap_i64, | ||
| 418 | .i64_extend_i32_s, | ||
| 419 | .i64_extend_i32_u, | ||
| 420 | .i32_extend8_s, | ||
| 421 | .i32_extend16_s, | ||
| 422 | .i64_extend8_s, | ||
| 423 | .i64_extend16_s, | ||
| 424 | .i64_extend32_s, | ||
| 425 | .f32_demote_f64, | ||
| 426 | .f64_promote_f32, | ||
| 427 | .i32_reinterpret_f32, | ||
| 428 | .i64_reinterpret_f64, | ||
| 429 | .f32_reinterpret_i32, | ||
| 430 | .f64_reinterpret_i64, | ||
| 431 | .i32_trunc_f32_s, | ||
| 432 | .i32_trunc_f32_u, | ||
| 433 | .i32_trunc_f64_s, | ||
| 434 | .i32_trunc_f64_u, | ||
| 435 | .i64_trunc_f32_s, | ||
| 436 | .i64_trunc_f32_u, | ||
| 437 | .i64_trunc_f64_s, | ||
| 438 | .i64_trunc_f64_u, | ||
| 439 | .f32_convert_i32_s, | ||
| 440 | .f32_convert_i32_u, | ||
| 441 | .f32_convert_i64_s, | ||
| 442 | .f32_convert_i64_u, | ||
| 443 | .f64_convert_i32_s, | ||
| 444 | .f64_convert_i32_u, | ||
| 445 | .f64_convert_i64_s, | ||
| 446 | .f64_convert_i64_u, | ||
| 447 | .i32_rem_s, | ||
| 448 | .i32_rem_u, | ||
| 449 | .i64_rem_s, | ||
| 450 | .i64_rem_u, | ||
| 451 | .i32_popcnt, | ||
| 452 | .i64_popcnt, | ||
| 453 | .i32_clz, | ||
| 454 | .i32_ctz, | ||
| 455 | .i64_clz, | ||
| 456 | .i64_ctz, | ||
| 533 | => { | 457 | => { |
| 534 | try writer.writeByte(@as(u8, @intCast(emit.mir.extra[extra_index + 1]))); | 458 | try code.append(gpa, @intFromEnum(tags[inst])); |
| 459 | inst += 1; | ||
| 460 | continue :loop tags[inst]; | ||
| 535 | }, | 461 | }, |
| 536 | .i8x16_splat, | ||
| 537 | .i16x8_splat, | ||
| 538 | .i32x4_splat, | ||
| 539 | .i64x2_splat, | ||
| 540 | .f32x4_splat, | ||
| 541 | .f64x2_splat, | ||
| 542 | => {}, // opcode already written | ||
| 543 | else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}), | ||
| 544 | } | ||
| 545 | } | ||
| 546 | 462 | ||
| 547 | fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void { | 463 | .misc_prefix => { |
| 548 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 464 | try code.ensureUnusedCapacity(gpa, 6 + 6); |
| 549 | const opcode = emit.mir.extra[extra_index]; | 465 | const extra_index = datas[inst].payload; |
| 550 | const writer = emit.code.writer(); | 466 | const opcode = mir.extra[extra_index]; |
| 551 | try emit.code.append(std.wasm.opcode(.atomics_prefix)); | 467 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 552 | try leb128.writeUleb128(writer, opcode); | 468 | leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable; |
| 553 | switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) { | 469 | switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) { |
| 554 | .i32_atomic_load, | 470 | // bulk-memory opcodes |
| 555 | .i64_atomic_load, | 471 | .data_drop => { |
| 556 | .i32_atomic_load8_u, | 472 | const segment = mir.extra[extra_index + 1]; |
| 557 | .i32_atomic_load16_u, | 473 | leb.writeUleb128(code.fixedWriter(), segment) catch unreachable; |
| 558 | .i64_atomic_load8_u, | 474 | |
| 559 | .i64_atomic_load16_u, | 475 | inst += 1; |
| 560 | .i64_atomic_load32_u, | 476 | continue :loop tags[inst]; |
| 561 | .i32_atomic_store, | 477 | }, |
| 562 | .i64_atomic_store, | 478 | .memory_init => { |
| 563 | .i32_atomic_store8, | 479 | const segment = mir.extra[extra_index + 1]; |
| 564 | .i32_atomic_store16, | 480 | leb.writeUleb128(code.fixedWriter(), segment) catch unreachable; |
| 565 | .i64_atomic_store8, | 481 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index |
| 566 | .i64_atomic_store16, | 482 | |
| 567 | .i64_atomic_store32, | 483 | inst += 1; |
| 568 | .i32_atomic_rmw_add, | 484 | continue :loop tags[inst]; |
| 569 | .i64_atomic_rmw_add, | 485 | }, |
| 570 | .i32_atomic_rmw8_add_u, | 486 | .memory_fill => { |
| 571 | .i32_atomic_rmw16_add_u, | 487 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index |
| 572 | .i64_atomic_rmw8_add_u, | 488 | |
| 573 | .i64_atomic_rmw16_add_u, | 489 | inst += 1; |
| 574 | .i64_atomic_rmw32_add_u, | 490 | continue :loop tags[inst]; |
| 575 | .i32_atomic_rmw_sub, | 491 | }, |
| 576 | .i64_atomic_rmw_sub, | 492 | .memory_copy => { |
| 577 | .i32_atomic_rmw8_sub_u, | 493 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index |
| 578 | .i32_atomic_rmw16_sub_u, | 494 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index |
| 579 | .i64_atomic_rmw8_sub_u, | 495 | |
| 580 | .i64_atomic_rmw16_sub_u, | 496 | inst += 1; |
| 581 | .i64_atomic_rmw32_sub_u, | 497 | continue :loop tags[inst]; |
| 582 | .i32_atomic_rmw_and, | 498 | }, |
| 583 | .i64_atomic_rmw_and, | 499 | |
| 584 | .i32_atomic_rmw8_and_u, | 500 | // nontrapping-float-to-int-conversion opcodes |
| 585 | .i32_atomic_rmw16_and_u, | 501 | .i32_trunc_sat_f32_s, |
| 586 | .i64_atomic_rmw8_and_u, | 502 | .i32_trunc_sat_f32_u, |
| 587 | .i64_atomic_rmw16_and_u, | 503 | .i32_trunc_sat_f64_s, |
| 588 | .i64_atomic_rmw32_and_u, | 504 | .i32_trunc_sat_f64_u, |
| 589 | .i32_atomic_rmw_or, | 505 | .i64_trunc_sat_f32_s, |
| 590 | .i64_atomic_rmw_or, | 506 | .i64_trunc_sat_f32_u, |
| 591 | .i32_atomic_rmw8_or_u, | 507 | .i64_trunc_sat_f64_s, |
| 592 | .i32_atomic_rmw16_or_u, | 508 | .i64_trunc_sat_f64_u, |
| 593 | .i64_atomic_rmw8_or_u, | 509 | => { |
| 594 | .i64_atomic_rmw16_or_u, | 510 | inst += 1; |
| 595 | .i64_atomic_rmw32_or_u, | 511 | continue :loop tags[inst]; |
| 596 | .i32_atomic_rmw_xor, | 512 | }, |
| 597 | .i64_atomic_rmw_xor, | 513 | |
| 598 | .i32_atomic_rmw8_xor_u, | 514 | .table_init => @panic("TODO"), |
| 599 | .i32_atomic_rmw16_xor_u, | 515 | .elem_drop => @panic("TODO"), |
| 600 | .i64_atomic_rmw8_xor_u, | 516 | .table_copy => @panic("TODO"), |
| 601 | .i64_atomic_rmw16_xor_u, | 517 | .table_grow => @panic("TODO"), |
| 602 | .i64_atomic_rmw32_xor_u, | 518 | .table_size => @panic("TODO"), |
| 603 | .i32_atomic_rmw_xchg, | 519 | .table_fill => @panic("TODO"), |
| 604 | .i64_atomic_rmw_xchg, | 520 | |
| 605 | .i32_atomic_rmw8_xchg_u, | 521 | _ => unreachable, |
| 606 | .i32_atomic_rmw16_xchg_u, | 522 | } |
| 607 | .i64_atomic_rmw8_xchg_u, | 523 | comptime unreachable; |
| 608 | .i64_atomic_rmw16_xchg_u, | ||
| 609 | .i64_atomic_rmw32_xchg_u, | ||
| 610 | |||
| 611 | .i32_atomic_rmw_cmpxchg, | ||
| 612 | .i64_atomic_rmw_cmpxchg, | ||
| 613 | .i32_atomic_rmw8_cmpxchg_u, | ||
| 614 | .i32_atomic_rmw16_cmpxchg_u, | ||
| 615 | .i64_atomic_rmw8_cmpxchg_u, | ||
| 616 | .i64_atomic_rmw16_cmpxchg_u, | ||
| 617 | .i64_atomic_rmw32_cmpxchg_u, | ||
| 618 | => { | ||
| 619 | const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data; | ||
| 620 | try encodeMemArg(mem_arg, writer); | ||
| 621 | }, | 524 | }, |
| 622 | .atomic_fence => { | 525 | .simd_prefix => { |
| 623 | // TODO: When multi-memory proposal is accepted and implemented in the compiler, | 526 | try code.ensureUnusedCapacity(gpa, 6 + 20); |
| 624 | // change this to (user-)specified index, rather than hardcode it to memory index 0. | 527 | const extra_index = datas[inst].payload; |
| 625 | const memory_index: u32 = 0; | 528 | const opcode = mir.extra[extra_index]; |
| 626 | try leb128.writeUleb128(writer, memory_index); | 529 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix)); |
| 530 | leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable; | ||
| 531 | switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) { | ||
| 532 | .v128_store, | ||
| 533 | .v128_load, | ||
| 534 | .v128_load8_splat, | ||
| 535 | .v128_load16_splat, | ||
| 536 | .v128_load32_splat, | ||
| 537 | .v128_load64_splat, | ||
| 538 | => { | ||
| 539 | encodeMemArg(code, mir.extraData(Mir.MemArg, extra_index + 1).data); | ||
| 540 | inst += 1; | ||
| 541 | continue :loop tags[inst]; | ||
| 542 | }, | ||
| 543 | .v128_const, .i8x16_shuffle => { | ||
| 544 | code.appendSliceAssumeCapacity(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4])); | ||
| 545 | inst += 1; | ||
| 546 | continue :loop tags[inst]; | ||
| 547 | }, | ||
| 548 | .i8x16_extract_lane_s, | ||
| 549 | .i8x16_extract_lane_u, | ||
| 550 | .i8x16_replace_lane, | ||
| 551 | .i16x8_extract_lane_s, | ||
| 552 | .i16x8_extract_lane_u, | ||
| 553 | .i16x8_replace_lane, | ||
| 554 | .i32x4_extract_lane, | ||
| 555 | .i32x4_replace_lane, | ||
| 556 | .i64x2_extract_lane, | ||
| 557 | .i64x2_replace_lane, | ||
| 558 | .f32x4_extract_lane, | ||
| 559 | .f32x4_replace_lane, | ||
| 560 | .f64x2_extract_lane, | ||
| 561 | .f64x2_replace_lane, | ||
| 562 | => { | ||
| 563 | code.appendAssumeCapacity(@intCast(mir.extra[extra_index + 1])); | ||
| 564 | inst += 1; | ||
| 565 | continue :loop tags[inst]; | ||
| 566 | }, | ||
| 567 | .i8x16_splat, | ||
| 568 | .i16x8_splat, | ||
| 569 | .i32x4_splat, | ||
| 570 | .i64x2_splat, | ||
| 571 | .f32x4_splat, | ||
| 572 | .f64x2_splat, | ||
| 573 | => { | ||
| 574 | inst += 1; | ||
| 575 | continue :loop tags[inst]; | ||
| 576 | }, | ||
| 577 | |||
| 578 | .v128_load8x8_s => @panic("TODO"), | ||
| 579 | .v128_load8x8_u => @panic("TODO"), | ||
| 580 | .v128_load16x4_s => @panic("TODO"), | ||
| 581 | .v128_load16x4_u => @panic("TODO"), | ||
| 582 | .v128_load32x2_s => @panic("TODO"), | ||
| 583 | .v128_load32x2_u => @panic("TODO"), | ||
| 584 | .i8x16_swizzle => @panic("TODO"), | ||
| 585 | .i8x16_eq => @panic("TODO"), | ||
| 586 | .i16x8_eq => @panic("TODO"), | ||
| 587 | .i32x4_eq => @panic("TODO"), | ||
| 588 | .i8x16_ne => @panic("TODO"), | ||
| 589 | .i16x8_ne => @panic("TODO"), | ||
| 590 | .i32x4_ne => @panic("TODO"), | ||
| 591 | .i8x16_lt_s => @panic("TODO"), | ||
| 592 | .i16x8_lt_s => @panic("TODO"), | ||
| 593 | .i32x4_lt_s => @panic("TODO"), | ||
| 594 | .i8x16_lt_u => @panic("TODO"), | ||
| 595 | .i16x8_lt_u => @panic("TODO"), | ||
| 596 | .i32x4_lt_u => @panic("TODO"), | ||
| 597 | .i8x16_gt_s => @panic("TODO"), | ||
| 598 | .i16x8_gt_s => @panic("TODO"), | ||
| 599 | .i32x4_gt_s => @panic("TODO"), | ||
| 600 | .i8x16_gt_u => @panic("TODO"), | ||
| 601 | .i16x8_gt_u => @panic("TODO"), | ||
| 602 | .i32x4_gt_u => @panic("TODO"), | ||
| 603 | .i8x16_le_s => @panic("TODO"), | ||
| 604 | .i16x8_le_s => @panic("TODO"), | ||
| 605 | .i32x4_le_s => @panic("TODO"), | ||
| 606 | .i8x16_le_u => @panic("TODO"), | ||
| 607 | .i16x8_le_u => @panic("TODO"), | ||
| 608 | .i32x4_le_u => @panic("TODO"), | ||
| 609 | .i8x16_ge_s => @panic("TODO"), | ||
| 610 | .i16x8_ge_s => @panic("TODO"), | ||
| 611 | .i32x4_ge_s => @panic("TODO"), | ||
| 612 | .i8x16_ge_u => @panic("TODO"), | ||
| 613 | .i16x8_ge_u => @panic("TODO"), | ||
| 614 | .i32x4_ge_u => @panic("TODO"), | ||
| 615 | .f32x4_eq => @panic("TODO"), | ||
| 616 | .f64x2_eq => @panic("TODO"), | ||
| 617 | .f32x4_ne => @panic("TODO"), | ||
| 618 | .f64x2_ne => @panic("TODO"), | ||
| 619 | .f32x4_lt => @panic("TODO"), | ||
| 620 | .f64x2_lt => @panic("TODO"), | ||
| 621 | .f32x4_gt => @panic("TODO"), | ||
| 622 | .f64x2_gt => @panic("TODO"), | ||
| 623 | .f32x4_le => @panic("TODO"), | ||
| 624 | .f64x2_le => @panic("TODO"), | ||
| 625 | .f32x4_ge => @panic("TODO"), | ||
| 626 | .f64x2_ge => @panic("TODO"), | ||
| 627 | .v128_not => @panic("TODO"), | ||
| 628 | .v128_and => @panic("TODO"), | ||
| 629 | .v128_andnot => @panic("TODO"), | ||
| 630 | .v128_or => @panic("TODO"), | ||
| 631 | .v128_xor => @panic("TODO"), | ||
| 632 | .v128_bitselect => @panic("TODO"), | ||
| 633 | .v128_any_true => @panic("TODO"), | ||
| 634 | .v128_load8_lane => @panic("TODO"), | ||
| 635 | .v128_load16_lane => @panic("TODO"), | ||
| 636 | .v128_load32_lane => @panic("TODO"), | ||
| 637 | .v128_load64_lane => @panic("TODO"), | ||
| 638 | .v128_store8_lane => @panic("TODO"), | ||
| 639 | .v128_store16_lane => @panic("TODO"), | ||
| 640 | .v128_store32_lane => @panic("TODO"), | ||
| 641 | .v128_store64_lane => @panic("TODO"), | ||
| 642 | .v128_load32_zero => @panic("TODO"), | ||
| 643 | .v128_load64_zero => @panic("TODO"), | ||
| 644 | .f32x4_demote_f64x2_zero => @panic("TODO"), | ||
| 645 | .f64x2_promote_low_f32x4 => @panic("TODO"), | ||
| 646 | .i8x16_abs => @panic("TODO"), | ||
| 647 | .i16x8_abs => @panic("TODO"), | ||
| 648 | .i32x4_abs => @panic("TODO"), | ||
| 649 | .i64x2_abs => @panic("TODO"), | ||
| 650 | .i8x16_neg => @panic("TODO"), | ||
| 651 | .i16x8_neg => @panic("TODO"), | ||
| 652 | .i32x4_neg => @panic("TODO"), | ||
| 653 | .i64x2_neg => @panic("TODO"), | ||
| 654 | .i8x16_popcnt => @panic("TODO"), | ||
| 655 | .i16x8_q15mulr_sat_s => @panic("TODO"), | ||
| 656 | .i8x16_all_true => @panic("TODO"), | ||
| 657 | .i16x8_all_true => @panic("TODO"), | ||
| 658 | .i32x4_all_true => @panic("TODO"), | ||
| 659 | .i64x2_all_true => @panic("TODO"), | ||
| 660 | .i8x16_bitmask => @panic("TODO"), | ||
| 661 | .i16x8_bitmask => @panic("TODO"), | ||
| 662 | .i32x4_bitmask => @panic("TODO"), | ||
| 663 | .i64x2_bitmask => @panic("TODO"), | ||
| 664 | .i8x16_narrow_i16x8_s => @panic("TODO"), | ||
| 665 | .i16x8_narrow_i32x4_s => @panic("TODO"), | ||
| 666 | .i8x16_narrow_i16x8_u => @panic("TODO"), | ||
| 667 | .i16x8_narrow_i32x4_u => @panic("TODO"), | ||
| 668 | .f32x4_ceil => @panic("TODO"), | ||
| 669 | .i16x8_extend_low_i8x16_s => @panic("TODO"), | ||
| 670 | .i32x4_extend_low_i16x8_s => @panic("TODO"), | ||
| 671 | .i64x2_extend_low_i32x4_s => @panic("TODO"), | ||
| 672 | .f32x4_floor => @panic("TODO"), | ||
| 673 | .i16x8_extend_high_i8x16_s => @panic("TODO"), | ||
| 674 | .i32x4_extend_high_i16x8_s => @panic("TODO"), | ||
| 675 | .i64x2_extend_high_i32x4_s => @panic("TODO"), | ||
| 676 | .f32x4_trunc => @panic("TODO"), | ||
| 677 | .i16x8_extend_low_i8x16_u => @panic("TODO"), | ||
| 678 | .i32x4_extend_low_i16x8_u => @panic("TODO"), | ||
| 679 | .i64x2_extend_low_i32x4_u => @panic("TODO"), | ||
| 680 | .f32x4_nearest => @panic("TODO"), | ||
| 681 | .i16x8_extend_high_i8x16_u => @panic("TODO"), | ||
| 682 | .i32x4_extend_high_i16x8_u => @panic("TODO"), | ||
| 683 | .i64x2_extend_high_i32x4_u => @panic("TODO"), | ||
| 684 | .i8x16_shl => @panic("TODO"), | ||
| 685 | .i16x8_shl => @panic("TODO"), | ||
| 686 | .i32x4_shl => @panic("TODO"), | ||
| 687 | .i64x2_shl => @panic("TODO"), | ||
| 688 | .i8x16_shr_s => @panic("TODO"), | ||
| 689 | .i16x8_shr_s => @panic("TODO"), | ||
| 690 | .i32x4_shr_s => @panic("TODO"), | ||
| 691 | .i64x2_shr_s => @panic("TODO"), | ||
| 692 | .i8x16_shr_u => @panic("TODO"), | ||
| 693 | .i16x8_shr_u => @panic("TODO"), | ||
| 694 | .i32x4_shr_u => @panic("TODO"), | ||
| 695 | .i64x2_shr_u => @panic("TODO"), | ||
| 696 | .i8x16_add => @panic("TODO"), | ||
| 697 | .i16x8_add => @panic("TODO"), | ||
| 698 | .i32x4_add => @panic("TODO"), | ||
| 699 | .i64x2_add => @panic("TODO"), | ||
| 700 | .i8x16_add_sat_s => @panic("TODO"), | ||
| 701 | .i16x8_add_sat_s => @panic("TODO"), | ||
| 702 | .i8x16_add_sat_u => @panic("TODO"), | ||
| 703 | .i16x8_add_sat_u => @panic("TODO"), | ||
| 704 | .i8x16_sub => @panic("TODO"), | ||
| 705 | .i16x8_sub => @panic("TODO"), | ||
| 706 | .i32x4_sub => @panic("TODO"), | ||
| 707 | .i64x2_sub => @panic("TODO"), | ||
| 708 | .i8x16_sub_sat_s => @panic("TODO"), | ||
| 709 | .i16x8_sub_sat_s => @panic("TODO"), | ||
| 710 | .i8x16_sub_sat_u => @panic("TODO"), | ||
| 711 | .i16x8_sub_sat_u => @panic("TODO"), | ||
| 712 | .f64x2_ceil => @panic("TODO"), | ||
| 713 | .f64x2_nearest => @panic("TODO"), | ||
| 714 | .f64x2_floor => @panic("TODO"), | ||
| 715 | .i16x8_mul => @panic("TODO"), | ||
| 716 | .i32x4_mul => @panic("TODO"), | ||
| 717 | .i64x2_mul => @panic("TODO"), | ||
| 718 | .i8x16_min_s => @panic("TODO"), | ||
| 719 | .i16x8_min_s => @panic("TODO"), | ||
| 720 | .i32x4_min_s => @panic("TODO"), | ||
| 721 | .i64x2_eq => @panic("TODO"), | ||
| 722 | .i8x16_min_u => @panic("TODO"), | ||
| 723 | .i16x8_min_u => @panic("TODO"), | ||
| 724 | .i32x4_min_u => @panic("TODO"), | ||
| 725 | .i64x2_ne => @panic("TODO"), | ||
| 726 | .i8x16_max_s => @panic("TODO"), | ||
| 727 | .i16x8_max_s => @panic("TODO"), | ||
| 728 | .i32x4_max_s => @panic("TODO"), | ||
| 729 | .i64x2_lt_s => @panic("TODO"), | ||
| 730 | .i8x16_max_u => @panic("TODO"), | ||
| 731 | .i16x8_max_u => @panic("TODO"), | ||
| 732 | .i32x4_max_u => @panic("TODO"), | ||
| 733 | .i64x2_gt_s => @panic("TODO"), | ||
| 734 | .f64x2_trunc => @panic("TODO"), | ||
| 735 | .i32x4_dot_i16x8_s => @panic("TODO"), | ||
| 736 | .i64x2_le_s => @panic("TODO"), | ||
| 737 | .i8x16_avgr_u => @panic("TODO"), | ||
| 738 | .i16x8_avgr_u => @panic("TODO"), | ||
| 739 | .i64x2_ge_s => @panic("TODO"), | ||
| 740 | .i16x8_extadd_pairwise_i8x16_s => @panic("TODO"), | ||
| 741 | .i16x8_extmul_low_i8x16_s => @panic("TODO"), | ||
| 742 | .i32x4_extmul_low_i16x8_s => @panic("TODO"), | ||
| 743 | .i64x2_extmul_low_i32x4_s => @panic("TODO"), | ||
| 744 | .i16x8_extadd_pairwise_i8x16_u => @panic("TODO"), | ||
| 745 | .i16x8_extmul_high_i8x16_s => @panic("TODO"), | ||
| 746 | .i32x4_extmul_high_i16x8_s => @panic("TODO"), | ||
| 747 | .i64x2_extmul_high_i32x4_s => @panic("TODO"), | ||
| 748 | .i32x4_extadd_pairwise_i16x8_s => @panic("TODO"), | ||
| 749 | .i16x8_extmul_low_i8x16_u => @panic("TODO"), | ||
| 750 | .i32x4_extmul_low_i16x8_u => @panic("TODO"), | ||
| 751 | .i64x2_extmul_low_i32x4_u => @panic("TODO"), | ||
| 752 | .i32x4_extadd_pairwise_i16x8_u => @panic("TODO"), | ||
| 753 | .i16x8_extmul_high_i8x16_u => @panic("TODO"), | ||
| 754 | .i32x4_extmul_high_i16x8_u => @panic("TODO"), | ||
| 755 | .i64x2_extmul_high_i32x4_u => @panic("TODO"), | ||
| 756 | .f32x4_abs => @panic("TODO"), | ||
| 757 | .f64x2_abs => @panic("TODO"), | ||
| 758 | .f32x4_neg => @panic("TODO"), | ||
| 759 | .f64x2_neg => @panic("TODO"), | ||
| 760 | .f32x4_sqrt => @panic("TODO"), | ||
| 761 | .f64x2_sqrt => @panic("TODO"), | ||
| 762 | .f32x4_add => @panic("TODO"), | ||
| 763 | .f64x2_add => @panic("TODO"), | ||
| 764 | .f32x4_sub => @panic("TODO"), | ||
| 765 | .f64x2_sub => @panic("TODO"), | ||
| 766 | .f32x4_mul => @panic("TODO"), | ||
| 767 | .f64x2_mul => @panic("TODO"), | ||
| 768 | .f32x4_div => @panic("TODO"), | ||
| 769 | .f64x2_div => @panic("TODO"), | ||
| 770 | .f32x4_min => @panic("TODO"), | ||
| 771 | .f64x2_min => @panic("TODO"), | ||
| 772 | .f32x4_max => @panic("TODO"), | ||
| 773 | .f64x2_max => @panic("TODO"), | ||
| 774 | .f32x4_pmin => @panic("TODO"), | ||
| 775 | .f64x2_pmin => @panic("TODO"), | ||
| 776 | .f32x4_pmax => @panic("TODO"), | ||
| 777 | .f64x2_pmax => @panic("TODO"), | ||
| 778 | .i32x4_trunc_sat_f32x4_s => @panic("TODO"), | ||
| 779 | .i32x4_trunc_sat_f32x4_u => @panic("TODO"), | ||
| 780 | .f32x4_convert_i32x4_s => @panic("TODO"), | ||
| 781 | .f32x4_convert_i32x4_u => @panic("TODO"), | ||
| 782 | .i32x4_trunc_sat_f64x2_s_zero => @panic("TODO"), | ||
| 783 | .i32x4_trunc_sat_f64x2_u_zero => @panic("TODO"), | ||
| 784 | .f64x2_convert_low_i32x4_s => @panic("TODO"), | ||
| 785 | .f64x2_convert_low_i32x4_u => @panic("TODO"), | ||
| 786 | .i8x16_relaxed_swizzle => @panic("TODO"), | ||
| 787 | .i32x4_relaxed_trunc_f32x4_s => @panic("TODO"), | ||
| 788 | .i32x4_relaxed_trunc_f32x4_u => @panic("TODO"), | ||
| 789 | .i32x4_relaxed_trunc_f64x2_s_zero => @panic("TODO"), | ||
| 790 | .i32x4_relaxed_trunc_f64x2_u_zero => @panic("TODO"), | ||
| 791 | .f32x4_relaxed_madd => @panic("TODO"), | ||
| 792 | .f32x4_relaxed_nmadd => @panic("TODO"), | ||
| 793 | .f64x2_relaxed_madd => @panic("TODO"), | ||
| 794 | .f64x2_relaxed_nmadd => @panic("TODO"), | ||
| 795 | .i8x16_relaxed_laneselect => @panic("TODO"), | ||
| 796 | .i16x8_relaxed_laneselect => @panic("TODO"), | ||
| 797 | .i32x4_relaxed_laneselect => @panic("TODO"), | ||
| 798 | .i64x2_relaxed_laneselect => @panic("TODO"), | ||
| 799 | .f32x4_relaxed_min => @panic("TODO"), | ||
| 800 | .f32x4_relaxed_max => @panic("TODO"), | ||
| 801 | .f64x2_relaxed_min => @panic("TODO"), | ||
| 802 | .f64x2_relaxed_max => @panic("TODO"), | ||
| 803 | .i16x8_relaxed_q15mulr_s => @panic("TODO"), | ||
| 804 | .i16x8_relaxed_dot_i8x16_i7x16_s => @panic("TODO"), | ||
| 805 | .i32x4_relaxed_dot_i8x16_i7x16_add_s => @panic("TODO"), | ||
| 806 | .f32x4_relaxed_dot_bf16x8_add_f32x4 => @panic("TODO"), | ||
| 807 | } | ||
| 808 | comptime unreachable; | ||
| 809 | }, | ||
| 810 | .atomics_prefix => { | ||
| 811 | try code.ensureUnusedCapacity(gpa, 6 + 20); | ||
| 812 | |||
| 813 | const extra_index = datas[inst].payload; | ||
| 814 | const opcode = mir.extra[extra_index]; | ||
| 815 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); | ||
| 816 | leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable; | ||
| 817 | switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) { | ||
| 818 | .i32_atomic_load, | ||
| 819 | .i64_atomic_load, | ||
| 820 | .i32_atomic_load8_u, | ||
| 821 | .i32_atomic_load16_u, | ||
| 822 | .i64_atomic_load8_u, | ||
| 823 | .i64_atomic_load16_u, | ||
| 824 | .i64_atomic_load32_u, | ||
| 825 | .i32_atomic_store, | ||
| 826 | .i64_atomic_store, | ||
| 827 | .i32_atomic_store8, | ||
| 828 | .i32_atomic_store16, | ||
| 829 | .i64_atomic_store8, | ||
| 830 | .i64_atomic_store16, | ||
| 831 | .i64_atomic_store32, | ||
| 832 | .i32_atomic_rmw_add, | ||
| 833 | .i64_atomic_rmw_add, | ||
| 834 | .i32_atomic_rmw8_add_u, | ||
| 835 | .i32_atomic_rmw16_add_u, | ||
| 836 | .i64_atomic_rmw8_add_u, | ||
| 837 | .i64_atomic_rmw16_add_u, | ||
| 838 | .i64_atomic_rmw32_add_u, | ||
| 839 | .i32_atomic_rmw_sub, | ||
| 840 | .i64_atomic_rmw_sub, | ||
| 841 | .i32_atomic_rmw8_sub_u, | ||
| 842 | .i32_atomic_rmw16_sub_u, | ||
| 843 | .i64_atomic_rmw8_sub_u, | ||
| 844 | .i64_atomic_rmw16_sub_u, | ||
| 845 | .i64_atomic_rmw32_sub_u, | ||
| 846 | .i32_atomic_rmw_and, | ||
| 847 | .i64_atomic_rmw_and, | ||
| 848 | .i32_atomic_rmw8_and_u, | ||
| 849 | .i32_atomic_rmw16_and_u, | ||
| 850 | .i64_atomic_rmw8_and_u, | ||
| 851 | .i64_atomic_rmw16_and_u, | ||
| 852 | .i64_atomic_rmw32_and_u, | ||
| 853 | .i32_atomic_rmw_or, | ||
| 854 | .i64_atomic_rmw_or, | ||
| 855 | .i32_atomic_rmw8_or_u, | ||
| 856 | .i32_atomic_rmw16_or_u, | ||
| 857 | .i64_atomic_rmw8_or_u, | ||
| 858 | .i64_atomic_rmw16_or_u, | ||
| 859 | .i64_atomic_rmw32_or_u, | ||
| 860 | .i32_atomic_rmw_xor, | ||
| 861 | .i64_atomic_rmw_xor, | ||
| 862 | .i32_atomic_rmw8_xor_u, | ||
| 863 | .i32_atomic_rmw16_xor_u, | ||
| 864 | .i64_atomic_rmw8_xor_u, | ||
| 865 | .i64_atomic_rmw16_xor_u, | ||
| 866 | .i64_atomic_rmw32_xor_u, | ||
| 867 | .i32_atomic_rmw_xchg, | ||
| 868 | .i64_atomic_rmw_xchg, | ||
| 869 | .i32_atomic_rmw8_xchg_u, | ||
| 870 | .i32_atomic_rmw16_xchg_u, | ||
| 871 | .i64_atomic_rmw8_xchg_u, | ||
| 872 | .i64_atomic_rmw16_xchg_u, | ||
| 873 | .i64_atomic_rmw32_xchg_u, | ||
| 874 | |||
| 875 | .i32_atomic_rmw_cmpxchg, | ||
| 876 | .i64_atomic_rmw_cmpxchg, | ||
| 877 | .i32_atomic_rmw8_cmpxchg_u, | ||
| 878 | .i32_atomic_rmw16_cmpxchg_u, | ||
| 879 | .i64_atomic_rmw8_cmpxchg_u, | ||
| 880 | .i64_atomic_rmw16_cmpxchg_u, | ||
| 881 | .i64_atomic_rmw32_cmpxchg_u, | ||
| 882 | => { | ||
| 883 | const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data; | ||
| 884 | encodeMemArg(code, mem_arg); | ||
| 885 | inst += 1; | ||
| 886 | continue :loop tags[inst]; | ||
| 887 | }, | ||
| 888 | .atomic_fence => { | ||
| 889 | // Hard-codes memory index 0 since multi-memory proposal is | ||
| 890 | // not yet accepted nor implemented. | ||
| 891 | const memory_index: u32 = 0; | ||
| 892 | leb.writeUleb128(code.fixedWriter(), memory_index) catch unreachable; | ||
| 893 | inst += 1; | ||
| 894 | continue :loop tags[inst]; | ||
| 895 | }, | ||
| 896 | .memory_atomic_notify => @panic("TODO"), | ||
| 897 | .memory_atomic_wait32 => @panic("TODO"), | ||
| 898 | .memory_atomic_wait64 => @panic("TODO"), | ||
| 899 | } | ||
| 900 | comptime unreachable; | ||
| 627 | }, | 901 | }, |
| 628 | else => |tag| return emit.fail("TODO: Implement atomic instruction: {s}", .{@tagName(tag)}), | ||
| 629 | } | 902 | } |
| 903 | comptime unreachable; | ||
| 630 | } | 904 | } |
| 631 | 905 | ||
| 632 | fn emitMemFill(emit: *Emit) !void { | 906 | /// Asserts 20 unused capacity. |
| 633 | try emit.code.append(0xFC); | 907 | fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void { |
| 634 | try emit.code.append(0x0B); | 908 | assert(code.unusedCapacitySlice().len >= 20); |
| 635 | // When multi-memory proposal reaches phase 4, we | 909 | // Wasm encodes alignment as power of 2, rather than natural alignment. |
| 636 | // can emit a different memory index here. | 910 | const encoded_alignment = @ctz(mem_arg.alignment); |
| 637 | // For now we will always emit index 0. | 911 | leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable; |
| 638 | try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); | 912 | leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable; |
| 639 | } | ||
| 640 | |||
| 641 | fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { | ||
| 642 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | ||
| 643 | const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data; | ||
| 644 | try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column); | ||
| 645 | } | 913 | } |
| 646 | 914 | ||
| 647 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { | 915 | fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void { |
| 648 | if (emit.dbg_output != .dwarf) return; | 916 | const comp = wasm.base.comp; |
| 917 | const gpa = comp.gpa; | ||
| 918 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | ||
| 649 | 919 | ||
| 650 | const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line)); | 920 | try code.ensureUnusedCapacity(gpa, 11); |
| 651 | const delta_pc = emit.offset() - emit.prev_di_offset; | 921 | code.appendAssumeCapacity(@intFromEnum(opcode)); |
| 652 | // TODO: This must emit a relocation to calculate the offset relative | ||
| 653 | // to the code section start. | ||
| 654 | try emit.dbg_output.dwarf.advancePCAndLine(delta_line, delta_pc); | ||
| 655 | 922 | ||
| 656 | emit.prev_di_line = line; | 923 | try wasm.out_relocs.append(gpa, .{ |
| 657 | emit.prev_di_column = column; | 924 | .offset = @intCast(code.items.len), |
| 658 | emit.prev_di_offset = emit.offset(); | 925 | .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) }, |
| 926 | .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, | ||
| 927 | .addend = data.offset, | ||
| 928 | }); | ||
| 929 | code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); | ||
| 659 | } | 930 | } |
| 660 | 931 | ||
| 661 | fn emitDbgPrologueEnd(emit: *Emit) !void { | 932 | fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void { |
| 662 | if (emit.dbg_output != .dwarf) return; | 933 | const comp = wasm.base.comp; |
| 934 | const gpa = comp.gpa; | ||
| 935 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | ||
| 936 | |||
| 937 | try code.ensureUnusedCapacity(gpa, 11); | ||
| 938 | code.appendAssumeCapacity(@intFromEnum(opcode)); | ||
| 663 | 939 | ||
| 664 | try emit.dbg_output.dwarf.setPrologueEnd(); | 940 | const addr = wasm.uavAddr(data.uav_exe); |
| 665 | try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); | 941 | leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable; |
| 666 | } | 942 | } |
| 667 | 943 | ||
| 668 | fn emitDbgEpilogueBegin(emit: *Emit) !void { | 944 | fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void { |
| 669 | if (emit.dbg_output != .dwarf) return; | 945 | const comp = wasm.base.comp; |
| 946 | const zcu = comp.zcu.?; | ||
| 947 | const ip = &zcu.intern_pool; | ||
| 948 | const gpa = comp.gpa; | ||
| 949 | const is_obj = comp.config.output_mode == .Obj; | ||
| 950 | const nav_ty = ip.getNav(data.nav_index).typeOf(ip); | ||
| 951 | assert(!ip.isFunctionType(nav_ty)); | ||
| 952 | |||
| 953 | try code.ensureUnusedCapacity(gpa, 11); | ||
| 954 | |||
| 955 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | ||
| 956 | code.appendAssumeCapacity(@intFromEnum(opcode)); | ||
| 957 | if (is_obj) { | ||
| 958 | try wasm.out_relocs.append(gpa, .{ | ||
| 959 | .offset = @intCast(code.items.len), | ||
| 960 | .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) }, | ||
| 961 | .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, | ||
| 962 | .addend = data.offset, | ||
| 963 | }); | ||
| 964 | code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); | ||
| 965 | } else { | ||
| 966 | const addr = wasm.navAddr(data.nav_index); | ||
| 967 | leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable; | ||
| 968 | } | ||
| 969 | } | ||
| 670 | 970 | ||
| 671 | try emit.dbg_output.dwarf.setEpilogueBegin(); | 971 | fn appendOutputFunctionIndex(code: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) void { |
| 672 | try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); | 972 | leb.writeUleb128(code.fixedWriter(), @intFromEnum(i)) catch unreachable; |
| 673 | } | 973 | } |
src/arch/wasm/Mir.zig+338-102| ... | @@ -7,11 +7,15 @@ | ... | @@ -7,11 +7,15 @@ |
| 7 | //! and known jump labels for blocks. | 7 | //! and known jump labels for blocks. |
| 8 | 8 | ||
| 9 | const Mir = @This(); | 9 | const Mir = @This(); |
| 10 | const InternPool = @import("../../InternPool.zig"); | ||
| 11 | const Wasm = @import("../../link/Wasm.zig"); | ||
| 10 | 12 | ||
| 13 | const builtin = @import("builtin"); | ||
| 11 | const std = @import("std"); | 14 | const std = @import("std"); |
| 15 | const assert = std.debug.assert; | ||
| 12 | 16 | ||
| 13 | /// A struct of array that represents each individual wasm | 17 | instruction_tags: []const Inst.Tag, |
| 14 | instructions: std.MultiArrayList(Inst).Slice, | 18 | instruction_datas: []const Inst.Data, |
| 15 | /// A slice of indexes where the meaning of the data is determined by the | 19 | /// A slice of indexes where the meaning of the data is determined by the |
| 16 | /// `Inst.Tag` value. | 20 | /// `Inst.Tag` value. |
| 17 | extra: []const u32, | 21 | extra: []const u32, |
| ... | @@ -26,16 +30,14 @@ pub const Inst = struct { | ... | @@ -26,16 +30,14 @@ pub const Inst = struct { |
| 26 | /// The position of a given MIR isntruction with the instruction list. | 30 | /// The position of a given MIR isntruction with the instruction list. |
| 27 | pub const Index = u32; | 31 | pub const Index = u32; |
| 28 | 32 | ||
| 29 | /// Contains all possible wasm opcodes the Zig compiler may emit | 33 | /// Some tags match wasm opcode values to facilitate trivial lowering. |
| 30 | /// Rather than re-using std.wasm.Opcode, we only declare the opcodes | ||
| 31 | /// we need, and also use this possibility to document how to access | ||
| 32 | /// their payload. | ||
| 33 | /// | ||
| 34 | /// Note: Uses its actual opcode value representation to easily convert | ||
| 35 | /// to and from its binary representation. | ||
| 36 | pub const Tag = enum(u8) { | 34 | pub const Tag = enum(u8) { |
| 37 | /// Uses `nop` | 35 | /// Uses `tag`. |
| 38 | @"unreachable" = 0x00, | 36 | @"unreachable" = 0x00, |
| 37 | /// Emits epilogue begin debug information. Marks the end of the function. | ||
| 38 | /// | ||
| 39 | /// Uses `tag` (no additional data). | ||
| 40 | dbg_epilogue_begin, | ||
| 39 | /// Creates a new block that can be jump from. | 41 | /// Creates a new block that can be jump from. |
| 40 | /// | 42 | /// |
| 41 | /// Type of the block is given in data `block_type` | 43 | /// Type of the block is given in data `block_type` |
| ... | @@ -44,56 +46,92 @@ pub const Inst = struct { | ... | @@ -44,56 +46,92 @@ pub const Inst = struct { |
| 44 | /// | 46 | /// |
| 45 | /// Type of the loop is given in data `block_type` | 47 | /// Type of the loop is given in data `block_type` |
| 46 | loop = 0x03, | 48 | loop = 0x03, |
| 49 | /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the | ||
| 50 | /// memory address of an unnamed constant. When emitting an object | ||
| 51 | /// file, this adds a relocation. | ||
| 52 | /// | ||
| 53 | /// This may not refer to a function. | ||
| 54 | /// | ||
| 55 | /// Uses `ip_index`. | ||
| 56 | uav_ref, | ||
| 57 | /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the | ||
| 58 | /// memory address of an unnamed constant, offset by an integer value. | ||
| 59 | /// When emitting an object file, this adds a relocation. | ||
| 60 | /// | ||
| 61 | /// This may not refer to a function. | ||
| 62 | /// | ||
| 63 | /// Uses `payload` pointing to a `UavRefOff`. | ||
| 64 | uav_ref_off, | ||
| 65 | /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the | ||
| 66 | /// memory address of a named constant. | ||
| 67 | /// | ||
| 68 | /// May not refer to a function. | ||
| 69 | /// | ||
| 70 | /// Uses `nav_index`. | ||
| 71 | nav_ref, | ||
| 72 | /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the | ||
| 73 | /// memory address of named constant, offset by an integer value. | ||
| 74 | /// When emitting an object file, this adds a relocation. | ||
| 75 | /// | ||
| 76 | /// May not refer to a function. | ||
| 77 | /// | ||
| 78 | /// Uses `payload` pointing to a `NavRefOff`. | ||
| 79 | nav_ref_off, | ||
| 80 | /// Lowers to an i32_const which is the index of the function in the | ||
| 81 | /// table section. | ||
| 82 | /// | ||
| 83 | /// Uses `indirect_function_table_index`. | ||
| 84 | func_ref, | ||
| 47 | /// Inserts debug information about the current line and column | 85 | /// Inserts debug information about the current line and column |
| 48 | /// of the source code | 86 | /// of the source code |
| 49 | /// | 87 | /// |
| 50 | /// Uses `payload` of which the payload type is `DbgLineColumn` | 88 | /// Uses `payload` of which the payload type is `DbgLineColumn` |
| 51 | dbg_line = 0x06, | 89 | dbg_line, |
| 52 | /// Emits epilogue begin debug information | 90 | /// Lowers to an i32_const containing the number of unique Zig error |
| 53 | /// | 91 | /// names. |
| 54 | /// Uses `nop` | 92 | /// Uses `tag`. |
| 55 | dbg_epilogue_begin = 0x07, | 93 | errors_len, |
| 56 | /// Emits prologue end debug information | ||
| 57 | /// | ||
| 58 | /// Uses `nop` | ||
| 59 | dbg_prologue_end = 0x08, | ||
| 60 | /// Represents the end of a function body or an initialization expression | 94 | /// Represents the end of a function body or an initialization expression |
| 61 | /// | 95 | /// |
| 62 | /// Payload is `nop` | 96 | /// Uses `tag` (no additional data). |
| 63 | end = 0x0B, | 97 | end = 0x0B, |
| 64 | /// Breaks from the current block to a label | 98 | /// Breaks from the current block to a label |
| 65 | /// | 99 | /// |
| 66 | /// Data is `label` where index represents the label to jump to | 100 | /// Uses `label` where index represents the label to jump to |
| 67 | br = 0x0C, | 101 | br = 0x0C, |
| 68 | /// Breaks from the current block if the stack value is non-zero | 102 | /// Breaks from the current block if the stack value is non-zero |
| 69 | /// | 103 | /// |
| 70 | /// Data is `label` where index represents the label to jump to | 104 | /// Uses `label` where index represents the label to jump to |
| 71 | br_if = 0x0D, | 105 | br_if = 0x0D, |
| 72 | /// Jump table that takes the stack value as an index where each value | 106 | /// Jump table that takes the stack value as an index where each value |
| 73 | /// represents the label to jump to. | 107 | /// represents the label to jump to. |
| 74 | /// | 108 | /// |
| 75 | /// Data is extra of which the Payload's type is `JumpTable` | 109 | /// Data is extra of which the Payload's type is `JumpTable` |
| 76 | br_table = 0x0E, | 110 | br_table, |
| 77 | /// Returns from the function | 111 | /// Returns from the function |
| 78 | /// | 112 | /// |
| 79 | /// Uses `nop` | 113 | /// Uses `tag`. |
| 80 | @"return" = 0x0F, | 114 | @"return" = 0x0F, |
| 81 | /// Calls a function by its index | 115 | /// Lowers to an i32_const (wasm32) or i64_const (wasm64) containing |
| 82 | /// | 116 | /// the base address of the table of error code names, with each |
| 83 | /// Uses `label` | 117 | /// element being a null-terminated slice. |
| 84 | call = 0x10, | 118 | /// |
| 119 | /// Uses `tag`. | ||
| 120 | error_name_table_ref, | ||
| 121 | /// Calls a function using `nav_index`. | ||
| 122 | call_nav, | ||
| 85 | /// Calls a function pointer by its function signature | 123 | /// Calls a function pointer by its function signature |
| 86 | /// and index into the function table. | 124 | /// and index into the function table. |
| 87 | /// | 125 | /// |
| 88 | /// Uses `label` | 126 | /// Uses `func_ty` |
| 89 | call_indirect = 0x11, | 127 | call_indirect, |
| 90 | /// Contains a symbol to a function pointer | 128 | /// Calls a function by its index. |
| 91 | /// uses `label` | 129 | /// |
| 92 | /// | 130 | /// The function is the auto-generated tag name function for the type |
| 93 | /// Note: This uses `0x16` as value which is reserved by the WebAssembly | 131 | /// provided in `ip_index`. |
| 94 | /// specification but unused, meaning we must update this if the specification were to | 132 | call_tag_name, |
| 95 | /// use this value. | 133 | /// Lowers to a `call` instruction, using `intrinsic`. |
| 96 | function_index = 0x16, | 134 | call_intrinsic, |
| 97 | /// Pops three values from the stack and pushes | 135 | /// Pops three values from the stack and pushes |
| 98 | /// the first or second value dependent on the third value. | 136 | /// the first or second value dependent on the third value. |
| 99 | /// Uses `tag` | 137 | /// Uses `tag` |
| ... | @@ -112,15 +150,11 @@ pub const Inst = struct { | ... | @@ -112,15 +150,11 @@ pub const Inst = struct { |
| 112 | /// | 150 | /// |
| 113 | /// Uses `label` | 151 | /// Uses `label` |
| 114 | local_tee = 0x22, | 152 | local_tee = 0x22, |
| 115 | /// Loads a (mutable) global at given index onto the stack | 153 | /// Pops a value from the stack and sets the stack pointer global. |
| 116 | /// | 154 | /// The value must be the same type as the stack pointer global. |
| 117 | /// Uses `label` | ||
| 118 | global_get = 0x23, | ||
| 119 | /// Pops a value from the stack and sets the global at given index. | ||
| 120 | /// Note: Both types must be equal and global must be marked mutable. | ||
| 121 | /// | 155 | /// |
| 122 | /// Uses `label`. | 156 | /// Uses `tag` (no additional data). |
| 123 | global_set = 0x24, | 157 | global_set_sp, |
| 124 | /// Loads a 32-bit integer from memory (data section) onto the stack | 158 | /// Loads a 32-bit integer from memory (data section) onto the stack |
| 125 | /// Pops the value from the stack which represents the offset into memory. | 159 | /// Pops the value from the stack which represents the offset into memory. |
| 126 | /// | 160 | /// |
| ... | @@ -256,19 +290,19 @@ pub const Inst = struct { | ... | @@ -256,19 +290,19 @@ pub const Inst = struct { |
| 256 | /// Loads a 32-bit signed immediate value onto the stack | 290 | /// Loads a 32-bit signed immediate value onto the stack |
| 257 | /// | 291 | /// |
| 258 | /// Uses `imm32` | 292 | /// Uses `imm32` |
| 259 | i32_const = 0x41, | 293 | i32_const, |
| 260 | /// Loads a i64-bit signed immediate value onto the stack | 294 | /// Loads a i64-bit signed immediate value onto the stack |
| 261 | /// | 295 | /// |
| 262 | /// uses `payload` of type `Imm64` | 296 | /// uses `payload` of type `Imm64` |
| 263 | i64_const = 0x42, | 297 | i64_const, |
| 264 | /// Loads a 32-bit float value onto the stack. | 298 | /// Loads a 32-bit float value onto the stack. |
| 265 | /// | 299 | /// |
| 266 | /// Uses `float32` | 300 | /// Uses `float32` |
| 267 | f32_const = 0x43, | 301 | f32_const, |
| 268 | /// Loads a 64-bit float value onto the stack. | 302 | /// Loads a 64-bit float value onto the stack. |
| 269 | /// | 303 | /// |
| 270 | /// Uses `payload` of type `Float64` | 304 | /// Uses `payload` of type `Float64` |
| 271 | f64_const = 0x44, | 305 | f64_const, |
| 272 | /// Uses `tag` | 306 | /// Uses `tag` |
| 273 | i32_eqz = 0x45, | 307 | i32_eqz = 0x45, |
| 274 | /// Uses `tag` | 308 | /// Uses `tag` |
| ... | @@ -522,25 +556,19 @@ pub const Inst = struct { | ... | @@ -522,25 +556,19 @@ pub const Inst = struct { |
| 522 | /// | 556 | /// |
| 523 | /// The `data` field depends on the extension instruction and | 557 | /// The `data` field depends on the extension instruction and |
| 524 | /// may contain additional data. | 558 | /// may contain additional data. |
| 525 | misc_prefix = 0xFC, | 559 | misc_prefix, |
| 526 | /// The instruction consists of a simd opcode. | 560 | /// The instruction consists of a simd opcode. |
| 527 | /// The actual simd-opcode is found at payload's index. | 561 | /// The actual simd-opcode is found at payload's index. |
| 528 | /// | 562 | /// |
| 529 | /// The `data` field depends on the simd instruction and | 563 | /// The `data` field depends on the simd instruction and |
| 530 | /// may contain additional data. | 564 | /// may contain additional data. |
| 531 | simd_prefix = 0xFD, | 565 | simd_prefix, |
| 532 | /// The instruction consists of an atomics opcode. | 566 | /// The instruction consists of an atomics opcode. |
| 533 | /// The actual atomics-opcode is found at payload's index. | 567 | /// The actual atomics-opcode is found at payload's index. |
| 534 | /// | 568 | /// |
| 535 | /// The `data` field depends on the atomics instruction and | 569 | /// The `data` field depends on the atomics instruction and |
| 536 | /// may contain additional data. | 570 | /// may contain additional data. |
| 537 | atomics_prefix = 0xFE, | 571 | atomics_prefix = 0xFE, |
| 538 | /// Contains a symbol to a memory address | ||
| 539 | /// Uses `label` | ||
| 540 | /// | ||
| 541 | /// Note: This uses `0xFF` as value as it is unused and not reserved | ||
| 542 | /// by the wasm specification, making it safe to use. | ||
| 543 | memory_address = 0xFF, | ||
| 544 | 572 | ||
| 545 | /// From a given wasm opcode, returns a MIR tag. | 573 | /// From a given wasm opcode, returns a MIR tag. |
| 546 | pub fn fromOpcode(opcode: std.wasm.Opcode) Tag { | 574 | pub fn fromOpcode(opcode: std.wasm.Opcode) Tag { |
| ... | @@ -560,26 +588,41 @@ pub const Inst = struct { | ... | @@ -560,26 +588,41 @@ pub const Inst = struct { |
| 560 | /// Uses no additional data | 588 | /// Uses no additional data |
| 561 | tag: void, | 589 | tag: void, |
| 562 | /// Contains the result type of a block | 590 | /// Contains the result type of a block |
| 563 | /// | 591 | block_type: std.wasm.BlockType, |
| 564 | /// Used by `block` and `loop` | 592 | /// Label: Each structured control instruction introduces an implicit label. |
| 565 | block_type: u8, | 593 | /// Labels are targets for branch instructions that reference them with |
| 566 | /// Contains an u32 index into a wasm section entry, such as a local. | 594 | /// label indices. Unlike with other index spaces, indexing of labels |
| 567 | /// Note: This is not an index to another instruction. | 595 | /// is relative by nesting depth, that is, label 0 refers to the |
| 568 | /// | 596 | /// innermost structured control instruction enclosing the referring |
| 569 | /// Used by e.g. `local_get`, `local_set`, etc. | 597 | /// branch instruction, while increasing indices refer to those farther |
| 598 | /// out. Consequently, labels can only be referenced from within the | ||
| 599 | /// associated structured control instruction. | ||
| 570 | label: u32, | 600 | label: u32, |
| 601 | /// Local: The index space for locals is only accessible inside a function and | ||
| 602 | /// includes the parameters of that function, which precede the local | ||
| 603 | /// variables. | ||
| 604 | local: u32, | ||
| 571 | /// A 32-bit immediate value. | 605 | /// A 32-bit immediate value. |
| 572 | /// | ||
| 573 | /// Used by `i32_const` | ||
| 574 | imm32: i32, | 606 | imm32: i32, |
| 575 | /// A 32-bit float value | 607 | /// A 32-bit float value |
| 576 | /// | ||
| 577 | /// Used by `f32_float` | ||
| 578 | float32: f32, | 608 | float32: f32, |
| 579 | /// Index into `extra`. Meaning of what can be found there is context-dependent. | 609 | /// Index into `extra`. Meaning of what can be found there is context-dependent. |
| 580 | /// | ||
| 581 | /// Used by e.g. `br_table` | ||
| 582 | payload: u32, | 610 | payload: u32, |
| 611 | |||
| 612 | ip_index: InternPool.Index, | ||
| 613 | nav_index: InternPool.Nav.Index, | ||
| 614 | func_ty: Wasm.FunctionType.Index, | ||
| 615 | intrinsic: Intrinsic, | ||
| 616 | uav_obj: Wasm.UavsObjIndex, | ||
| 617 | uav_exe: Wasm.UavsExeIndex, | ||
| 618 | indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex, | ||
| 619 | |||
| 620 | comptime { | ||
| 621 | switch (builtin.mode) { | ||
| 622 | .Debug, .ReleaseSafe => {}, | ||
| 623 | .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4), | ||
| 624 | } | ||
| 625 | } | ||
| 583 | }; | 626 | }; |
| 584 | }; | 627 | }; |
| 585 | 628 | ||
| ... | @@ -596,6 +639,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data | ... | @@ -596,6 +639,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data |
| 596 | inline for (fields) |field| { | 639 | inline for (fields) |field| { |
| 597 | @field(result, field.name) = switch (field.type) { | 640 | @field(result, field.name) = switch (field.type) { |
| 598 | u32 => self.extra[i], | 641 | u32 => self.extra[i], |
| 642 | i32 => @bitCast(self.extra[i]), | ||
| 643 | Wasm.UavsObjIndex, | ||
| 644 | Wasm.UavsExeIndex, | ||
| 645 | InternPool.Nav.Index, | ||
| 646 | => @enumFromInt(self.extra[i]), | ||
| 599 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), | 647 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 600 | }; | 648 | }; |
| 601 | i += 1; | 649 | i += 1; |
| ... | @@ -609,28 +657,19 @@ pub const JumpTable = struct { | ... | @@ -609,28 +657,19 @@ pub const JumpTable = struct { |
| 609 | length: u32, | 657 | length: u32, |
| 610 | }; | 658 | }; |
| 611 | 659 | ||
| 612 | /// Stores an unsigned 64bit integer | ||
| 613 | /// into a 32bit most significant bits field | ||
| 614 | /// and a 32bit least significant bits field. | ||
| 615 | /// | ||
| 616 | /// This uses an unsigned integer rather than a signed integer | ||
| 617 | /// as we can easily store those into `extra` | ||
| 618 | pub const Imm64 = struct { | 660 | pub const Imm64 = struct { |
| 619 | msb: u32, | 661 | msb: u32, |
| 620 | lsb: u32, | 662 | lsb: u32, |
| 621 | 663 | ||
| 622 | pub fn fromU64(imm: u64) Imm64 { | 664 | pub fn init(full: u64) Imm64 { |
| 623 | return .{ | 665 | return .{ |
| 624 | .msb = @as(u32, @truncate(imm >> 32)), | 666 | .msb = @truncate(full >> 32), |
| 625 | .lsb = @as(u32, @truncate(imm)), | 667 | .lsb = @truncate(full), |
| 626 | }; | 668 | }; |
| 627 | } | 669 | } |
| 628 | 670 | ||
| 629 | pub fn toU64(self: Imm64) u64 { | 671 | pub fn toInt(i: Imm64) u64 { |
| 630 | var result: u64 = 0; | 672 | return (@as(u64, i.msb) << 32) | @as(u64, i.lsb); |
| 631 | result |= @as(u64, self.msb) << 32; | ||
| 632 | result |= @as(u64, self.lsb); | ||
| 633 | return result; | ||
| 634 | } | 673 | } |
| 635 | }; | 674 | }; |
| 636 | 675 | ||
| ... | @@ -638,23 +677,16 @@ pub const Float64 = struct { | ... | @@ -638,23 +677,16 @@ pub const Float64 = struct { |
| 638 | msb: u32, | 677 | msb: u32, |
| 639 | lsb: u32, | 678 | lsb: u32, |
| 640 | 679 | ||
| 641 | pub fn fromFloat64(float: f64) Float64 { | 680 | pub fn init(f: f64) Float64 { |
| 642 | const tmp = @as(u64, @bitCast(float)); | 681 | const int: u64 = @bitCast(f); |
| 643 | return .{ | 682 | return .{ |
| 644 | .msb = @as(u32, @truncate(tmp >> 32)), | 683 | .msb = @truncate(int >> 32), |
| 645 | .lsb = @as(u32, @truncate(tmp)), | 684 | .lsb = @truncate(int), |
| 646 | }; | 685 | }; |
| 647 | } | 686 | } |
| 648 | 687 | ||
| 649 | pub fn toF64(self: Float64) f64 { | 688 | pub fn toInt(f: Float64) u64 { |
| 650 | @as(f64, @bitCast(self.toU64())); | 689 | return (@as(u64, f.msb) << 32) | @as(u64, f.lsb); |
| 651 | } | ||
| 652 | |||
| 653 | pub fn toU64(self: Float64) u64 { | ||
| 654 | var result: u64 = 0; | ||
| 655 | result |= @as(u64, self.msb) << 32; | ||
| 656 | result |= @as(u64, self.lsb); | ||
| 657 | return result; | ||
| 658 | } | 690 | } |
| 659 | }; | 691 | }; |
| 660 | 692 | ||
| ... | @@ -663,11 +695,19 @@ pub const MemArg = struct { | ... | @@ -663,11 +695,19 @@ pub const MemArg = struct { |
| 663 | alignment: u32, | 695 | alignment: u32, |
| 664 | }; | 696 | }; |
| 665 | 697 | ||
| 666 | /// Represents a memory address, which holds both the pointer | 698 | pub const UavRefOffObj = struct { |
| 667 | /// or the parent pointer and the offset to it. | 699 | uav_obj: Wasm.UavsObjIndex, |
| 668 | pub const Memory = struct { | 700 | offset: i32, |
| 669 | pointer: u32, | 701 | }; |
| 670 | offset: u32, | 702 | |
| 703 | pub const UavRefOffExe = struct { | ||
| 704 | uav_exe: Wasm.UavsExeIndex, | ||
| 705 | offset: i32, | ||
| 706 | }; | ||
| 707 | |||
| 708 | pub const NavRefOff = struct { | ||
| 709 | nav_index: InternPool.Nav.Index, | ||
| 710 | offset: i32, | ||
| 671 | }; | 711 | }; |
| 672 | 712 | ||
| 673 | /// Maps a source line with wasm bytecode | 713 | /// Maps a source line with wasm bytecode |
| ... | @@ -675,3 +715,199 @@ pub const DbgLineColumn = struct { | ... | @@ -675,3 +715,199 @@ pub const DbgLineColumn = struct { |
| 675 | line: u32, | 715 | line: u32, |
| 676 | column: u32, | 716 | column: u32, |
| 677 | }; | 717 | }; |
| 718 | |||
| 719 | /// Tag names exactly match the corresponding symbol name. | ||
| 720 | pub const Intrinsic = enum(u32) { | ||
| 721 | __addhf3, | ||
| 722 | __addtf3, | ||
| 723 | __addxf3, | ||
| 724 | __ashlti3, | ||
| 725 | __ashrti3, | ||
| 726 | __bitreversedi2, | ||
| 727 | __bitreversesi2, | ||
| 728 | __bswapdi2, | ||
| 729 | __bswapsi2, | ||
| 730 | __ceilh, | ||
| 731 | __ceilx, | ||
| 732 | __cosh, | ||
| 733 | __cosx, | ||
| 734 | __divhf3, | ||
| 735 | __divtf3, | ||
| 736 | __divti3, | ||
| 737 | __divxf3, | ||
| 738 | __eqtf2, | ||
| 739 | __eqxf2, | ||
| 740 | __exp2h, | ||
| 741 | __exp2x, | ||
| 742 | __exph, | ||
| 743 | __expx, | ||
| 744 | __extenddftf2, | ||
| 745 | __extenddfxf2, | ||
| 746 | __extendhfsf2, | ||
| 747 | __extendhftf2, | ||
| 748 | __extendhfxf2, | ||
| 749 | __extendsftf2, | ||
| 750 | __extendsfxf2, | ||
| 751 | __extendxftf2, | ||
| 752 | __fabsh, | ||
| 753 | __fabsx, | ||
| 754 | __fixdfdi, | ||
| 755 | __fixdfsi, | ||
| 756 | __fixdfti, | ||
| 757 | __fixhfdi, | ||
| 758 | __fixhfsi, | ||
| 759 | __fixhfti, | ||
| 760 | __fixsfdi, | ||
| 761 | __fixsfsi, | ||
| 762 | __fixsfti, | ||
| 763 | __fixtfdi, | ||
| 764 | __fixtfsi, | ||
| 765 | __fixtfti, | ||
| 766 | __fixunsdfdi, | ||
| 767 | __fixunsdfsi, | ||
| 768 | __fixunsdfti, | ||
| 769 | __fixunshfdi, | ||
| 770 | __fixunshfsi, | ||
| 771 | __fixunshfti, | ||
| 772 | __fixunssfdi, | ||
| 773 | __fixunssfsi, | ||
| 774 | __fixunssfti, | ||
| 775 | __fixunstfdi, | ||
| 776 | __fixunstfsi, | ||
| 777 | __fixunstfti, | ||
| 778 | __fixunsxfdi, | ||
| 779 | __fixunsxfsi, | ||
| 780 | __fixunsxfti, | ||
| 781 | __fixxfdi, | ||
| 782 | __fixxfsi, | ||
| 783 | __fixxfti, | ||
| 784 | __floatdidf, | ||
| 785 | __floatdihf, | ||
| 786 | __floatdisf, | ||
| 787 | __floatditf, | ||
| 788 | __floatdixf, | ||
| 789 | __floatsidf, | ||
| 790 | __floatsihf, | ||
| 791 | __floatsisf, | ||
| 792 | __floatsitf, | ||
| 793 | __floatsixf, | ||
| 794 | __floattidf, | ||
| 795 | __floattihf, | ||
| 796 | __floattisf, | ||
| 797 | __floattitf, | ||
| 798 | __floattixf, | ||
| 799 | __floatundidf, | ||
| 800 | __floatundihf, | ||
| 801 | __floatundisf, | ||
| 802 | __floatunditf, | ||
| 803 | __floatundixf, | ||
| 804 | __floatunsidf, | ||
| 805 | __floatunsihf, | ||
| 806 | __floatunsisf, | ||
| 807 | __floatunsitf, | ||
| 808 | __floatunsixf, | ||
| 809 | __floatuntidf, | ||
| 810 | __floatuntihf, | ||
| 811 | __floatuntisf, | ||
| 812 | __floatuntitf, | ||
| 813 | __floatuntixf, | ||
| 814 | __floorh, | ||
| 815 | __floorx, | ||
| 816 | __fmah, | ||
| 817 | __fmax, | ||
| 818 | __fmaxh, | ||
| 819 | __fmaxx, | ||
| 820 | __fminh, | ||
| 821 | __fminx, | ||
| 822 | __fmodh, | ||
| 823 | __fmodx, | ||
| 824 | __getf2, | ||
| 825 | __gexf2, | ||
| 826 | __gttf2, | ||
| 827 | __gtxf2, | ||
| 828 | __letf2, | ||
| 829 | __lexf2, | ||
| 830 | __log10h, | ||
| 831 | __log10x, | ||
| 832 | __log2h, | ||
| 833 | __log2x, | ||
| 834 | __logh, | ||
| 835 | __logx, | ||
| 836 | __lshrti3, | ||
| 837 | __lttf2, | ||
| 838 | __ltxf2, | ||
| 839 | __modti3, | ||
| 840 | __mulhf3, | ||
| 841 | __mulodi4, | ||
| 842 | __muloti4, | ||
| 843 | __multf3, | ||
| 844 | __multi3, | ||
| 845 | __mulxf3, | ||
| 846 | __netf2, | ||
| 847 | __nexf2, | ||
| 848 | __roundh, | ||
| 849 | __roundx, | ||
| 850 | __sinh, | ||
| 851 | __sinx, | ||
| 852 | __sqrth, | ||
| 853 | __sqrtx, | ||
| 854 | __subhf3, | ||
| 855 | __subtf3, | ||
| 856 | __subxf3, | ||
| 857 | __tanh, | ||
| 858 | __tanx, | ||
| 859 | __trunch, | ||
| 860 | __truncsfhf2, | ||
| 861 | __trunctfdf2, | ||
| 862 | __trunctfhf2, | ||
| 863 | __trunctfsf2, | ||
| 864 | __trunctfxf2, | ||
| 865 | __truncx, | ||
| 866 | __truncxfdf2, | ||
| 867 | __truncxfhf2, | ||
| 868 | __truncxfsf2, | ||
| 869 | __udivti3, | ||
| 870 | __umodti3, | ||
| 871 | ceilq, | ||
| 872 | cos, | ||
| 873 | cosf, | ||
| 874 | cosq, | ||
| 875 | exp, | ||
| 876 | exp2, | ||
| 877 | exp2f, | ||
| 878 | exp2q, | ||
| 879 | expf, | ||
| 880 | expq, | ||
| 881 | fabsq, | ||
| 882 | floorq, | ||
| 883 | fma, | ||
| 884 | fmaf, | ||
| 885 | fmaq, | ||
| 886 | fmax, | ||
| 887 | fmaxf, | ||
| 888 | fmaxq, | ||
| 889 | fmin, | ||
| 890 | fminf, | ||
| 891 | fminq, | ||
| 892 | fmod, | ||
| 893 | fmodf, | ||
| 894 | fmodq, | ||
| 895 | log, | ||
| 896 | log10, | ||
| 897 | log10f, | ||
| 898 | log10q, | ||
| 899 | log2, | ||
| 900 | log2f, | ||
| 901 | log2q, | ||
| 902 | logf, | ||
| 903 | logq, | ||
| 904 | roundq, | ||
| 905 | sin, | ||
| 906 | sinf, | ||
| 907 | sinq, | ||
| 908 | sqrtq, | ||
| 909 | tan, | ||
| 910 | tanf, | ||
| 911 | tanq, | ||
| 912 | truncq, | ||
| 913 | }; |
src/arch/wasm/abi.zig+1-1| ... | @@ -22,7 +22,7 @@ const direct: [2]Class = .{ .direct, .none }; | ... | @@ -22,7 +22,7 @@ const direct: [2]Class = .{ .direct, .none }; |
| 22 | /// Classifies a given Zig type to determine how they must be passed | 22 | /// Classifies a given Zig type to determine how they must be passed |
| 23 | /// or returned as value within a wasm function. | 23 | /// or returned as value within a wasm function. |
| 24 | /// When all elements result in `.none`, no value must be passed in or returned. | 24 | /// When all elements result in `.none`, no value must be passed in or returned. |
| 25 | pub fn classifyType(ty: Type, zcu: *Zcu) [2]Class { | 25 | pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class { |
| 26 | const ip = &zcu.intern_pool; | 26 | const ip = &zcu.intern_pool; |
| 27 | const target = zcu.getTarget(); | 27 | const target = zcu.getTarget(); |
| 28 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none; | 28 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none; |
src/arch/x86_64/CodeGen.zig+36-85| ... | @@ -19,7 +19,6 @@ const Allocator = mem.Allocator; | ... | @@ -19,7 +19,6 @@ const Allocator = mem.Allocator; |
| 19 | const CodeGenError = codegen.CodeGenError; | 19 | const CodeGenError = codegen.CodeGenError; |
| 20 | const Compilation = @import("../../Compilation.zig"); | 20 | const Compilation = @import("../../Compilation.zig"); |
| 21 | const ErrorMsg = Zcu.ErrorMsg; | 21 | const ErrorMsg = Zcu.ErrorMsg; |
| 22 | const Result = codegen.Result; | ||
| 23 | const Emit = @import("Emit.zig"); | 22 | const Emit = @import("Emit.zig"); |
| 24 | const Liveness = @import("../../Liveness.zig"); | 23 | const Liveness = @import("../../Liveness.zig"); |
| 25 | const Lower = @import("Lower.zig"); | 24 | const Lower = @import("Lower.zig"); |
| ... | @@ -59,7 +58,6 @@ target: *const std.Target, | ... | @@ -59,7 +58,6 @@ target: *const std.Target, |
| 59 | owner: Owner, | 58 | owner: Owner, |
| 60 | inline_func: InternPool.Index, | 59 | inline_func: InternPool.Index, |
| 61 | mod: *Package.Module, | 60 | mod: *Package.Module, |
| 62 | err_msg: ?*ErrorMsg, | ||
| 63 | arg_index: u32, | 61 | arg_index: u32, |
| 64 | args: []MCValue, | 62 | args: []MCValue, |
| 65 | va_info: union { | 63 | va_info: union { |
| ... | @@ -819,9 +817,9 @@ pub fn generate( | ... | @@ -819,9 +817,9 @@ pub fn generate( |
| 819 | func_index: InternPool.Index, | 817 | func_index: InternPool.Index, |
| 820 | air: Air, | 818 | air: Air, |
| 821 | liveness: Liveness, | 819 | liveness: Liveness, |
| 822 | code: *std.ArrayList(u8), | 820 | code: *std.ArrayListUnmanaged(u8), |
| 823 | debug_output: link.File.DebugInfoOutput, | 821 | debug_output: link.File.DebugInfoOutput, |
| 824 | ) CodeGenError!Result { | 822 | ) CodeGenError!void { |
| 825 | const zcu = pt.zcu; | 823 | const zcu = pt.zcu; |
| 826 | const comp = zcu.comp; | 824 | const comp = zcu.comp; |
| 827 | const gpa = zcu.gpa; | 825 | const gpa = zcu.gpa; |
| ... | @@ -841,7 +839,6 @@ pub fn generate( | ... | @@ -841,7 +839,6 @@ pub fn generate( |
| 841 | .debug_output = debug_output, | 839 | .debug_output = debug_output, |
| 842 | .owner = .{ .nav_index = func.owner_nav }, | 840 | .owner = .{ .nav_index = func.owner_nav }, |
| 843 | .inline_func = func_index, | 841 | .inline_func = func_index, |
| 844 | .err_msg = null, | ||
| 845 | .arg_index = undefined, | 842 | .arg_index = undefined, |
| 846 | .args = undefined, // populated after `resolveCallingConventionValues` | 843 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 847 | .va_info = undefined, // populated after `resolveCallingConventionValues` | 844 | .va_info = undefined, // populated after `resolveCallingConventionValues` |
| ... | @@ -881,15 +878,7 @@ pub fn generate( | ... | @@ -881,15 +878,7 @@ pub fn generate( |
| 881 | const fn_info = zcu.typeToFunc(fn_type).?; | 878 | const fn_info = zcu.typeToFunc(fn_type).?; |
| 882 | const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*); | 879 | const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*); |
| 883 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { | 880 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { |
| 884 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 881 | error.CodegenFail => return error.CodegenFail, |
| 885 | error.OutOfRegisters => return Result{ | ||
| 886 | .fail = try ErrorMsg.create( | ||
| 887 | gpa, | ||
| 888 | src_loc, | ||
| 889 | "CodeGen ran out of registers. This is a bug in the Zig compiler.", | ||
| 890 | .{}, | ||
| 891 | ), | ||
| 892 | }, | ||
| 893 | else => |e| return e, | 882 | else => |e| return e, |
| 894 | }; | 883 | }; |
| 895 | defer call_info.deinit(&function); | 884 | defer call_info.deinit(&function); |
| ... | @@ -926,10 +915,8 @@ pub fn generate( | ... | @@ -926,10 +915,8 @@ pub fn generate( |
| 926 | }; | 915 | }; |
| 927 | 916 | ||
| 928 | function.gen() catch |err| switch (err) { | 917 | function.gen() catch |err| switch (err) { |
| 929 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 918 | error.CodegenFail => return error.CodegenFail, |
| 930 | error.OutOfRegisters => return Result{ | 919 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 931 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 932 | }, | ||
| 933 | else => |e| return e, | 920 | else => |e| return e, |
| 934 | }; | 921 | }; |
| 935 | 922 | ||
| ... | @@ -953,10 +940,7 @@ pub fn generate( | ... | @@ -953,10 +940,7 @@ pub fn generate( |
| 953 | .pic = mod.pic, | 940 | .pic = mod.pic, |
| 954 | }, | 941 | }, |
| 955 | .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { | 942 | .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { |
| 956 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 943 | error.CodegenFail => return error.CodegenFail, |
| 957 | error.OutOfRegisters => return Result{ | ||
| 958 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 959 | }, | ||
| 960 | else => |e| return e, | 944 | else => |e| return e, |
| 961 | }, | 945 | }, |
| 962 | .debug_output = debug_output, | 946 | .debug_output = debug_output, |
| ... | @@ -974,29 +958,11 @@ pub fn generate( | ... | @@ -974,29 +958,11 @@ pub fn generate( |
| 974 | }; | 958 | }; |
| 975 | defer emit.deinit(); | 959 | defer emit.deinit(); |
| 976 | emit.emitMir() catch |err| switch (err) { | 960 | emit.emitMir() catch |err| switch (err) { |
| 977 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | 961 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), |
| 978 | error.InvalidInstruction, error.CannotEncode => |e| { | ||
| 979 | const msg = switch (e) { | ||
| 980 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | ||
| 981 | error.CannotEncode => "CodeGen failed to encode the instruction.", | ||
| 982 | }; | ||
| 983 | return Result{ | ||
| 984 | .fail = try ErrorMsg.create( | ||
| 985 | gpa, | ||
| 986 | src_loc, | ||
| 987 | "{s} This is a bug in the Zig compiler.", | ||
| 988 | .{msg}, | ||
| 989 | ), | ||
| 990 | }; | ||
| 991 | }, | ||
| 992 | else => |e| return e, | ||
| 993 | }; | ||
| 994 | 962 | ||
| 995 | if (function.err_msg) |em| { | 963 | error.InvalidInstruction, error.CannotEncode => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), |
| 996 | return Result{ .fail = em }; | 964 | else => |e| return function.fail("emit MIR failed: {s}", .{@errorName(e)}), |
| 997 | } else { | 965 | }; |
| 998 | return Result.ok; | ||
| 999 | } | ||
| 1000 | } | 966 | } |
| 1001 | 967 | ||
| 1002 | pub fn generateLazy( | 968 | pub fn generateLazy( |
| ... | @@ -1004,9 +970,9 @@ pub fn generateLazy( | ... | @@ -1004,9 +970,9 @@ pub fn generateLazy( |
| 1004 | pt: Zcu.PerThread, | 970 | pt: Zcu.PerThread, |
| 1005 | src_loc: Zcu.LazySrcLoc, | 971 | src_loc: Zcu.LazySrcLoc, |
| 1006 | lazy_sym: link.File.LazySymbol, | 972 | lazy_sym: link.File.LazySymbol, |
| 1007 | code: *std.ArrayList(u8), | 973 | code: *std.ArrayListUnmanaged(u8), |
| 1008 | debug_output: link.File.DebugInfoOutput, | 974 | debug_output: link.File.DebugInfoOutput, |
| 1009 | ) CodeGenError!Result { | 975 | ) CodeGenError!void { |
| 1010 | const comp = bin_file.comp; | 976 | const comp = bin_file.comp; |
| 1011 | const gpa = comp.gpa; | 977 | const gpa = comp.gpa; |
| 1012 | // This function is for generating global code, so we use the root module. | 978 | // This function is for generating global code, so we use the root module. |
| ... | @@ -1022,7 +988,6 @@ pub fn generateLazy( | ... | @@ -1022,7 +988,6 @@ pub fn generateLazy( |
| 1022 | .debug_output = debug_output, | 988 | .debug_output = debug_output, |
| 1023 | .owner = .{ .lazy_sym = lazy_sym }, | 989 | .owner = .{ .lazy_sym = lazy_sym }, |
| 1024 | .inline_func = undefined, | 990 | .inline_func = undefined, |
| 1025 | .err_msg = null, | ||
| 1026 | .arg_index = undefined, | 991 | .arg_index = undefined, |
| 1027 | .args = undefined, | 992 | .args = undefined, |
| 1028 | .va_info = undefined, | 993 | .va_info = undefined, |
| ... | @@ -1038,10 +1003,8 @@ pub fn generateLazy( | ... | @@ -1038,10 +1003,8 @@ pub fn generateLazy( |
| 1038 | } | 1003 | } |
| 1039 | 1004 | ||
| 1040 | function.genLazy(lazy_sym) catch |err| switch (err) { | 1005 | function.genLazy(lazy_sym) catch |err| switch (err) { |
| 1041 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 1006 | error.CodegenFail => return error.CodegenFail, |
| 1042 | error.OutOfRegisters => return Result{ | 1007 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), |
| 1043 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 1044 | }, | ||
| 1045 | else => |e| return e, | 1008 | else => |e| return e, |
| 1046 | }; | 1009 | }; |
| 1047 | 1010 | ||
| ... | @@ -1065,10 +1028,7 @@ pub fn generateLazy( | ... | @@ -1065,10 +1028,7 @@ pub fn generateLazy( |
| 1065 | .pic = mod.pic, | 1028 | .pic = mod.pic, |
| 1066 | }, | 1029 | }, |
| 1067 | .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { | 1030 | .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { |
| 1068 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 1031 | error.CodegenFail => return error.CodegenFail, |
| 1069 | error.OutOfRegisters => return Result{ | ||
| 1070 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | ||
| 1071 | }, | ||
| 1072 | else => |e| return e, | 1032 | else => |e| return e, |
| 1073 | }, | 1033 | }, |
| 1074 | .debug_output = debug_output, | 1034 | .debug_output = debug_output, |
| ... | @@ -1078,29 +1038,11 @@ pub fn generateLazy( | ... | @@ -1078,29 +1038,11 @@ pub fn generateLazy( |
| 1078 | }; | 1038 | }; |
| 1079 | defer emit.deinit(); | 1039 | defer emit.deinit(); |
| 1080 | emit.emitMir() catch |err| switch (err) { | 1040 | emit.emitMir() catch |err| switch (err) { |
| 1081 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | 1041 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), |
| 1082 | error.InvalidInstruction, error.CannotEncode => |e| { | 1042 | error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}), |
| 1083 | const msg = switch (e) { | 1043 | error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}), |
| 1084 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | 1044 | else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}), |
| 1085 | error.CannotEncode => "CodeGen failed to encode the instruction.", | ||
| 1086 | }; | ||
| 1087 | return Result{ | ||
| 1088 | .fail = try ErrorMsg.create( | ||
| 1089 | gpa, | ||
| 1090 | src_loc, | ||
| 1091 | "{s} This is a bug in the Zig compiler.", | ||
| 1092 | .{msg}, | ||
| 1093 | ), | ||
| 1094 | }; | ||
| 1095 | }, | ||
| 1096 | else => |e| return e, | ||
| 1097 | }; | 1045 | }; |
| 1098 | |||
| 1099 | if (function.err_msg) |em| { | ||
| 1100 | return Result{ .fail = em }; | ||
| 1101 | } else { | ||
| 1102 | return Result.ok; | ||
| 1103 | } | ||
| 1104 | } | 1046 | } |
| 1105 | 1047 | ||
| 1106 | const FormatNavData = struct { | 1048 | const FormatNavData = struct { |
| ... | @@ -19276,10 +19218,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { | ... | @@ -19276,10 +19218,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { |
| 19276 | .load_got => |sym_index| .{ .lea_got = sym_index }, | 19218 | .load_got => |sym_index| .{ .lea_got = sym_index }, |
| 19277 | .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, | 19219 | .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, |
| 19278 | }, | 19220 | }, |
| 19279 | .fail => |msg| { | 19221 | .fail => |msg| return self.failMsg(msg), |
| 19280 | self.err_msg = msg; | ||
| 19281 | return error.CodegenFail; | ||
| 19282 | }, | ||
| 19283 | }; | 19222 | }; |
| 19284 | } | 19223 | } |
| 19285 | 19224 | ||
| ... | @@ -19592,11 +19531,23 @@ fn resolveCallingConventionValues( | ... | @@ -19592,11 +19531,23 @@ fn resolveCallingConventionValues( |
| 19592 | return result; | 19531 | return result; |
| 19593 | } | 19532 | } |
| 19594 | 19533 | ||
| 19595 | fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { | 19534 | fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 19596 | @branchHint(.cold); | 19535 | @branchHint(.cold); |
| 19597 | assert(self.err_msg == null); | 19536 | const zcu = self.pt.zcu; |
| 19598 | const gpa = self.gpa; | 19537 | switch (self.owner) { |
| 19599 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | 19538 | .nav_index => |i| return zcu.codegenFail(i, format, args), |
| 19539 | .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args), | ||
| 19540 | } | ||
| 19541 | return error.CodegenFail; | ||
| 19542 | } | ||
| 19543 | |||
| 19544 | fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { | ||
| 19545 | @branchHint(.cold); | ||
| 19546 | const zcu = self.pt.zcu; | ||
| 19547 | switch (self.owner) { | ||
| 19548 | .nav_index => |i| return zcu.codegenFailMsg(i, msg), | ||
| 19549 | .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg), | ||
| 19550 | } | ||
| 19600 | return error.CodegenFail; | 19551 | return error.CodegenFail; |
| 19601 | } | 19552 | } |
| 19602 | 19553 |
src/arch/x86_64/Emit.zig+8-7| ... | @@ -4,7 +4,7 @@ air: Air, | ... | @@ -4,7 +4,7 @@ air: Air, |
| 4 | lower: Lower, | 4 | lower: Lower, |
| 5 | atom_index: u32, | 5 | atom_index: u32, |
| 6 | debug_output: link.File.DebugInfoOutput, | 6 | debug_output: link.File.DebugInfoOutput, |
| 7 | code: *std.ArrayList(u8), | 7 | code: *std.ArrayListUnmanaged(u8), |
| 8 | 8 | ||
| 9 | prev_di_loc: Loc, | 9 | prev_di_loc: Loc, |
| 10 | /// Relative to the beginning of `code`. | 10 | /// Relative to the beginning of `code`. |
| ... | @@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{ | ... | @@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{ |
| 18 | } || link.File.UpdateDebugInfoError; | 18 | } || link.File.UpdateDebugInfoError; |
| 19 | 19 | ||
| 20 | pub fn emitMir(emit: *Emit) Error!void { | 20 | pub fn emitMir(emit: *Emit) Error!void { |
| 21 | const gpa = emit.lower.bin_file.comp.gpa; | ||
| 21 | for (0..emit.lower.mir.instructions.len) |mir_i| { | 22 | for (0..emit.lower.mir.instructions.len) |mir_i| { |
| 22 | const mir_index: Mir.Inst.Index = @intCast(mir_i); | 23 | const mir_index: Mir.Inst.Index = @intCast(mir_i); |
| 23 | try emit.code_offset_mapping.putNoClobber( | 24 | try emit.code_offset_mapping.putNoClobber( |
| ... | @@ -82,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -82,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 82 | } | 83 | } |
| 83 | continue; | 84 | continue; |
| 84 | } | 85 | } |
| 85 | try lowered_inst.encode(emit.code.writer(), .{}); | 86 | try lowered_inst.encode(emit.code.writer(gpa), .{}); |
| 86 | const end_offset: u32 = @intCast(emit.code.items.len); | 87 | const end_offset: u32 = @intCast(emit.code.items.len); |
| 87 | while (lowered_relocs.len > 0 and | 88 | while (lowered_relocs.len > 0 and |
| 88 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ | 89 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ |
| ... | @@ -100,7 +101,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -100,7 +101,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 100 | const zo = elf_file.zigObjectPtr().?; | 101 | const zo = elf_file.zigObjectPtr().?; |
| 101 | const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?; | 102 | const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?; |
| 102 | const r_type = @intFromEnum(std.elf.R_X86_64.PLT32); | 103 | const r_type = @intFromEnum(std.elf.R_X86_64.PLT32); |
| 103 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | 104 | try atom_ptr.addReloc(gpa, .{ |
| 104 | .r_offset = end_offset - 4, | 105 | .r_offset = end_offset - 4, |
| 105 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, | 106 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 106 | .r_addend = lowered_relocs[0].off - 4, | 107 | .r_addend = lowered_relocs[0].off - 4, |
| ... | @@ -147,7 +148,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -147,7 +148,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 147 | const zo = elf_file.zigObjectPtr().?; | 148 | const zo = elf_file.zigObjectPtr().?; |
| 148 | const atom = zo.symbol(emit.atom_index).atom(elf_file).?; | 149 | const atom = zo.symbol(emit.atom_index).atom(elf_file).?; |
| 149 | const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD); | 150 | const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD); |
| 150 | try atom.addReloc(elf_file.base.comp.gpa, .{ | 151 | try atom.addReloc(gpa, .{ |
| 151 | .r_offset = end_offset - 4, | 152 | .r_offset = end_offset - 4, |
| 152 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, | 153 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 153 | .r_addend = lowered_relocs[0].off - 4, | 154 | .r_addend = lowered_relocs[0].off - 4, |
| ... | @@ -158,7 +159,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -158,7 +159,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 158 | const zo = elf_file.zigObjectPtr().?; | 159 | const zo = elf_file.zigObjectPtr().?; |
| 159 | const atom = zo.symbol(emit.atom_index).atom(elf_file).?; | 160 | const atom = zo.symbol(emit.atom_index).atom(elf_file).?; |
| 160 | const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32); | 161 | const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32); |
| 161 | try atom.addReloc(elf_file.base.comp.gpa, .{ | 162 | try atom.addReloc(gpa, .{ |
| 162 | .r_offset = end_offset - 4, | 163 | .r_offset = end_offset - 4, |
| 163 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, | 164 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 164 | .r_addend = lowered_relocs[0].off, | 165 | .r_addend = lowered_relocs[0].off, |
| ... | @@ -173,7 +174,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -173,7 +174,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 173 | @intFromEnum(std.elf.R_X86_64.GOTPCREL) | 174 | @intFromEnum(std.elf.R_X86_64.GOTPCREL) |
| 174 | else | 175 | else |
| 175 | @intFromEnum(std.elf.R_X86_64.PC32); | 176 | @intFromEnum(std.elf.R_X86_64.PC32); |
| 176 | try atom.addReloc(elf_file.base.comp.gpa, .{ | 177 | try atom.addReloc(gpa, .{ |
| 177 | .r_offset = end_offset - 4, | 178 | .r_offset = end_offset - 4, |
| 178 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, | 179 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 179 | .r_addend = lowered_relocs[0].off - 4, | 180 | .r_addend = lowered_relocs[0].off - 4, |
| ... | @@ -183,7 +184,7 @@ pub fn emitMir(emit: *Emit) Error!void { | ... | @@ -183,7 +184,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 183 | @intFromEnum(std.elf.R_X86_64.TPOFF32) | 184 | @intFromEnum(std.elf.R_X86_64.TPOFF32) |
| 184 | else | 185 | else |
| 185 | @intFromEnum(std.elf.R_X86_64.@"32"); | 186 | @intFromEnum(std.elf.R_X86_64.@"32"); |
| 186 | try atom.addReloc(elf_file.base.comp.gpa, .{ | 187 | try atom.addReloc(gpa, .{ |
| 187 | .r_offset = end_offset - 4, | 188 | .r_offset = end_offset - 4, |
| 188 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, | 189 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 189 | .r_addend = lowered_relocs[0].off, | 190 | .r_addend = lowered_relocs[0].off, |
src/codegen.zig+199-181| ... | @@ -2,7 +2,6 @@ const std = @import("std"); | ... | @@ -2,7 +2,6 @@ const std = @import("std"); |
| 2 | const build_options = @import("build_options"); | 2 | const build_options = @import("build_options"); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const leb128 = std.leb; | ||
| 6 | const link = @import("link.zig"); | 5 | const link = @import("link.zig"); |
| 7 | const log = std.log.scoped(.codegen); | 6 | const log = std.log.scoped(.codegen); |
| 8 | const mem = std.mem; | 7 | const mem = std.mem; |
| ... | @@ -24,19 +23,13 @@ const Zir = std.zig.Zir; | ... | @@ -24,19 +23,13 @@ const Zir = std.zig.Zir; |
| 24 | const Alignment = InternPool.Alignment; | 23 | const Alignment = InternPool.Alignment; |
| 25 | const dev = @import("dev.zig"); | 24 | const dev = @import("dev.zig"); |
| 26 | 25 | ||
| 27 | pub const Result = union(enum) { | ||
| 28 | /// The `code` parameter passed to `generateSymbol` has the value ok. | ||
| 29 | ok, | ||
| 30 | |||
| 31 | /// There was a codegen error. | ||
| 32 | fail: *ErrorMsg, | ||
| 33 | }; | ||
| 34 | |||
| 35 | pub const CodeGenError = error{ | 26 | pub const CodeGenError = error{ |
| 36 | OutOfMemory, | 27 | OutOfMemory, |
| 28 | /// Compiler was asked to operate on a number larger than supported. | ||
| 37 | Overflow, | 29 | Overflow, |
| 30 | /// Indicates the error is already stored in Zcu `failed_codegen`. | ||
| 38 | CodegenFail, | 31 | CodegenFail, |
| 39 | } || link.File.UpdateDebugInfoError; | 32 | }; |
| 40 | 33 | ||
| 41 | fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature { | 34 | fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature { |
| 42 | comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_")); | 35 | comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_")); |
| ... | @@ -49,7 +42,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type { | ... | @@ -49,7 +42,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type { |
| 49 | .stage2_arm => @import("arch/arm/CodeGen.zig"), | 42 | .stage2_arm => @import("arch/arm/CodeGen.zig"), |
| 50 | .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"), | 43 | .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"), |
| 51 | .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"), | 44 | .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"), | 45 | .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"), |
| 54 | else => unreachable, | 46 | else => unreachable, |
| 55 | }; | 47 | }; |
| ... | @@ -62,9 +54,9 @@ pub fn generateFunction( | ... | @@ -62,9 +54,9 @@ pub fn generateFunction( |
| 62 | func_index: InternPool.Index, | 54 | func_index: InternPool.Index, |
| 63 | air: Air, | 55 | air: Air, |
| 64 | liveness: Liveness, | 56 | liveness: Liveness, |
| 65 | code: *std.ArrayList(u8), | 57 | code: *std.ArrayListUnmanaged(u8), |
| 66 | debug_output: link.File.DebugInfoOutput, | 58 | debug_output: link.File.DebugInfoOutput, |
| 67 | ) CodeGenError!Result { | 59 | ) CodeGenError!void { |
| 68 | const zcu = pt.zcu; | 60 | const zcu = pt.zcu; |
| 69 | const func = zcu.funcInfo(func_index); | 61 | const func = zcu.funcInfo(func_index); |
| 70 | const target = zcu.navFileScope(func.owner_nav).mod.resolved_target.result; | 62 | const target = zcu.navFileScope(func.owner_nav).mod.resolved_target.result; |
| ... | @@ -74,7 +66,6 @@ pub fn generateFunction( | ... | @@ -74,7 +66,6 @@ pub fn generateFunction( |
| 74 | .stage2_arm, | 66 | .stage2_arm, |
| 75 | .stage2_riscv64, | 67 | .stage2_riscv64, |
| 76 | .stage2_sparc64, | 68 | .stage2_sparc64, |
| 77 | .stage2_wasm, | ||
| 78 | .stage2_x86_64, | 69 | .stage2_x86_64, |
| 79 | => |backend| { | 70 | => |backend| { |
| 80 | dev.check(devFeatureForBackend(backend)); | 71 | dev.check(devFeatureForBackend(backend)); |
| ... | @@ -88,17 +79,15 @@ pub fn generateLazyFunction( | ... | @@ -88,17 +79,15 @@ pub fn generateLazyFunction( |
| 88 | pt: Zcu.PerThread, | 79 | pt: Zcu.PerThread, |
| 89 | src_loc: Zcu.LazySrcLoc, | 80 | src_loc: Zcu.LazySrcLoc, |
| 90 | lazy_sym: link.File.LazySymbol, | 81 | lazy_sym: link.File.LazySymbol, |
| 91 | code: *std.ArrayList(u8), | 82 | code: *std.ArrayListUnmanaged(u8), |
| 92 | debug_output: link.File.DebugInfoOutput, | 83 | debug_output: link.File.DebugInfoOutput, |
| 93 | ) CodeGenError!Result { | 84 | ) CodeGenError!void { |
| 94 | const zcu = pt.zcu; | 85 | const zcu = pt.zcu; |
| 95 | const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool); | 86 | const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool); |
| 96 | const target = zcu.fileByIndex(file).mod.resolved_target.result; | 87 | const target = zcu.fileByIndex(file).mod.resolved_target.result; |
| 97 | switch (target_util.zigBackend(target, false)) { | 88 | switch (target_util.zigBackend(target, false)) { |
| 98 | else => unreachable, | 89 | else => unreachable, |
| 99 | inline .stage2_x86_64, | 90 | inline .stage2_x86_64, .stage2_riscv64 => |backend| { |
| 100 | .stage2_riscv64, | ||
| 101 | => |backend| { | ||
| 102 | dev.check(devFeatureForBackend(backend)); | 91 | dev.check(devFeatureForBackend(backend)); |
| 103 | return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); | 92 | return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); |
| 104 | }, | 93 | }, |
| ... | @@ -120,20 +109,21 @@ pub fn generateLazySymbol( | ... | @@ -120,20 +109,21 @@ pub fn generateLazySymbol( |
| 120 | lazy_sym: link.File.LazySymbol, | 109 | lazy_sym: link.File.LazySymbol, |
| 121 | // TODO don't use an "out" parameter like this; put it in the result instead | 110 | // TODO don't use an "out" parameter like this; put it in the result instead |
| 122 | alignment: *Alignment, | 111 | alignment: *Alignment, |
| 123 | code: *std.ArrayList(u8), | 112 | code: *std.ArrayListUnmanaged(u8), |
| 124 | debug_output: link.File.DebugInfoOutput, | 113 | debug_output: link.File.DebugInfoOutput, |
| 125 | reloc_parent: link.File.RelocInfo.Parent, | 114 | reloc_parent: link.File.RelocInfo.Parent, |
| 126 | ) CodeGenError!Result { | 115 | ) CodeGenError!void { |
| 127 | _ = reloc_parent; | 116 | _ = reloc_parent; |
| 128 | 117 | ||
| 129 | const tracy = trace(@src()); | 118 | const tracy = trace(@src()); |
| 130 | defer tracy.end(); | 119 | defer tracy.end(); |
| 131 | 120 | ||
| 132 | const comp = bin_file.comp; | 121 | const comp = bin_file.comp; |
| 133 | const ip = &pt.zcu.intern_pool; | 122 | const gpa = comp.gpa; |
| 123 | const zcu = pt.zcu; | ||
| 124 | const ip = &zcu.intern_pool; | ||
| 134 | const target = comp.root_mod.resolved_target.result; | 125 | const target = comp.root_mod.resolved_target.result; |
| 135 | const endian = target.cpu.arch.endian(); | 126 | const endian = target.cpu.arch.endian(); |
| 136 | const gpa = comp.gpa; | ||
| 137 | 127 | ||
| 138 | log.debug("generateLazySymbol: kind = {s}, ty = {}", .{ | 128 | log.debug("generateLazySymbol: kind = {s}, ty = {}", .{ |
| 139 | @tagName(lazy_sym.kind), | 129 | @tagName(lazy_sym.kind), |
| ... | @@ -150,52 +140,56 @@ pub fn generateLazySymbol( | ... | @@ -150,52 +140,56 @@ pub fn generateLazySymbol( |
| 150 | const err_names = ip.global_error_set.getNamesFromMainThread(); | 140 | const err_names = ip.global_error_set.getNamesFromMainThread(); |
| 151 | var offset_index: u32 = @intCast(code.items.len); | 141 | var offset_index: u32 = @intCast(code.items.len); |
| 152 | var string_index: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0))); | 142 | var string_index: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0))); |
| 153 | try code.resize(offset_index + string_index); | 143 | try code.resize(gpa, offset_index + string_index); |
| 154 | mem.writeInt(u32, code.items[offset_index..][0..4], @intCast(err_names.len), endian); | 144 | mem.writeInt(u32, code.items[offset_index..][0..4], @intCast(err_names.len), endian); |
| 155 | if (err_names.len == 0) return .ok; | 145 | if (err_names.len == 0) return; |
| 156 | offset_index += 4; | 146 | offset_index += 4; |
| 157 | for (err_names) |err_name_nts| { | 147 | for (err_names) |err_name_nts| { |
| 158 | const err_name = err_name_nts.toSlice(ip); | 148 | const err_name = err_name_nts.toSlice(ip); |
| 159 | mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian); | 149 | mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian); |
| 160 | offset_index += 4; | 150 | offset_index += 4; |
| 161 | try code.ensureUnusedCapacity(err_name.len + 1); | 151 | try code.ensureUnusedCapacity(gpa, err_name.len + 1); |
| 162 | code.appendSliceAssumeCapacity(err_name); | 152 | code.appendSliceAssumeCapacity(err_name); |
| 163 | code.appendAssumeCapacity(0); | 153 | code.appendAssumeCapacity(0); |
| 164 | string_index += @intCast(err_name.len + 1); | 154 | string_index += @intCast(err_name.len + 1); |
| 165 | } | 155 | } |
| 166 | mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian); | 156 | mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian); |
| 167 | return .ok; | 157 | } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") { |
| 168 | } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(pt.zcu) == .@"enum") { | ||
| 169 | alignment.* = .@"1"; | 158 | alignment.* = .@"1"; |
| 170 | const enum_ty = Type.fromInterned(lazy_sym.ty); | 159 | const enum_ty = Type.fromInterned(lazy_sym.ty); |
| 171 | const tag_names = enum_ty.enumFields(pt.zcu); | 160 | const tag_names = enum_ty.enumFields(zcu); |
| 172 | for (0..tag_names.len) |tag_index| { | 161 | for (0..tag_names.len) |tag_index| { |
| 173 | const tag_name = tag_names.get(ip)[tag_index].toSlice(ip); | 162 | const tag_name = tag_names.get(ip)[tag_index].toSlice(ip); |
| 174 | try code.ensureUnusedCapacity(tag_name.len + 1); | 163 | try code.ensureUnusedCapacity(gpa, tag_name.len + 1); |
| 175 | code.appendSliceAssumeCapacity(tag_name); | 164 | code.appendSliceAssumeCapacity(tag_name); |
| 176 | code.appendAssumeCapacity(0); | 165 | code.appendAssumeCapacity(0); |
| 177 | } | 166 | } |
| 178 | return .ok; | 167 | } else { |
| 179 | } else return .{ .fail = try .create( | 168 | return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {}", .{ |
| 180 | gpa, | 169 | @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt), |
| 181 | src_loc, | 170 | }); |
| 182 | "TODO implement generateLazySymbol for {s} {}", | 171 | } |
| 183 | .{ @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt) }, | ||
| 184 | ) }; | ||
| 185 | } | 172 | } |
| 186 | 173 | ||
| 174 | pub const GenerateSymbolError = error{ | ||
| 175 | OutOfMemory, | ||
| 176 | /// Compiler was asked to operate on a number larger than supported. | ||
| 177 | Overflow, | ||
| 178 | }; | ||
| 179 | |||
| 187 | pub fn generateSymbol( | 180 | pub fn generateSymbol( |
| 188 | bin_file: *link.File, | 181 | bin_file: *link.File, |
| 189 | pt: Zcu.PerThread, | 182 | pt: Zcu.PerThread, |
| 190 | src_loc: Zcu.LazySrcLoc, | 183 | src_loc: Zcu.LazySrcLoc, |
| 191 | val: Value, | 184 | val: Value, |
| 192 | code: *std.ArrayList(u8), | 185 | code: *std.ArrayListUnmanaged(u8), |
| 193 | reloc_parent: link.File.RelocInfo.Parent, | 186 | reloc_parent: link.File.RelocInfo.Parent, |
| 194 | ) CodeGenError!Result { | 187 | ) GenerateSymbolError!void { |
| 195 | const tracy = trace(@src()); | 188 | const tracy = trace(@src()); |
| 196 | defer tracy.end(); | 189 | defer tracy.end(); |
| 197 | 190 | ||
| 198 | const zcu = pt.zcu; | 191 | const zcu = pt.zcu; |
| 192 | const gpa = zcu.gpa; | ||
| 199 | const ip = &zcu.intern_pool; | 193 | const ip = &zcu.intern_pool; |
| 200 | const ty = val.typeOf(zcu); | 194 | const ty = val.typeOf(zcu); |
| 201 | 195 | ||
| ... | @@ -206,8 +200,8 @@ pub fn generateSymbol( | ... | @@ -206,8 +200,8 @@ pub fn generateSymbol( |
| 206 | 200 | ||
| 207 | if (val.isUndefDeep(zcu)) { | 201 | if (val.isUndefDeep(zcu)) { |
| 208 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; | 202 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 209 | try code.appendNTimes(0xaa, abi_size); | 203 | try code.appendNTimes(gpa, 0xaa, abi_size); |
| 210 | return .ok; | 204 | return; |
| 211 | } | 205 | } |
| 212 | 206 | ||
| 213 | switch (ip.indexToKey(val.toIntern())) { | 207 | switch (ip.indexToKey(val.toIntern())) { |
| ... | @@ -231,14 +225,13 @@ pub fn generateSymbol( | ... | @@ -231,14 +225,13 @@ pub fn generateSymbol( |
| 231 | 225 | ||
| 232 | .undef => unreachable, // handled above | 226 | .undef => unreachable, // handled above |
| 233 | .simple_value => |simple_value| switch (simple_value) { | 227 | .simple_value => |simple_value| switch (simple_value) { |
| 234 | .undefined, | 228 | .undefined => unreachable, // non-runtime value |
| 235 | .void, | 229 | .void => unreachable, // non-runtime value |
| 236 | .null, | 230 | .null => unreachable, // non-runtime value |
| 237 | .empty_tuple, | 231 | .@"unreachable" => unreachable, // non-runtime value |
| 238 | .@"unreachable", | 232 | .generic_poison => unreachable, // non-runtime value |
| 239 | .generic_poison, | 233 | .empty_tuple => return, |
| 240 | => unreachable, // non-runtime values | 234 | .false, .true => try code.append(gpa, switch (simple_value) { |
| 241 | .false, .true => try code.append(switch (simple_value) { | ||
| 242 | .false => 0, | 235 | .false => 0, |
| 243 | .true => 1, | 236 | .true => 1, |
| 244 | else => unreachable, | 237 | else => unreachable, |
| ... | @@ -254,11 +247,11 @@ pub fn generateSymbol( | ... | @@ -254,11 +247,11 @@ pub fn generateSymbol( |
| 254 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; | 247 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 255 | var space: Value.BigIntSpace = undefined; | 248 | var space: Value.BigIntSpace = undefined; |
| 256 | const int_val = val.toBigInt(&space, zcu); | 249 | const int_val = val.toBigInt(&space, zcu); |
| 257 | int_val.writeTwosComplement(try code.addManyAsSlice(abi_size), endian); | 250 | int_val.writeTwosComplement(try code.addManyAsSlice(gpa, abi_size), endian); |
| 258 | }, | 251 | }, |
| 259 | .err => |err| { | 252 | .err => |err| { |
| 260 | const int = try pt.getErrorValue(err.name); | 253 | const int = try pt.getErrorValue(err.name); |
| 261 | try code.writer().writeInt(u16, @intCast(int), endian); | 254 | try code.writer(gpa).writeInt(u16, @intCast(int), endian); |
| 262 | }, | 255 | }, |
| 263 | .error_union => |error_union| { | 256 | .error_union => |error_union| { |
| 264 | const payload_ty = ty.errorUnionPayload(zcu); | 257 | const payload_ty = ty.errorUnionPayload(zcu); |
| ... | @@ -268,8 +261,8 @@ pub fn generateSymbol( | ... | @@ -268,8 +261,8 @@ pub fn generateSymbol( |
| 268 | }; | 261 | }; |
| 269 | 262 | ||
| 270 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 263 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 271 | try code.writer().writeInt(u16, err_val, endian); | 264 | try code.writer(gpa).writeInt(u16, err_val, endian); |
| 272 | return .ok; | 265 | return; |
| 273 | } | 266 | } |
| 274 | 267 | ||
| 275 | const payload_align = payload_ty.abiAlignment(zcu); | 268 | const payload_align = payload_ty.abiAlignment(zcu); |
| ... | @@ -278,72 +271,57 @@ pub fn generateSymbol( | ... | @@ -278,72 +271,57 @@ pub fn generateSymbol( |
| 278 | 271 | ||
| 279 | // error value first when its type is larger than the error union's payload | 272 | // error value first when its type is larger than the error union's payload |
| 280 | if (error_align.order(payload_align) == .gt) { | 273 | if (error_align.order(payload_align) == .gt) { |
| 281 | try code.writer().writeInt(u16, err_val, endian); | 274 | try code.writer(gpa).writeInt(u16, err_val, endian); |
| 282 | } | 275 | } |
| 283 | 276 | ||
| 284 | // emit payload part of the error union | 277 | // emit payload part of the error union |
| 285 | { | 278 | { |
| 286 | const begin = code.items.len; | 279 | const begin = code.items.len; |
| 287 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) { | 280 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) { |
| 288 | .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }), | 281 | .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }), |
| 289 | .payload => |payload| payload, | 282 | .payload => |payload| payload, |
| 290 | }), code, reloc_parent)) { | 283 | }), code, reloc_parent); |
| 291 | .ok => {}, | ||
| 292 | .fail => |em| return .{ .fail = em }, | ||
| 293 | } | ||
| 294 | const unpadded_end = code.items.len - begin; | 284 | const unpadded_end = code.items.len - begin; |
| 295 | const padded_end = abi_align.forward(unpadded_end); | 285 | const padded_end = abi_align.forward(unpadded_end); |
| 296 | const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; | 286 | const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; |
| 297 | 287 | ||
| 298 | if (padding > 0) { | 288 | if (padding > 0) { |
| 299 | try code.appendNTimes(0, padding); | 289 | try code.appendNTimes(gpa, 0, padding); |
| 300 | } | 290 | } |
| 301 | } | 291 | } |
| 302 | 292 | ||
| 303 | // Payload size is larger than error set, so emit our error set last | 293 | // Payload size is larger than error set, so emit our error set last |
| 304 | if (error_align.compare(.lte, payload_align)) { | 294 | if (error_align.compare(.lte, payload_align)) { |
| 305 | const begin = code.items.len; | 295 | const begin = code.items.len; |
| 306 | try code.writer().writeInt(u16, err_val, endian); | 296 | try code.writer(gpa).writeInt(u16, err_val, endian); |
| 307 | const unpadded_end = code.items.len - begin; | 297 | const unpadded_end = code.items.len - begin; |
| 308 | const padded_end = abi_align.forward(unpadded_end); | 298 | const padded_end = abi_align.forward(unpadded_end); |
| 309 | const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; | 299 | const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; |
| 310 | 300 | ||
| 311 | if (padding > 0) { | 301 | if (padding > 0) { |
| 312 | try code.appendNTimes(0, padding); | 302 | try code.appendNTimes(gpa, 0, padding); |
| 313 | } | 303 | } |
| 314 | } | 304 | } |
| 315 | }, | 305 | }, |
| 316 | .enum_tag => |enum_tag| { | 306 | .enum_tag => |enum_tag| { |
| 317 | const int_tag_ty = ty.intTagType(zcu); | 307 | const int_tag_ty = ty.intTagType(zcu); |
| 318 | switch (try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent)) { | 308 | try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent); |
| 319 | .ok => {}, | ||
| 320 | .fail => |em| return .{ .fail = em }, | ||
| 321 | } | ||
| 322 | }, | 309 | }, |
| 323 | .float => |float| switch (float.storage) { | 310 | .float => |float| switch (float.storage) { |
| 324 | .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(2)), | 311 | .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)), |
| 325 | .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(4)), | 312 | .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)), |
| 326 | .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(8)), | 313 | .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)), |
| 327 | .f80 => |f80_val| { | 314 | .f80 => |f80_val| { |
| 328 | writeFloat(f80, f80_val, target, endian, try code.addManyAsArray(10)); | 315 | writeFloat(f80, f80_val, target, endian, try code.addManyAsArray(gpa, 10)); |
| 329 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; | 316 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 330 | try code.appendNTimes(0, abi_size - 10); | 317 | try code.appendNTimes(gpa, 0, abi_size - 10); |
| 331 | }, | 318 | }, |
| 332 | .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(16)), | 319 | .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)), |
| 333 | }, | ||
| 334 | .ptr => switch (try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0)) { | ||
| 335 | .ok => {}, | ||
| 336 | .fail => |em| return .{ .fail = em }, | ||
| 337 | }, | 320 | }, |
| 321 | .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0), | ||
| 338 | .slice => |slice| { | 322 | .slice => |slice| { |
| 339 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent)) { | 323 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent); |
| 340 | .ok => {}, | 324 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent); |
| 341 | .fail => |em| return .{ .fail = em }, | ||
| 342 | } | ||
| 343 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent)) { | ||
| 344 | .ok => {}, | ||
| 345 | .fail => |em| return .{ .fail = em }, | ||
| 346 | } | ||
| 347 | }, | 325 | }, |
| 348 | .opt => { | 326 | .opt => { |
| 349 | const payload_type = ty.optionalChild(zcu); | 327 | const payload_type = ty.optionalChild(zcu); |
| ... | @@ -352,12 +330,9 @@ pub fn generateSymbol( | ... | @@ -352,12 +330,9 @@ pub fn generateSymbol( |
| 352 | 330 | ||
| 353 | if (ty.optionalReprIsPayload(zcu)) { | 331 | if (ty.optionalReprIsPayload(zcu)) { |
| 354 | if (payload_val) |value| { | 332 | if (payload_val) |value| { |
| 355 | switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) { | 333 | try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent); |
| 356 | .ok => {}, | ||
| 357 | .fail => |em| return Result{ .fail = em }, | ||
| 358 | } | ||
| 359 | } else { | 334 | } else { |
| 360 | try code.appendNTimes(0, abi_size); | 335 | try code.appendNTimes(gpa, 0, abi_size); |
| 361 | } | 336 | } |
| 362 | } else { | 337 | } else { |
| 363 | const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1; | 338 | const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1; |
| ... | @@ -365,39 +340,33 @@ pub fn generateSymbol( | ... | @@ -365,39 +340,33 @@ pub fn generateSymbol( |
| 365 | const value = payload_val orelse Value.fromInterned(try pt.intern(.{ | 340 | const value = payload_val orelse Value.fromInterned(try pt.intern(.{ |
| 366 | .undef = payload_type.toIntern(), | 341 | .undef = payload_type.toIntern(), |
| 367 | })); | 342 | })); |
| 368 | switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) { | 343 | try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent); |
| 369 | .ok => {}, | ||
| 370 | .fail => |em| return Result{ .fail = em }, | ||
| 371 | } | ||
| 372 | } | 344 | } |
| 373 | try code.writer().writeByte(@intFromBool(payload_val != null)); | 345 | try code.writer(gpa).writeByte(@intFromBool(payload_val != null)); |
| 374 | try code.appendNTimes(0, padding); | 346 | try code.appendNTimes(gpa, 0, padding); |
| 375 | } | 347 | } |
| 376 | }, | 348 | }, |
| 377 | .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { | 349 | .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { |
| 378 | .array_type => |array_type| switch (aggregate.storage) { | 350 | .array_type => |array_type| switch (aggregate.storage) { |
| 379 | .bytes => |bytes| try code.appendSlice(bytes.toSlice(array_type.lenIncludingSentinel(), ip)), | 351 | .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(array_type.lenIncludingSentinel(), ip)), |
| 380 | .elems, .repeated_elem => { | 352 | .elems, .repeated_elem => { |
| 381 | var index: u64 = 0; | 353 | var index: u64 = 0; |
| 382 | while (index < array_type.lenIncludingSentinel()) : (index += 1) { | 354 | while (index < array_type.lenIncludingSentinel()) : (index += 1) { |
| 383 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) { | 355 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) { |
| 384 | .bytes => unreachable, | 356 | .bytes => unreachable, |
| 385 | .elems => |elems| elems[@intCast(index)], | 357 | .elems => |elems| elems[@intCast(index)], |
| 386 | .repeated_elem => |elem| if (index < array_type.len) | 358 | .repeated_elem => |elem| if (index < array_type.len) |
| 387 | elem | 359 | elem |
| 388 | else | 360 | else |
| 389 | array_type.sentinel, | 361 | array_type.sentinel, |
| 390 | }), code, reloc_parent)) { | 362 | }), code, reloc_parent); |
| 391 | .ok => {}, | ||
| 392 | .fail => |em| return .{ .fail = em }, | ||
| 393 | } | ||
| 394 | } | 363 | } |
| 395 | }, | 364 | }, |
| 396 | }, | 365 | }, |
| 397 | .vector_type => |vector_type| { | 366 | .vector_type => |vector_type| { |
| 398 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; | 367 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 399 | if (vector_type.child == .bool_type) { | 368 | if (vector_type.child == .bool_type) { |
| 400 | const bytes = try code.addManyAsSlice(abi_size); | 369 | const bytes = try code.addManyAsSlice(gpa, abi_size); |
| 401 | @memset(bytes, 0xaa); | 370 | @memset(bytes, 0xaa); |
| 402 | var index: usize = 0; | 371 | var index: usize = 0; |
| 403 | const len = math.cast(usize, vector_type.len) orelse return error.Overflow; | 372 | const len = math.cast(usize, vector_type.len) orelse return error.Overflow; |
| ... | @@ -436,20 +405,17 @@ pub fn generateSymbol( | ... | @@ -436,20 +405,17 @@ pub fn generateSymbol( |
| 436 | } | 405 | } |
| 437 | } else { | 406 | } else { |
| 438 | switch (aggregate.storage) { | 407 | switch (aggregate.storage) { |
| 439 | .bytes => |bytes| try code.appendSlice(bytes.toSlice(vector_type.len, ip)), | 408 | .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(vector_type.len, ip)), |
| 440 | .elems, .repeated_elem => { | 409 | .elems, .repeated_elem => { |
| 441 | var index: u64 = 0; | 410 | var index: u64 = 0; |
| 442 | while (index < vector_type.len) : (index += 1) { | 411 | while (index < vector_type.len) : (index += 1) { |
| 443 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) { | 412 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) { |
| 444 | .bytes => unreachable, | 413 | .bytes => unreachable, |
| 445 | .elems => |elems| elems[ | 414 | .elems => |elems| elems[ |
| 446 | math.cast(usize, index) orelse return error.Overflow | 415 | math.cast(usize, index) orelse return error.Overflow |
| 447 | ], | 416 | ], |
| 448 | .repeated_elem => |elem| elem, | 417 | .repeated_elem => |elem| elem, |
| 449 | }), code, reloc_parent)) { | 418 | }), code, reloc_parent); |
| 450 | .ok => {}, | ||
| 451 | .fail => |em| return .{ .fail = em }, | ||
| 452 | } | ||
| 453 | } | 419 | } |
| 454 | }, | 420 | }, |
| 455 | } | 421 | } |
| ... | @@ -457,7 +423,7 @@ pub fn generateSymbol( | ... | @@ -457,7 +423,7 @@ pub fn generateSymbol( |
| 457 | const padding = abi_size - | 423 | const padding = abi_size - |
| 458 | (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse | 424 | (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse |
| 459 | return error.Overflow); | 425 | return error.Overflow); |
| 460 | if (padding > 0) try code.appendNTimes(0, padding); | 426 | if (padding > 0) try code.appendNTimes(gpa, 0, padding); |
| 461 | } | 427 | } |
| 462 | }, | 428 | }, |
| 463 | .tuple_type => |tuple| { | 429 | .tuple_type => |tuple| { |
| ... | @@ -479,10 +445,7 @@ pub fn generateSymbol( | ... | @@ -479,10 +445,7 @@ pub fn generateSymbol( |
| 479 | .repeated_elem => |elem| elem, | 445 | .repeated_elem => |elem| elem, |
| 480 | }; | 446 | }; |
| 481 | 447 | ||
| 482 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent)) { | 448 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent); |
| 483 | .ok => {}, | ||
| 484 | .fail => |em| return Result{ .fail = em }, | ||
| 485 | } | ||
| 486 | const unpadded_field_end = code.items.len - struct_begin; | 449 | const unpadded_field_end = code.items.len - struct_begin; |
| 487 | 450 | ||
| 488 | // Pad struct members if required | 451 | // Pad struct members if required |
| ... | @@ -491,7 +454,7 @@ pub fn generateSymbol( | ... | @@ -491,7 +454,7 @@ pub fn generateSymbol( |
| 491 | return error.Overflow; | 454 | return error.Overflow; |
| 492 | 455 | ||
| 493 | if (padding > 0) { | 456 | if (padding > 0) { |
| 494 | try code.appendNTimes(0, padding); | 457 | try code.appendNTimes(gpa, 0, padding); |
| 495 | } | 458 | } |
| 496 | } | 459 | } |
| 497 | }, | 460 | }, |
| ... | @@ -501,7 +464,7 @@ pub fn generateSymbol( | ... | @@ -501,7 +464,7 @@ pub fn generateSymbol( |
| 501 | .@"packed" => { | 464 | .@"packed" => { |
| 502 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; | 465 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 503 | const current_pos = code.items.len; | 466 | const current_pos = code.items.len; |
| 504 | try code.appendNTimes(0, abi_size); | 467 | try code.appendNTimes(gpa, 0, abi_size); |
| 505 | var bits: u16 = 0; | 468 | var bits: u16 = 0; |
| 506 | 469 | ||
| 507 | for (struct_type.field_types.get(ip), 0..) |field_ty, index| { | 470 | for (struct_type.field_types.get(ip), 0..) |field_ty, index| { |
| ... | @@ -519,12 +482,10 @@ pub fn generateSymbol( | ... | @@ -519,12 +482,10 @@ pub fn generateSymbol( |
| 519 | if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) { | 482 | if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) { |
| 520 | const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse | 483 | const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse |
| 521 | return error.Overflow; | 484 | return error.Overflow; |
| 522 | var tmp_list = try std.ArrayList(u8).initCapacity(code.allocator, field_size); | 485 | var tmp_list = try std.ArrayListUnmanaged(u8).initCapacity(gpa, field_size); |
| 523 | defer tmp_list.deinit(); | 486 | defer tmp_list.deinit(gpa); |
| 524 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent)) { | 487 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent); |
| 525 | .ok => @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items), | 488 | @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items); |
| 526 | .fail => |em| return Result{ .fail = em }, | ||
| 527 | } | ||
| 528 | } else { | 489 | } else { |
| 529 | Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable; | 490 | Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable; |
| 530 | } | 491 | } |
| ... | @@ -554,12 +515,9 @@ pub fn generateSymbol( | ... | @@ -554,12 +515,9 @@ pub fn generateSymbol( |
| 554 | usize, | 515 | usize, |
| 555 | offsets[field_index] - (code.items.len - struct_begin), | 516 | offsets[field_index] - (code.items.len - struct_begin), |
| 556 | ) orelse return error.Overflow; | 517 | ) orelse return error.Overflow; |
| 557 | if (padding > 0) try code.appendNTimes(0, padding); | 518 | if (padding > 0) try code.appendNTimes(gpa, 0, padding); |
| 558 | 519 | ||
| 559 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent)) { | 520 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent); |
| 560 | .ok => {}, | ||
| 561 | .fail => |em| return Result{ .fail = em }, | ||
| 562 | } | ||
| 563 | } | 521 | } |
| 564 | 522 | ||
| 565 | const size = struct_type.sizeUnordered(ip); | 523 | const size = struct_type.sizeUnordered(ip); |
| ... | @@ -570,7 +528,7 @@ pub fn generateSymbol( | ... | @@ -570,7 +528,7 @@ pub fn generateSymbol( |
| 570 | std.mem.alignForward(u64, size, @max(alignment, 1)) - | 528 | std.mem.alignForward(u64, size, @max(alignment, 1)) - |
| 571 | (code.items.len - struct_begin), | 529 | (code.items.len - struct_begin), |
| 572 | ) orelse return error.Overflow; | 530 | ) orelse return error.Overflow; |
| 573 | if (padding > 0) try code.appendNTimes(0, padding); | 531 | if (padding > 0) try code.appendNTimes(gpa, 0, padding); |
| 574 | }, | 532 | }, |
| 575 | } | 533 | } |
| 576 | }, | 534 | }, |
| ... | @@ -585,10 +543,7 @@ pub fn generateSymbol( | ... | @@ -585,10 +543,7 @@ pub fn generateSymbol( |
| 585 | 543 | ||
| 586 | // Check if we should store the tag first. | 544 | // Check if we should store the tag first. |
| 587 | if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) { | 545 | if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) { |
| 588 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent)) { | 546 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent); |
| 589 | .ok => {}, | ||
| 590 | .fail => |em| return Result{ .fail = em }, | ||
| 591 | } | ||
| 592 | } | 547 | } |
| 593 | 548 | ||
| 594 | const union_obj = zcu.typeToUnion(ty).?; | 549 | const union_obj = zcu.typeToUnion(ty).?; |
| ... | @@ -596,39 +551,29 @@ pub fn generateSymbol( | ... | @@ -596,39 +551,29 @@ pub fn generateSymbol( |
| 596 | const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?; | 551 | const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?; |
| 597 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); | 552 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 598 | if (!field_ty.hasRuntimeBits(zcu)) { | 553 | if (!field_ty.hasRuntimeBits(zcu)) { |
| 599 | try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); | 554 | try code.appendNTimes(gpa, 0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); |
| 600 | } else { | 555 | } else { |
| 601 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) { | 556 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent); |
| 602 | .ok => {}, | ||
| 603 | .fail => |em| return Result{ .fail = em }, | ||
| 604 | } | ||
| 605 | 557 | ||
| 606 | const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow; | 558 | const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow; |
| 607 | if (padding > 0) { | 559 | if (padding > 0) { |
| 608 | try code.appendNTimes(0, padding); | 560 | try code.appendNTimes(gpa, 0, padding); |
| 609 | } | 561 | } |
| 610 | } | 562 | } |
| 611 | } else { | 563 | } else { |
| 612 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) { | 564 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent); |
| 613 | .ok => {}, | ||
| 614 | .fail => |em| return Result{ .fail = em }, | ||
| 615 | } | ||
| 616 | } | 565 | } |
| 617 | 566 | ||
| 618 | if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) { | 567 | if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) { |
| 619 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent)) { | 568 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent); |
| 620 | .ok => {}, | ||
| 621 | .fail => |em| return Result{ .fail = em }, | ||
| 622 | } | ||
| 623 | 569 | ||
| 624 | if (layout.padding > 0) { | 570 | if (layout.padding > 0) { |
| 625 | try code.appendNTimes(0, layout.padding); | 571 | try code.appendNTimes(gpa, 0, layout.padding); |
| 626 | } | 572 | } |
| 627 | } | 573 | } |
| 628 | }, | 574 | }, |
| 629 | .memoized_call => unreachable, | 575 | .memoized_call => unreachable, |
| 630 | } | 576 | } |
| 631 | return .ok; | ||
| 632 | } | 577 | } |
| 633 | 578 | ||
| 634 | fn lowerPtr( | 579 | fn lowerPtr( |
| ... | @@ -636,15 +581,15 @@ fn lowerPtr( | ... | @@ -636,15 +581,15 @@ fn lowerPtr( |
| 636 | pt: Zcu.PerThread, | 581 | pt: Zcu.PerThread, |
| 637 | src_loc: Zcu.LazySrcLoc, | 582 | src_loc: Zcu.LazySrcLoc, |
| 638 | ptr_val: InternPool.Index, | 583 | ptr_val: InternPool.Index, |
| 639 | code: *std.ArrayList(u8), | 584 | code: *std.ArrayListUnmanaged(u8), |
| 640 | reloc_parent: link.File.RelocInfo.Parent, | 585 | reloc_parent: link.File.RelocInfo.Parent, |
| 641 | prev_offset: u64, | 586 | prev_offset: u64, |
| 642 | ) CodeGenError!Result { | 587 | ) GenerateSymbolError!void { |
| 643 | const zcu = pt.zcu; | 588 | const zcu = pt.zcu; |
| 644 | const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; | 589 | const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; |
| 645 | const offset: u64 = prev_offset + ptr.byte_offset; | 590 | const offset: u64 = prev_offset + ptr.byte_offset; |
| 646 | return switch (ptr.base_addr) { | 591 | return switch (ptr.base_addr) { |
| 647 | .nav => |nav| try lowerNavRef(bin_file, pt, src_loc, nav, code, reloc_parent, offset), | 592 | .nav => |nav| try lowerNavRef(bin_file, pt, nav, code, reloc_parent, offset), |
| 648 | .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset), | 593 | .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset), |
| 649 | .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent), | 594 | .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent), |
| 650 | .eu_payload => |eu_ptr| try lowerPtr( | 595 | .eu_payload => |eu_ptr| try lowerPtr( |
| ... | @@ -689,29 +634,62 @@ fn lowerUavRef( | ... | @@ -689,29 +634,62 @@ fn lowerUavRef( |
| 689 | pt: Zcu.PerThread, | 634 | pt: Zcu.PerThread, |
| 690 | src_loc: Zcu.LazySrcLoc, | 635 | src_loc: Zcu.LazySrcLoc, |
| 691 | uav: InternPool.Key.Ptr.BaseAddr.Uav, | 636 | uav: InternPool.Key.Ptr.BaseAddr.Uav, |
| 692 | code: *std.ArrayList(u8), | 637 | code: *std.ArrayListUnmanaged(u8), |
| 693 | reloc_parent: link.File.RelocInfo.Parent, | 638 | reloc_parent: link.File.RelocInfo.Parent, |
| 694 | offset: u64, | 639 | offset: u64, |
| 695 | ) CodeGenError!Result { | 640 | ) GenerateSymbolError!void { |
| 696 | const zcu = pt.zcu; | 641 | const zcu = pt.zcu; |
| 642 | const gpa = zcu.gpa; | ||
| 697 | const ip = &zcu.intern_pool; | 643 | const ip = &zcu.intern_pool; |
| 698 | const target = lf.comp.root_mod.resolved_target.result; | 644 | const comp = lf.comp; |
| 699 | 645 | const target = &comp.root_mod.resolved_target.result; | |
| 700 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); | 646 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); |
| 647 | const is_obj = comp.config.output_mode == .Obj; | ||
| 701 | const uav_val = uav.val; | 648 | const uav_val = uav.val; |
| 702 | const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); | 649 | const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); |
| 703 | log.debug("lowerUavRef: ty = {}", .{uav_ty.fmt(pt)}); | ||
| 704 | const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; | 650 | const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; |
| 651 | |||
| 652 | log.debug("lowerUavRef: ty = {}", .{uav_ty.fmt(pt)}); | ||
| 653 | try code.ensureUnusedCapacity(gpa, ptr_width_bytes); | ||
| 654 | |||
| 705 | if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) { | 655 | if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) { |
| 706 | try code.appendNTimes(0xaa, ptr_width_bytes); | 656 | code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes); |
| 707 | return Result.ok; | 657 | return; |
| 658 | } | ||
| 659 | |||
| 660 | switch (lf.tag) { | ||
| 661 | .c => unreachable, | ||
| 662 | .spirv => unreachable, | ||
| 663 | .nvptx => unreachable, | ||
| 664 | .wasm => { | ||
| 665 | dev.check(link.File.Tag.wasm.devFeature()); | ||
| 666 | const wasm = lf.cast(.wasm).?; | ||
| 667 | assert(reloc_parent == .none); | ||
| 668 | if (is_obj) { | ||
| 669 | try wasm.out_relocs.append(gpa, .{ | ||
| 670 | .offset = @intCast(code.items.len), | ||
| 671 | .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) }, | ||
| 672 | .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, | ||
| 673 | .addend = @intCast(offset), | ||
| 674 | }); | ||
| 675 | } else { | ||
| 676 | try wasm.uav_fixups.ensureUnusedCapacity(gpa, 1); | ||
| 677 | wasm.uav_fixups.appendAssumeCapacity(.{ | ||
| 678 | .uavs_exe_index = try wasm.refUavExe(uav.val, uav.orig_ty), | ||
| 679 | .offset = @intCast(code.items.len), | ||
| 680 | .addend = @intCast(offset), | ||
| 681 | }); | ||
| 682 | } | ||
| 683 | code.appendNTimesAssumeCapacity(0, ptr_width_bytes); | ||
| 684 | return; | ||
| 685 | }, | ||
| 686 | else => {}, | ||
| 708 | } | 687 | } |
| 709 | 688 | ||
| 710 | const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment; | 689 | const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment; |
| 711 | const res = try lf.lowerUav(pt, uav_val, uav_align, src_loc); | 690 | switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) { |
| 712 | switch (res) { | ||
| 713 | .mcv => {}, | 691 | .mcv => {}, |
| 714 | .fail => |em| return .{ .fail = em }, | 692 | .fail => |em| std.debug.panic("TODO rework lowerUav. internal error: {s}", .{em.msg}), |
| 715 | } | 693 | } |
| 716 | 694 | ||
| 717 | const vaddr = try lf.getUavVAddr(uav_val, .{ | 695 | const vaddr = try lf.getUavVAddr(uav_val, .{ |
| ... | @@ -721,51 +699,91 @@ fn lowerUavRef( | ... | @@ -721,51 +699,91 @@ fn lowerUavRef( |
| 721 | }); | 699 | }); |
| 722 | const endian = target.cpu.arch.endian(); | 700 | const endian = target.cpu.arch.endian(); |
| 723 | switch (ptr_width_bytes) { | 701 | switch (ptr_width_bytes) { |
| 724 | 2 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian), | 702 | 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian), |
| 725 | 4 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian), | 703 | 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian), |
| 726 | 8 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian), | 704 | 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian), |
| 727 | else => unreachable, | 705 | else => unreachable, |
| 728 | } | 706 | } |
| 729 | |||
| 730 | return Result.ok; | ||
| 731 | } | 707 | } |
| 732 | 708 | ||
| 733 | fn lowerNavRef( | 709 | fn lowerNavRef( |
| 734 | lf: *link.File, | 710 | lf: *link.File, |
| 735 | pt: Zcu.PerThread, | 711 | pt: Zcu.PerThread, |
| 736 | src_loc: Zcu.LazySrcLoc, | ||
| 737 | nav_index: InternPool.Nav.Index, | 712 | nav_index: InternPool.Nav.Index, |
| 738 | code: *std.ArrayList(u8), | 713 | code: *std.ArrayListUnmanaged(u8), |
| 739 | reloc_parent: link.File.RelocInfo.Parent, | 714 | reloc_parent: link.File.RelocInfo.Parent, |
| 740 | offset: u64, | 715 | offset: u64, |
| 741 | ) CodeGenError!Result { | 716 | ) GenerateSymbolError!void { |
| 742 | _ = src_loc; | ||
| 743 | const zcu = pt.zcu; | 717 | const zcu = pt.zcu; |
| 718 | const gpa = zcu.gpa; | ||
| 744 | const ip = &zcu.intern_pool; | 719 | const ip = &zcu.intern_pool; |
| 745 | const target = zcu.navFileScope(nav_index).mod.resolved_target.result; | 720 | const target = zcu.navFileScope(nav_index).mod.resolved_target.result; |
| 746 | 721 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); | |
| 747 | const ptr_width = target.ptrBitWidth(); | 722 | const is_obj = lf.comp.config.output_mode == .Obj; |
| 748 | const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip)); | 723 | const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip)); |
| 749 | const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; | 724 | const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; |
| 725 | |||
| 726 | try code.ensureUnusedCapacity(gpa, ptr_width_bytes); | ||
| 727 | |||
| 750 | if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) { | 728 | if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) { |
| 751 | try code.appendNTimes(0xaa, @divExact(ptr_width, 8)); | 729 | code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes); |
| 752 | return Result.ok; | 730 | return; |
| 753 | } | 731 | } |
| 754 | 732 | ||
| 755 | const vaddr = try lf.getNavVAddr(pt, nav_index, .{ | 733 | switch (lf.tag) { |
| 734 | .c => unreachable, | ||
| 735 | .spirv => unreachable, | ||
| 736 | .nvptx => unreachable, | ||
| 737 | .wasm => { | ||
| 738 | dev.check(link.File.Tag.wasm.devFeature()); | ||
| 739 | const wasm = lf.cast(.wasm).?; | ||
| 740 | assert(reloc_parent == .none); | ||
| 741 | if (is_fn_body) { | ||
| 742 | const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); | ||
| 743 | if (!gop.found_existing) gop.value_ptr.* = {}; | ||
| 744 | if (is_obj) { | ||
| 745 | @panic("TODO add out_reloc for this"); | ||
| 746 | } else { | ||
| 747 | try wasm.func_table_fixups.append(gpa, .{ | ||
| 748 | .table_index = @enumFromInt(gop.index), | ||
| 749 | .offset = @intCast(code.items.len), | ||
| 750 | }); | ||
| 751 | } | ||
| 752 | } else { | ||
| 753 | if (is_obj) { | ||
| 754 | try wasm.out_relocs.append(gpa, .{ | ||
| 755 | .offset = @intCast(code.items.len), | ||
| 756 | .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) }, | ||
| 757 | .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, | ||
| 758 | .addend = @intCast(offset), | ||
| 759 | }); | ||
| 760 | } else { | ||
| 761 | try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1); | ||
| 762 | wasm.nav_fixups.appendAssumeCapacity(.{ | ||
| 763 | .navs_exe_index = try wasm.refNavExe(nav_index), | ||
| 764 | .offset = @intCast(code.items.len), | ||
| 765 | .addend = @intCast(offset), | ||
| 766 | }); | ||
| 767 | } | ||
| 768 | } | ||
| 769 | code.appendNTimesAssumeCapacity(0, ptr_width_bytes); | ||
| 770 | return; | ||
| 771 | }, | ||
| 772 | else => {}, | ||
| 773 | } | ||
| 774 | |||
| 775 | const vaddr = lf.getNavVAddr(pt, nav_index, .{ | ||
| 756 | .parent = reloc_parent, | 776 | .parent = reloc_parent, |
| 757 | .offset = code.items.len, | 777 | .offset = code.items.len, |
| 758 | .addend = @intCast(offset), | 778 | .addend = @intCast(offset), |
| 759 | }); | 779 | }) catch @panic("TODO rework getNavVAddr"); |
| 760 | const endian = target.cpu.arch.endian(); | 780 | const endian = target.cpu.arch.endian(); |
| 761 | switch (ptr_width) { | 781 | switch (ptr_width_bytes) { |
| 762 | 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian), | 782 | 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian), |
| 763 | 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian), | 783 | 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian), |
| 764 | 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian), | 784 | 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian), |
| 765 | else => unreachable, | 785 | else => unreachable, |
| 766 | } | 786 | } |
| 767 | |||
| 768 | return Result.ok; | ||
| 769 | } | 787 | } |
| 770 | 788 | ||
| 771 | /// Helper struct to denote that the value is in memory but requires a linker relocation fixup: | 789 | /// Helper struct to denote that the value is in memory but requires a linker relocation fixup: |
src/codegen/c.zig+4-4| ... | @@ -3052,12 +3052,12 @@ pub fn genDeclValue( | ... | @@ -3052,12 +3052,12 @@ pub fn genDeclValue( |
| 3052 | try w.writeAll(";\n"); | 3052 | try w.writeAll(";\n"); |
| 3053 | } | 3053 | } |
| 3054 | 3054 | ||
| 3055 | pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const u32) !void { | 3055 | pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void { |
| 3056 | const zcu = dg.pt.zcu; | 3056 | const zcu = dg.pt.zcu; |
| 3057 | const ip = &zcu.intern_pool; | 3057 | const ip = &zcu.intern_pool; |
| 3058 | const fwd = dg.fwdDeclWriter(); | 3058 | const fwd = dg.fwdDeclWriter(); |
| 3059 | 3059 | ||
| 3060 | const main_name = zcu.all_exports.items[export_indices[0]].opts.name; | 3060 | const main_name = export_indices[0].ptr(zcu).opts.name; |
| 3061 | try fwd.writeAll("#define "); | 3061 | try fwd.writeAll("#define "); |
| 3062 | switch (exported) { | 3062 | switch (exported) { |
| 3063 | .nav => |nav| try dg.renderNavName(fwd, nav), | 3063 | .nav => |nav| try dg.renderNavName(fwd, nav), |
| ... | @@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const | ... | @@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3069 | 3069 | ||
| 3070 | const exported_val = exported.getValue(zcu); | 3070 | const exported_val = exported.getValue(zcu); |
| 3071 | if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| { | 3071 | if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| { |
| 3072 | const @"export" = &zcu.all_exports.items[export_index]; | 3072 | const @"export" = export_index.ptr(zcu); |
| 3073 | try fwd.writeAll("zig_extern "); | 3073 | try fwd.writeAll("zig_extern "); |
| 3074 | if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); | 3074 | if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); |
| 3075 | try dg.renderFunctionSignature( | 3075 | try dg.renderFunctionSignature( |
| ... | @@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const | ... | @@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3091 | else => true, | 3091 | else => true, |
| 3092 | }; | 3092 | }; |
| 3093 | for (export_indices) |export_index| { | 3093 | for (export_indices) |export_index| { |
| 3094 | const @"export" = &zcu.all_exports.items[export_index]; | 3094 | const @"export" = export_index.ptr(zcu); |
| 3095 | try fwd.writeAll("zig_extern "); | 3095 | try fwd.writeAll("zig_extern "); |
| 3096 | if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); | 3096 | if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); |
| 3097 | const extern_name = @"export".opts.name.toSlice(ip); | 3097 | const extern_name = @"export".opts.name.toSlice(ip); |
src/codegen/llvm.zig+22-32| ... | @@ -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 | }; |
| 1061 | 1061 | ||
| 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; | ||
| 1065 | 1066 | ||
| 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(); |
| 1224 | 1225 | ||
| 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(); |
| 1228 | 1230 | ||
| 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 | } |
| 1232 | 1235 | ||
| 1233 | if (options.asm_path == null and options.bin_path == null and | 1236 | 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; |
| 1235 | 1238 | ||
| 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(); |
| 1239 | 1243 | ||
| 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 | } |
| 1243 | 1248 | ||
| 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 | } |
| 1248 | 1252 | ||
| 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 { |
| 1263 | 1267 | ||
| 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); |
| 1277 | 1280 | 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 | } |
| 1284 | 1282 | ||
| 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 | } |
| 1396 | 1391 | ||
| ... | @@ -1815,7 +1810,7 @@ pub const Object = struct { | ... | @@ -1815,7 +1810,7 @@ pub const Object = struct { |
| 1815 | self: *Object, | 1810 | self: *Object, |
| 1816 | pt: Zcu.PerThread, | 1811 | pt: Zcu.PerThread, |
| 1817 | exported: Zcu.Exported, | 1812 | exported: Zcu.Exported, |
| 1818 | export_indices: []const u32, | 1813 | export_indices: []const Zcu.Export.Index, |
| 1819 | ) link.File.UpdateExportsError!void { | 1814 | ) link.File.UpdateExportsError!void { |
| 1820 | assert(std.meta.eql(pt, self.pt)); | 1815 | assert(std.meta.eql(pt, self.pt)); |
| 1821 | const zcu = pt.zcu; | 1816 | const zcu = pt.zcu; |
| ... | @@ -1843,11 +1838,11 @@ pub const Object = struct { | ... | @@ -1843,11 +1838,11 @@ pub const Object = struct { |
| 1843 | o: *Object, | 1838 | o: *Object, |
| 1844 | zcu: *Zcu, | 1839 | zcu: *Zcu, |
| 1845 | exported_value: InternPool.Index, | 1840 | exported_value: InternPool.Index, |
| 1846 | export_indices: []const u32, | 1841 | export_indices: []const Zcu.Export.Index, |
| 1847 | ) link.File.UpdateExportsError!void { | 1842 | ) link.File.UpdateExportsError!void { |
| 1848 | const gpa = zcu.gpa; | 1843 | const gpa = zcu.gpa; |
| 1849 | const ip = &zcu.intern_pool; | 1844 | const ip = &zcu.intern_pool; |
| 1850 | const main_exp_name = try o.builder.strtabString(zcu.all_exports.items[export_indices[0]].opts.name.toSlice(ip)); | 1845 | const main_exp_name = try o.builder.strtabString(export_indices[0].ptr(zcu).opts.name.toSlice(ip)); |
| 1851 | const global_index = i: { | 1846 | const global_index = i: { |
| 1852 | const gop = try o.uav_map.getOrPut(gpa, exported_value); | 1847 | const gop = try o.uav_map.getOrPut(gpa, exported_value); |
| 1853 | if (gop.found_existing) { | 1848 | if (gop.found_existing) { |
| ... | @@ -1878,11 +1873,11 @@ pub const Object = struct { | ... | @@ -1878,11 +1873,11 @@ pub const Object = struct { |
| 1878 | o: *Object, | 1873 | o: *Object, |
| 1879 | zcu: *Zcu, | 1874 | zcu: *Zcu, |
| 1880 | global_index: Builder.Global.Index, | 1875 | global_index: Builder.Global.Index, |
| 1881 | export_indices: []const u32, | 1876 | export_indices: []const Zcu.Export.Index, |
| 1882 | ) link.File.UpdateExportsError!void { | 1877 | ) link.File.UpdateExportsError!void { |
| 1883 | const comp = zcu.comp; | 1878 | const comp = zcu.comp; |
| 1884 | const ip = &zcu.intern_pool; | 1879 | const ip = &zcu.intern_pool; |
| 1885 | const first_export = zcu.all_exports.items[export_indices[0]]; | 1880 | const first_export = export_indices[0].ptr(zcu); |
| 1886 | 1881 | ||
| 1887 | // We will rename this global to have a name matching `first_export`. | 1882 | // We will rename this global to have a name matching `first_export`. |
| 1888 | // Successive exports become aliases. | 1883 | // Successive exports become aliases. |
| ... | @@ -1939,7 +1934,7 @@ pub const Object = struct { | ... | @@ -1939,7 +1934,7 @@ pub const Object = struct { |
| 1939 | // Until then we iterate over existing aliases and make them point | 1934 | // Until then we iterate over existing aliases and make them point |
| 1940 | // to the correct decl, or otherwise add a new alias. Old aliases are leaked. | 1935 | // to the correct decl, or otherwise add a new alias. Old aliases are leaked. |
| 1941 | for (export_indices[1..]) |export_idx| { | 1936 | for (export_indices[1..]) |export_idx| { |
| 1942 | const exp = zcu.all_exports.items[export_idx]; | 1937 | const exp = export_idx.ptr(zcu); |
| 1943 | const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip)); | 1938 | const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip)); |
| 1944 | if (o.builder.getGlobal(exp_name)) |global| { | 1939 | if (o.builder.getGlobal(exp_name)) |global| { |
| 1945 | switch (global.ptrConst(&o.builder).kind) { | 1940 | switch (global.ptrConst(&o.builder).kind) { |
| ... | @@ -1967,11 +1962,6 @@ pub const Object = struct { | ... | @@ -1967,11 +1962,6 @@ pub const Object = struct { |
| 1967 | } | 1962 | } |
| 1968 | } | 1963 | } |
| 1969 | 1964 | ||
| 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/dev.zig+12| ... | @@ -30,6 +30,10 @@ pub const Env = enum { | ... | @@ -30,6 +30,10 @@ pub const Env = enum { |
| 30 | /// - `zig build-* -fno-llvm -fno-lld -target riscv64-linux` | 30 | /// - `zig build-* -fno-llvm -fno-lld -target riscv64-linux` |
| 31 | @"riscv64-linux", | 31 | @"riscv64-linux", |
| 32 | 32 | ||
| 33 | /// - sema | ||
| 34 | /// - `zig build-* -fno-llvm -fno-lld -target wasm32-* --listen=-` | ||
| 35 | wasm, | ||
| 36 | |||
| 33 | pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool { | 37 | pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool { |
| 34 | return switch (dev_env) { | 38 | return switch (dev_env) { |
| 35 | .full => true, | 39 | .full => true, |
| ... | @@ -144,6 +148,14 @@ pub const Env = enum { | ... | @@ -144,6 +148,14 @@ pub const Env = enum { |
| 144 | => true, | 148 | => true, |
| 145 | else => Env.sema.supports(feature), | 149 | else => Env.sema.supports(feature), |
| 146 | }, | 150 | }, |
| 151 | .wasm => switch (feature) { | ||
| 152 | .stdio_listen, | ||
| 153 | .incremental, | ||
| 154 | .wasm_backend, | ||
| 155 | .wasm_linker, | ||
| 156 | => true, | ||
| 157 | else => Env.sema.supports(feature), | ||
| 158 | }, | ||
| 147 | }; | 159 | }; |
| 148 | } | 160 | } |
| 149 | 161 |
src/glibc.zig+12| ... | @@ -1217,6 +1217,18 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi | ... | @@ -1217,6 +1217,18 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi |
| 1217 | }); | 1217 | }); |
| 1218 | } | 1218 | } |
| 1219 | 1219 | ||
| 1220 | pub fn sharedObjectsCount(target: *const std.Target) u8 { | ||
| 1221 | const target_version = target.os.versionRange().gnuLibCVersion() orelse return 0; | ||
| 1222 | var count: u8 = 0; | ||
| 1223 | for (libs) |lib| { | ||
| 1224 | if (lib.removed_in) |rem_in| { | ||
| 1225 | if (target_version.order(rem_in) != .lt) continue; | ||
| 1226 | } | ||
| 1227 | count += 1; | ||
| 1228 | } | ||
| 1229 | return count; | ||
| 1230 | } | ||
| 1231 | |||
| 1220 | fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { | 1232 | fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { |
| 1221 | const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?; | 1233 | const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?; |
| 1222 | 1234 |
src/link.zig+181-156| ... | @@ -38,6 +38,11 @@ pub const Diags = struct { | ... | @@ -38,6 +38,11 @@ pub const Diags = struct { |
| 38 | flags: Flags, | 38 | flags: Flags, |
| 39 | lld: std.ArrayListUnmanaged(Lld), | 39 | lld: std.ArrayListUnmanaged(Lld), |
| 40 | 40 | ||
| 41 | pub const SourceLocation = union(enum) { | ||
| 42 | none, | ||
| 43 | wasm: File.Wasm.SourceLocation, | ||
| 44 | }; | ||
| 45 | |||
| 41 | pub const Flags = packed struct { | 46 | pub const Flags = packed struct { |
| 42 | no_entry_point_found: bool = false, | 47 | no_entry_point_found: bool = false, |
| 43 | missing_libc: bool = false, | 48 | missing_libc: bool = false, |
| ... | @@ -70,9 +75,25 @@ pub const Diags = struct { | ... | @@ -70,9 +75,25 @@ pub const Diags = struct { |
| 70 | }; | 75 | }; |
| 71 | 76 | ||
| 72 | pub const Msg = struct { | 77 | pub const Msg = struct { |
| 78 | source_location: SourceLocation = .none, | ||
| 73 | msg: []const u8, | 79 | msg: []const u8, |
| 74 | notes: []Msg = &.{}, | 80 | notes: []Msg = &.{}, |
| 75 | 81 | ||
| 82 | fn string( | ||
| 83 | msg: *const Msg, | ||
| 84 | bundle: *std.zig.ErrorBundle.Wip, | ||
| 85 | base: ?*File, | ||
| 86 | ) Allocator.Error!std.zig.ErrorBundle.String { | ||
| 87 | return switch (msg.source_location) { | ||
| 88 | .none => try bundle.addString(msg.msg), | ||
| 89 | .wasm => |sl| { | ||
| 90 | dev.check(.wasm_linker); | ||
| 91 | const wasm = base.?.cast(.wasm).?; | ||
| 92 | return sl.string(msg.msg, bundle, wasm); | ||
| 93 | }, | ||
| 94 | }; | ||
| 95 | } | ||
| 96 | |||
| 76 | pub fn deinit(self: *Msg, gpa: Allocator) void { | 97 | pub fn deinit(self: *Msg, gpa: Allocator) void { |
| 77 | for (self.notes) |*note| note.deinit(gpa); | 98 | for (self.notes) |*note| note.deinit(gpa); |
| 78 | gpa.free(self.notes); | 99 | gpa.free(self.notes); |
| ... | @@ -97,15 +118,12 @@ pub const Diags = struct { | ... | @@ -97,15 +118,12 @@ pub const Diags = struct { |
| 97 | err_msg.msg = try std.fmt.allocPrint(gpa, format, args); | 118 | err_msg.msg = try std.fmt.allocPrint(gpa, format, args); |
| 98 | } | 119 | } |
| 99 | 120 | ||
| 100 | pub fn addNote( | 121 | pub fn addNote(err: *ErrorWithNotes, comptime format: []const u8, args: anytype) void { |
| 101 | err: *ErrorWithNotes, | ||
| 102 | comptime format: []const u8, | ||
| 103 | args: anytype, | ||
| 104 | ) error{OutOfMemory}!void { | ||
| 105 | const gpa = err.diags.gpa; | 122 | const gpa = err.diags.gpa; |
| 123 | const msg = std.fmt.allocPrint(gpa, format, args) catch return err.diags.setAllocFailure(); | ||
| 106 | const err_msg = &err.diags.msgs.items[err.index]; | 124 | const err_msg = &err.diags.msgs.items[err.index]; |
| 107 | assert(err.note_slot < err_msg.notes.len); | 125 | assert(err.note_slot < err_msg.notes.len); |
| 108 | err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) }; | 126 | err_msg.notes[err.note_slot] = .{ .msg = msg }; |
| 109 | err.note_slot += 1; | 127 | err.note_slot += 1; |
| 110 | } | 128 | } |
| 111 | }; | 129 | }; |
| ... | @@ -196,22 +214,35 @@ pub const Diags = struct { | ... | @@ -196,22 +214,35 @@ pub const Diags = struct { |
| 196 | return error.LinkFailure; | 214 | return error.LinkFailure; |
| 197 | } | 215 | } |
| 198 | 216 | ||
| 217 | pub fn failSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) error{LinkFailure} { | ||
| 218 | @branchHint(.cold); | ||
| 219 | addErrorSourceLocation(diags, sl, format, args); | ||
| 220 | return error.LinkFailure; | ||
| 221 | } | ||
| 222 | |||
| 199 | pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void { | 223 | pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void { |
| 224 | return addErrorSourceLocation(diags, .none, format, args); | ||
| 225 | } | ||
| 226 | |||
| 227 | pub fn addErrorSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) void { | ||
| 200 | @branchHint(.cold); | 228 | @branchHint(.cold); |
| 201 | const gpa = diags.gpa; | 229 | const gpa = diags.gpa; |
| 202 | const eu_main_msg = std.fmt.allocPrint(gpa, format, args); | 230 | const eu_main_msg = std.fmt.allocPrint(gpa, format, args); |
| 203 | diags.mutex.lock(); | 231 | diags.mutex.lock(); |
| 204 | defer diags.mutex.unlock(); | 232 | defer diags.mutex.unlock(); |
| 205 | addErrorLockedFallible(diags, eu_main_msg) catch |err| switch (err) { | 233 | addErrorLockedFallible(diags, sl, eu_main_msg) catch |err| switch (err) { |
| 206 | error.OutOfMemory => diags.setAllocFailureLocked(), | 234 | error.OutOfMemory => diags.setAllocFailureLocked(), |
| 207 | }; | 235 | }; |
| 208 | } | 236 | } |
| 209 | 237 | ||
| 210 | fn addErrorLockedFallible(diags: *Diags, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void { | 238 | fn addErrorLockedFallible(diags: *Diags, sl: SourceLocation, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void { |
| 211 | const gpa = diags.gpa; | 239 | const gpa = diags.gpa; |
| 212 | const main_msg = try eu_main_msg; | 240 | const main_msg = try eu_main_msg; |
| 213 | errdefer gpa.free(main_msg); | 241 | errdefer gpa.free(main_msg); |
| 214 | try diags.msgs.append(gpa, .{ .msg = main_msg }); | 242 | try diags.msgs.append(gpa, .{ |
| 243 | .msg = main_msg, | ||
| 244 | .source_location = sl, | ||
| 245 | }); | ||
| 215 | } | 246 | } |
| 216 | 247 | ||
| 217 | pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { | 248 | pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { |
| ... | @@ -329,16 +360,16 @@ pub const Diags = struct { | ... | @@ -329,16 +360,16 @@ pub const Diags = struct { |
| 329 | diags.flags.alloc_failure_occurred = true; | 360 | diags.flags.alloc_failure_occurred = true; |
| 330 | } | 361 | } |
| 331 | 362 | ||
| 332 | pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void { | 363 | pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip, base: ?*File) Allocator.Error!void { |
| 333 | for (diags.msgs.items) |link_err| { | 364 | for (diags.msgs.items) |link_err| { |
| 334 | try bundle.addRootErrorMessage(.{ | 365 | try bundle.addRootErrorMessage(.{ |
| 335 | .msg = try bundle.addString(link_err.msg), | 366 | .msg = try link_err.string(bundle, base), |
| 336 | .notes_len = @intCast(link_err.notes.len), | 367 | .notes_len = @intCast(link_err.notes.len), |
| 337 | }); | 368 | }); |
| 338 | const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); | 369 | const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); |
| 339 | for (link_err.notes, 0..) |note, i| { | 370 | for (link_err.notes, 0..) |note, i| { |
| 340 | bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ | 371 | bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ |
| 341 | .msg = try bundle.addString(note.msg), | 372 | .msg = try note.string(bundle, base), |
| 342 | })); | 373 | })); |
| 343 | } | 374 | } |
| 344 | } | 375 | } |
| ... | @@ -364,6 +395,7 @@ pub const File = struct { | ... | @@ -364,6 +395,7 @@ pub const File = struct { |
| 364 | build_id: std.zig.BuildId, | 395 | build_id: std.zig.BuildId, |
| 365 | allow_shlib_undefined: bool, | 396 | allow_shlib_undefined: bool, |
| 366 | stack_size: u64, | 397 | stack_size: u64, |
| 398 | post_prelink: bool = false, | ||
| 367 | 399 | ||
| 368 | /// Prevents other processes from clobbering files in the output directory | 400 | /// Prevents other processes from clobbering files in the output directory |
| 369 | /// of this linking operation. | 401 | /// of this linking operation. |
| ... | @@ -400,6 +432,7 @@ pub const File = struct { | ... | @@ -400,6 +432,7 @@ pub const File = struct { |
| 400 | export_table: bool, | 432 | export_table: bool, |
| 401 | initial_memory: ?u64, | 433 | initial_memory: ?u64, |
| 402 | max_memory: ?u64, | 434 | max_memory: ?u64, |
| 435 | object_host_name: ?[]const u8, | ||
| 403 | export_symbol_names: []const []const u8, | 436 | export_symbol_names: []const []const u8, |
| 404 | global_base: ?u64, | 437 | global_base: ?u64, |
| 405 | build_id: std.zig.BuildId, | 438 | build_id: std.zig.BuildId, |
| ... | @@ -632,43 +665,15 @@ pub const File = struct { | ... | @@ -632,43 +665,15 @@ pub const File = struct { |
| 632 | pub const UpdateDebugInfoError = Dwarf.UpdateError; | 665 | pub const UpdateDebugInfoError = Dwarf.UpdateError; |
| 633 | pub const FlushDebugInfoError = Dwarf.FlushError; | 666 | pub const FlushDebugInfoError = Dwarf.FlushError; |
| 634 | 667 | ||
| 668 | /// Note that `LinkFailure` is not a member of this error set because the error message | ||
| 669 | /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`. | ||
| 635 | pub const UpdateNavError = error{ | 670 | pub const UpdateNavError = error{ |
| 636 | OutOfMemory, | ||
| 637 | Overflow, | 671 | Overflow, |
| 638 | Underflow, | 672 | OutOfMemory, |
| 639 | FileTooBig, | 673 | /// Indicates the error is already reported and stored in |
| 640 | InputOutput, | 674 | /// `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, | 675 | CodegenFail, |
| 663 | EmitFail, | 676 | }; |
| 664 | NameTooLong, | ||
| 665 | CurrentWorkingDirectoryUnlinked, | ||
| 666 | LockViolation, | ||
| 667 | NetNameDeleted, | ||
| 668 | DeviceBusy, | ||
| 669 | InvalidArgument, | ||
| 670 | HotSwapUnavailableOnHostOperatingSystem, | ||
| 671 | } || UpdateDebugInfoError; | ||
| 672 | 677 | ||
| 673 | /// Called from within CodeGen to retrieve the symbol index of a global symbol. | 678 | /// 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 will | 679 | /// If no symbol exists yet with this name, a new undefined global symbol will |
| ... | @@ -701,7 +706,13 @@ pub const File = struct { | ... | @@ -701,7 +706,13 @@ pub const File = struct { |
| 701 | } | 706 | } |
| 702 | } | 707 | } |
| 703 | 708 | ||
| 704 | pub fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateNavError!void { | 709 | pub const UpdateContainerTypeError = error{ |
| 710 | OutOfMemory, | ||
| 711 | /// `Zcu.failed_types` is already populated with the error message. | ||
| 712 | TypeFailureReported, | ||
| 713 | }; | ||
| 714 | |||
| 715 | pub fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { | ||
| 705 | switch (base.tag) { | 716 | switch (base.tag) { |
| 706 | else => {}, | 717 | else => {}, |
| 707 | inline .elf => |tag| { | 718 | inline .elf => |tag| { |
| ... | @@ -727,9 +738,15 @@ pub const File = struct { | ... | @@ -727,9 +738,15 @@ pub const File = struct { |
| 727 | } | 738 | } |
| 728 | } | 739 | } |
| 729 | 740 | ||
| 741 | pub const UpdateLineNumberError = error{ | ||
| 742 | OutOfMemory, | ||
| 743 | Overflow, | ||
| 744 | LinkFailure, | ||
| 745 | }; | ||
| 746 | |||
| 730 | /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because | 747 | /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because |
| 731 | /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`. | 748 | /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`. |
| 732 | pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateNavError!void { | 749 | pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void { |
| 733 | { | 750 | { |
| 734 | const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; | 751 | const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; |
| 735 | const file = pt.zcu.fileByIndex(ti.file); | 752 | const file = pt.zcu.fileByIndex(ti.file); |
| ... | @@ -771,83 +788,11 @@ pub const File = struct { | ... | @@ -771,83 +788,11 @@ pub const File = struct { |
| 771 | } | 788 | } |
| 772 | } | 789 | } |
| 773 | 790 | ||
| 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{ | 791 | pub const FlushError = error{ |
| 777 | CacheCheckFailed, | 792 | /// Indicates an error will be present in `Compilation.link_diags`. |
| 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`. | ||
| 789 | LinkFailure, | 793 | 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, | 794 | OutOfMemory, |
| 822 | Overflow, | 795 | }; |
| 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; | ||
| 851 | 796 | ||
| 852 | /// Commit pending changes and write headers. Takes into account final output mode | 797 | /// Commit pending changes and write headers. Takes into account final output mode |
| 853 | /// and `use_lld`, not only `effectiveOutputMode`. | 798 | /// and `use_lld`, not only `effectiveOutputMode`. |
| ... | @@ -864,10 +809,17 @@ pub const File = struct { | ... | @@ -864,10 +809,17 @@ pub const File = struct { |
| 864 | assert(comp.c_object_table.count() == 1); | 809 | assert(comp.c_object_table.count() == 1); |
| 865 | const the_key = comp.c_object_table.keys()[0]; | 810 | const the_key = comp.c_object_table.keys()[0]; |
| 866 | const cached_pp_file_path = the_key.status.success.object_path; | 811 | 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, .{}); | 812 | cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| { |
| 813 | const diags = &base.comp.link_diags; | ||
| 814 | return diags.fail("failed to copy '{'}' to '{'}': {s}", .{ | ||
| 815 | @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err), | ||
| 816 | }); | ||
| 817 | }; | ||
| 868 | return; | 818 | return; |
| 869 | } | 819 | } |
| 870 | 820 | ||
| 821 | assert(base.post_prelink); | ||
| 822 | |||
| 871 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 823 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 872 | const output_mode = comp.config.output_mode; | 824 | const output_mode = comp.config.output_mode; |
| 873 | const link_mode = comp.config.link_mode; | 825 | const link_mode = comp.config.link_mode; |
| ... | @@ -893,16 +845,6 @@ pub const File = struct { | ... | @@ -893,16 +845,6 @@ pub const File = struct { |
| 893 | } | 845 | } |
| 894 | } | 846 | } |
| 895 | 847 | ||
| 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{ | 848 | pub const UpdateExportsError = error{ |
| 907 | OutOfMemory, | 849 | OutOfMemory, |
| 908 | AnalysisFail, | 850 | AnalysisFail, |
| ... | @@ -916,7 +858,7 @@ pub const File = struct { | ... | @@ -916,7 +858,7 @@ pub const File = struct { |
| 916 | base: *File, | 858 | base: *File, |
| 917 | pt: Zcu.PerThread, | 859 | pt: Zcu.PerThread, |
| 918 | exported: Zcu.Exported, | 860 | exported: Zcu.Exported, |
| 919 | export_indices: []const u32, | 861 | export_indices: []const Zcu.Export.Index, |
| 920 | ) UpdateExportsError!void { | 862 | ) UpdateExportsError!void { |
| 921 | switch (base.tag) { | 863 | switch (base.tag) { |
| 922 | inline else => |tag| { | 864 | inline else => |tag| { |
| ... | @@ -932,6 +874,7 @@ pub const File = struct { | ... | @@ -932,6 +874,7 @@ pub const File = struct { |
| 932 | addend: u32, | 874 | addend: u32, |
| 933 | 875 | ||
| 934 | pub const Parent = union(enum) { | 876 | pub const Parent = union(enum) { |
| 877 | none, | ||
| 935 | atom_index: u32, | 878 | atom_index: u32, |
| 936 | debug_output: DebugInfoOutput, | 879 | debug_output: DebugInfoOutput, |
| 937 | }; | 880 | }; |
| ... | @@ -948,6 +891,7 @@ pub const File = struct { | ... | @@ -948,6 +891,7 @@ pub const File = struct { |
| 948 | .c => unreachable, | 891 | .c => unreachable, |
| 949 | .spirv => unreachable, | 892 | .spirv => unreachable, |
| 950 | .nvptx => unreachable, | 893 | .nvptx => unreachable, |
| 894 | .wasm => unreachable, | ||
| 951 | inline else => |tag| { | 895 | inline else => |tag| { |
| 952 | dev.check(tag.devFeature()); | 896 | dev.check(tag.devFeature()); |
| 953 | return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info); | 897 | return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info); |
| ... | @@ -966,6 +910,7 @@ pub const File = struct { | ... | @@ -966,6 +910,7 @@ pub const File = struct { |
| 966 | .c => unreachable, | 910 | .c => unreachable, |
| 967 | .spirv => unreachable, | 911 | .spirv => unreachable, |
| 968 | .nvptx => unreachable, | 912 | .nvptx => unreachable, |
| 913 | .wasm => unreachable, | ||
| 969 | inline else => |tag| { | 914 | inline else => |tag| { |
| 970 | dev.check(tag.devFeature()); | 915 | dev.check(tag.devFeature()); |
| 971 | return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc); | 916 | return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc); |
| ... | @@ -978,6 +923,7 @@ pub const File = struct { | ... | @@ -978,6 +923,7 @@ pub const File = struct { |
| 978 | .c => unreachable, | 923 | .c => unreachable, |
| 979 | .spirv => unreachable, | 924 | .spirv => unreachable, |
| 980 | .nvptx => unreachable, | 925 | .nvptx => unreachable, |
| 926 | .wasm => unreachable, | ||
| 981 | inline else => |tag| { | 927 | inline else => |tag| { |
| 982 | dev.check(tag.devFeature()); | 928 | dev.check(tag.devFeature()); |
| 983 | return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info); | 929 | return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info); |
| ... | @@ -1099,12 +1045,44 @@ pub const File = struct { | ... | @@ -1099,12 +1045,44 @@ pub const File = struct { |
| 1099 | } | 1045 | } |
| 1100 | } | 1046 | } |
| 1101 | 1047 | ||
| 1048 | /// Called when all linker inputs have been sent via `loadInput`. After | ||
| 1049 | /// this, `loadInput` will not be called anymore. | ||
| 1050 | pub fn prelink(base: *File, prog_node: std.Progress.Node) FlushError!void { | ||
| 1051 | assert(!base.post_prelink); | ||
| 1052 | const use_lld = build_options.have_llvm and base.comp.config.use_lld; | ||
| 1053 | if (use_lld) return; | ||
| 1054 | |||
| 1055 | // In this case, an object file is created by the LLVM backend, so | ||
| 1056 | // there is no prelink phase. The Zig code is linked as a standard | ||
| 1057 | // object along with the others. | ||
| 1058 | if (base.zcu_object_sub_path != null) return; | ||
| 1059 | |||
| 1060 | switch (base.tag) { | ||
| 1061 | inline .wasm => |tag| { | ||
| 1062 | dev.check(tag.devFeature()); | ||
| 1063 | return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(prog_node); | ||
| 1064 | }, | ||
| 1065 | else => {}, | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 1102 | pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { | 1069 | pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { |
| 1103 | dev.check(.lld_linker); | 1070 | dev.check(.lld_linker); |
| 1104 | 1071 | ||
| 1105 | const tracy = trace(@src()); | 1072 | const tracy = trace(@src()); |
| 1106 | defer tracy.end(); | 1073 | defer tracy.end(); |
| 1107 | 1074 | ||
| 1075 | const comp = base.comp; | ||
| 1076 | const diags = &comp.link_diags; | ||
| 1077 | |||
| 1078 | return linkAsArchiveInner(base, arena, tid, prog_node) catch |err| switch (err) { | ||
| 1079 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1080 | error.LinkFailure => return error.LinkFailure, | ||
| 1081 | else => |e| return diags.fail("failed to link as archive: {s}", .{@errorName(e)}), | ||
| 1082 | }; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | fn linkAsArchiveInner(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { | ||
| 1108 | const comp = base.comp; | 1086 | const comp = base.comp; |
| 1109 | 1087 | ||
| 1110 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. | 1088 | const directory = base.emit.root_dir; // Just an alias to make it shorter to type. |
| ... | @@ -1364,6 +1342,16 @@ pub const File = struct { | ... | @@ -1364,6 +1342,16 @@ pub const File = struct { |
| 1364 | }, llvm_object, prog_node); | 1342 | }, llvm_object, prog_node); |
| 1365 | } | 1343 | } |
| 1366 | 1344 | ||
| 1345 | pub fn cgFail( | ||
| 1346 | base: *File, | ||
| 1347 | nav_index: InternPool.Nav.Index, | ||
| 1348 | comptime format: []const u8, | ||
| 1349 | args: anytype, | ||
| 1350 | ) error{ CodegenFail, OutOfMemory } { | ||
| 1351 | @branchHint(.cold); | ||
| 1352 | return base.comp.zcu.?.codegenFail(nav_index, format, args); | ||
| 1353 | } | ||
| 1354 | |||
| 1367 | pub const C = @import("link/C.zig"); | 1355 | pub const C = @import("link/C.zig"); |
| 1368 | pub const Coff = @import("link/Coff.zig"); | 1356 | pub const Coff = @import("link/Coff.zig"); |
| 1369 | pub const Plan9 = @import("link/Plan9.zig"); | 1357 | pub const Plan9 = @import("link/Plan9.zig"); |
| ... | @@ -1379,12 +1367,32 @@ pub const File = struct { | ... | @@ -1379,12 +1367,32 @@ pub const File = struct { |
| 1379 | /// from the rest of compilation. All tasks performed here are | 1367 | /// from the rest of compilation. All tasks performed here are |
| 1380 | /// single-threaded with respect to one another. | 1368 | /// single-threaded with respect to one another. |
| 1381 | pub fn flushTaskQueue(tid: usize, comp: *Compilation) void { | 1369 | pub fn flushTaskQueue(tid: usize, comp: *Compilation) void { |
| 1370 | const diags = &comp.link_diags; | ||
| 1382 | // As soon as check() is called, another `flushTaskQueue` call could occur, | 1371 | // As soon as check() is called, another `flushTaskQueue` call could occur, |
| 1383 | // so the safety lock must go after the check. | 1372 | // so the safety lock must go after the check. |
| 1384 | while (comp.link_task_queue.check()) |tasks| { | 1373 | while (comp.link_task_queue.check()) |tasks| { |
| 1385 | comp.link_task_queue_safety.lock(); | 1374 | comp.link_task_queue_safety.lock(); |
| 1386 | defer comp.link_task_queue_safety.unlock(); | 1375 | defer comp.link_task_queue_safety.unlock(); |
| 1376 | |||
| 1377 | if (comp.remaining_prelink_tasks > 0) { | ||
| 1378 | comp.link_task_queue_postponed.ensureUnusedCapacity(comp.gpa, tasks.len) catch |err| switch (err) { | ||
| 1379 | error.OutOfMemory => return diags.setAllocFailure(), | ||
| 1380 | }; | ||
| 1381 | } | ||
| 1382 | |||
| 1387 | for (tasks) |task| doTask(comp, tid, task); | 1383 | for (tasks) |task| doTask(comp, tid, task); |
| 1384 | |||
| 1385 | if (comp.remaining_prelink_tasks == 0) { | ||
| 1386 | if (comp.bin_file) |base| if (!base.post_prelink) { | ||
| 1387 | base.prelink(comp.work_queue_progress_node) catch |err| switch (err) { | ||
| 1388 | error.OutOfMemory => diags.setAllocFailure(), | ||
| 1389 | error.LinkFailure => continue, | ||
| 1390 | }; | ||
| 1391 | base.post_prelink = true; | ||
| 1392 | for (comp.link_task_queue_postponed.items) |task| doTask(comp, tid, task); | ||
| 1393 | comp.link_task_queue_postponed.clearRetainingCapacity(); | ||
| 1394 | }; | ||
| 1395 | } | ||
| 1388 | } | 1396 | } |
| 1389 | } | 1397 | } |
| 1390 | 1398 | ||
| ... | @@ -1428,6 +1436,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1428,6 +1436,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1428 | const diags = &comp.link_diags; | 1436 | const diags = &comp.link_diags; |
| 1429 | switch (task) { | 1437 | switch (task) { |
| 1430 | .load_explicitly_provided => if (comp.bin_file) |base| { | 1438 | .load_explicitly_provided => if (comp.bin_file) |base| { |
| 1439 | comp.remaining_prelink_tasks -= 1; | ||
| 1431 | const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len); | 1440 | const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len); |
| 1432 | defer prog_node.end(); | 1441 | defer prog_node.end(); |
| 1433 | for (comp.link_inputs) |input| { | 1442 | for (comp.link_inputs) |input| { |
| ... | @@ -1445,6 +1454,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1445,6 +1454,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1445 | } | 1454 | } |
| 1446 | }, | 1455 | }, |
| 1447 | .load_host_libc => if (comp.bin_file) |base| { | 1456 | .load_host_libc => if (comp.bin_file) |base| { |
| 1457 | comp.remaining_prelink_tasks -= 1; | ||
| 1448 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0); | 1458 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0); |
| 1449 | defer prog_node.end(); | 1459 | defer prog_node.end(); |
| 1450 | 1460 | ||
| ... | @@ -1504,6 +1514,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1504,6 +1514,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1504 | } | 1514 | } |
| 1505 | }, | 1515 | }, |
| 1506 | .load_object => |path| if (comp.bin_file) |base| { | 1516 | .load_object => |path| if (comp.bin_file) |base| { |
| 1517 | comp.remaining_prelink_tasks -= 1; | ||
| 1507 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0); | 1518 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0); |
| 1508 | defer prog_node.end(); | 1519 | defer prog_node.end(); |
| 1509 | base.openLoadObject(path) catch |err| switch (err) { | 1520 | base.openLoadObject(path) catch |err| switch (err) { |
| ... | @@ -1512,6 +1523,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1512,6 +1523,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1512 | }; | 1523 | }; |
| 1513 | }, | 1524 | }, |
| 1514 | .load_archive => |path| if (comp.bin_file) |base| { | 1525 | .load_archive => |path| if (comp.bin_file) |base| { |
| 1526 | comp.remaining_prelink_tasks -= 1; | ||
| 1515 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0); | 1527 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0); |
| 1516 | defer prog_node.end(); | 1528 | defer prog_node.end(); |
| 1517 | base.openLoadArchive(path, null) catch |err| switch (err) { | 1529 | base.openLoadArchive(path, null) catch |err| switch (err) { |
| ... | @@ -1520,6 +1532,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1520,6 +1532,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1520 | }; | 1532 | }; |
| 1521 | }, | 1533 | }, |
| 1522 | .load_dso => |path| if (comp.bin_file) |base| { | 1534 | .load_dso => |path| if (comp.bin_file) |base| { |
| 1535 | comp.remaining_prelink_tasks -= 1; | ||
| 1523 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0); | 1536 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0); |
| 1524 | defer prog_node.end(); | 1537 | defer prog_node.end(); |
| 1525 | base.openLoadDso(path, .{ | 1538 | base.openLoadDso(path, .{ |
| ... | @@ -1531,6 +1544,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1531,6 +1544,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1531 | }; | 1544 | }; |
| 1532 | }, | 1545 | }, |
| 1533 | .load_input => |input| if (comp.bin_file) |base| { | 1546 | .load_input => |input| if (comp.bin_file) |base| { |
| 1547 | comp.remaining_prelink_tasks -= 1; | ||
| 1534 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0); | 1548 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0); |
| 1535 | defer prog_node.end(); | 1549 | defer prog_node.end(); |
| 1536 | base.loadInput(input) catch |err| switch (err) { | 1550 | base.loadInput(input) catch |err| switch (err) { |
| ... | @@ -1545,26 +1559,38 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { | ... | @@ -1545,26 +1559,38 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1545 | }; | 1559 | }; |
| 1546 | }, | 1560 | }, |
| 1547 | .codegen_nav => |nav_index| { | 1561 | .codegen_nav => |nav_index| { |
| 1548 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | 1562 | if (comp.remaining_prelink_tasks == 0) { |
| 1549 | defer pt.deactivate(); | 1563 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); |
| 1550 | pt.linkerUpdateNav(nav_index) catch |err| switch (err) { | 1564 | defer pt.deactivate(); |
| 1551 | error.OutOfMemory => diags.setAllocFailure(), | 1565 | pt.linkerUpdateNav(nav_index) catch |err| switch (err) { |
| 1552 | }; | 1566 | error.OutOfMemory => diags.setAllocFailure(), |
| 1567 | }; | ||
| 1568 | } else { | ||
| 1569 | comp.link_task_queue_postponed.appendAssumeCapacity(task); | ||
| 1570 | } | ||
| 1553 | }, | 1571 | }, |
| 1554 | .codegen_func => |func| { | 1572 | .codegen_func => |func| { |
| 1555 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | 1573 | if (comp.remaining_prelink_tasks == 0) { |
| 1556 | defer pt.deactivate(); | 1574 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); |
| 1557 | // This call takes ownership of `func.air`. | 1575 | defer pt.deactivate(); |
| 1558 | pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) { | 1576 | // This call takes ownership of `func.air`. |
| 1559 | error.OutOfMemory => diags.setAllocFailure(), | 1577 | pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) { |
| 1560 | }; | 1578 | error.OutOfMemory => diags.setAllocFailure(), |
| 1579 | }; | ||
| 1580 | } else { | ||
| 1581 | comp.link_task_queue_postponed.appendAssumeCapacity(task); | ||
| 1582 | } | ||
| 1561 | }, | 1583 | }, |
| 1562 | .codegen_type => |ty| { | 1584 | .codegen_type => |ty| { |
| 1563 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | 1585 | if (comp.remaining_prelink_tasks == 0) { |
| 1564 | defer pt.deactivate(); | 1586 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); |
| 1565 | pt.linkerUpdateContainerType(ty) catch |err| switch (err) { | 1587 | defer pt.deactivate(); |
| 1566 | error.OutOfMemory => diags.setAllocFailure(), | 1588 | pt.linkerUpdateContainerType(ty) catch |err| switch (err) { |
| 1567 | }; | 1589 | error.OutOfMemory => diags.setAllocFailure(), |
| 1590 | }; | ||
| 1591 | } else { | ||
| 1592 | comp.link_task_queue_postponed.appendAssumeCapacity(task); | ||
| 1593 | } | ||
| 1568 | }, | 1594 | }, |
| 1569 | .update_line_number => |ti| { | 1595 | .update_line_number => |ti| { |
| 1570 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | 1596 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); |
| ... | @@ -1593,7 +1619,7 @@ pub fn spawnLld( | ... | @@ -1593,7 +1619,7 @@ pub fn spawnLld( |
| 1593 | const exit_code = try lldMain(arena, argv, false); | 1619 | const exit_code = try lldMain(arena, argv, false); |
| 1594 | if (exit_code == 0) return; | 1620 | if (exit_code == 0) return; |
| 1595 | if (comp.clang_passthrough_mode) std.process.exit(exit_code); | 1621 | if (comp.clang_passthrough_mode) std.process.exit(exit_code); |
| 1596 | return error.LLDReportedFailure; | 1622 | return error.LinkFailure; |
| 1597 | } | 1623 | } |
| 1598 | 1624 | ||
| 1599 | var stderr: []u8 = &.{}; | 1625 | var stderr: []u8 = &.{}; |
| ... | @@ -1670,17 +1696,16 @@ pub fn spawnLld( | ... | @@ -1670,17 +1696,16 @@ pub fn spawnLld( |
| 1670 | return error.UnableToSpawnSelf; | 1696 | return error.UnableToSpawnSelf; |
| 1671 | }; | 1697 | }; |
| 1672 | 1698 | ||
| 1699 | const diags = &comp.link_diags; | ||
| 1673 | switch (term) { | 1700 | switch (term) { |
| 1674 | .Exited => |code| if (code != 0) { | 1701 | .Exited => |code| if (code != 0) { |
| 1675 | if (comp.clang_passthrough_mode) std.process.exit(code); | 1702 | if (comp.clang_passthrough_mode) std.process.exit(code); |
| 1676 | const diags = &comp.link_diags; | ||
| 1677 | diags.lockAndParseLldStderr(argv[1], stderr); | 1703 | diags.lockAndParseLldStderr(argv[1], stderr); |
| 1678 | return error.LLDReportedFailure; | 1704 | return error.LinkFailure; |
| 1679 | }, | 1705 | }, |
| 1680 | else => { | 1706 | else => { |
| 1681 | if (comp.clang_passthrough_mode) std.process.abort(); | 1707 | if (comp.clang_passthrough_mode) std.process.abort(); |
| 1682 | log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); | 1708 | return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); |
| 1683 | return error.LLDCrashed; | ||
| 1684 | }, | 1709 | }, |
| 1685 | } | 1710 | } |
| 1686 | 1711 | ||
| ... | @@ -2239,7 +2264,7 @@ fn resolvePathInputLib( | ... | @@ -2239,7 +2264,7 @@ fn resolvePathInputLib( |
| 2239 | try wip_errors.init(gpa); | 2264 | try wip_errors.init(gpa); |
| 2240 | defer wip_errors.deinit(); | 2265 | defer wip_errors.deinit(); |
| 2241 | 2266 | ||
| 2242 | try diags.addMessagesToBundle(&wip_errors); | 2267 | try diags.addMessagesToBundle(&wip_errors, null); |
| 2243 | 2268 | ||
| 2244 | var error_bundle = try wip_errors.toOwnedBundle(""); | 2269 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 2245 | defer error_bundle.deinit(gpa); | 2270 | defer error_bundle.deinit(gpa); |
src/link/C.zig+11-16| ... | @@ -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 | } |
| 177 | 177 | ||
| 178 | pub 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 | |||
| 186 | pub fn updateFunc( | 178 | pub 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 | } |
| 315 | 307 | ||
| 316 | pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 308 | pub 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(); |
| 319 | 311 | ||
| ... | @@ -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 | } |
| 392 | 384 | ||
| 393 | pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { | 385 | pub 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 | } |
| 396 | 388 | ||
| ... | @@ -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 | } |
| 411 | 403 | ||
| 412 | pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { | 404 | pub 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. |
| 414 | 406 | ||
| 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(); |
| 420 | 412 | ||
| 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; |
| ... | @@ -476,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -476,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 476 | defer export_names.deinit(gpa); | 469 | defer export_names.deinit(gpa); |
| 477 | try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count())); | 470 | try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count())); |
| 478 | for (zcu.single_exports.values()) |export_index| { | 471 | for (zcu.single_exports.values()) |export_index| { |
| 479 | export_names.putAssumeCapacity(zcu.all_exports.items[export_index].opts.name, {}); | 472 | export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {}); |
| 480 | } | 473 | } |
| 481 | for (zcu.multi_exports.values()) |info| { | 474 | for (zcu.multi_exports.values()) |info| { |
| 482 | try export_names.ensureUnusedCapacity(gpa, info.len); | 475 | try export_names.ensureUnusedCapacity(gpa, info.len); |
| ... | @@ -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)); |
| 555 | 548 | ||
| 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 | } |
| 560 | 555 | ||
| 561 | const Flush = struct { | 556 | const Flush = struct { |
| ... | @@ -845,7 +840,7 @@ pub fn updateExports( | ... | @@ -845,7 +840,7 @@ pub fn updateExports( |
| 845 | self: *C, | 840 | self: *C, |
| 846 | pt: Zcu.PerThread, | 841 | pt: Zcu.PerThread, |
| 847 | exported: Zcu.Exported, | 842 | exported: Zcu.Exported, |
| 848 | export_indices: []const u32, | 843 | export_indices: []const Zcu.Export.Index, |
| 849 | ) !void { | 844 | ) !void { |
| 850 | const zcu = pt.zcu; | 845 | const zcu = pt.zcu; |
| 851 | const gpa = zcu.gpa; | 846 | const gpa = zcu.gpa; |
src/link/Coff.zig+126-85| ... | @@ -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 | } |
| 413 | 413 | ||
| 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 | } |
| 859 | 859 | ||
| 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); |
| 862 | 862 | ||
| 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.?; |
| 892 | 892 | ||
| 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,7 +908,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void { | ... | @@ -908,7 +908,7 @@ 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.base.file.?.pwriteAll(&buf, file_offset); |
| 913 | }, | 913 | }, |
| 914 | .p64 => { | 914 | .p64 => { |
| ... | @@ -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 | } |
| 1095 | 1095 | ||
| 1096 | pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 1096 | pub 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,34 +1112,41 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index, | ... | @@ -1106,34 +1112,41 @@ 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; | ||
| 1109 | 1116 | ||
| 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); |
| 1112 | 1119 | ||
| 1113 | coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?; | 1120 | coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?; |
| 1114 | 1121 | ||
| 1115 | var code_buffer = std.ArrayList(u8).init(gpa); | 1122 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1116 | defer code_buffer.deinit(); | 1123 | defer code_buffer.deinit(gpa); |
| 1117 | 1124 | ||
| 1118 | const res = try codegen.generateFunction( | 1125 | 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) { |
| 1128 | const code = switch (res) { | 1135 | error.CodegenFail => return error.CodegenFail, |
| 1129 | .ok => code_buffer.items, | 1136 | error.OutOfMemory => return error.OutOfMemory, |
| 1130 | .fail => |em| { | 1137 | error.Overflow => |e| { |
| 1131 | try zcu.failed_codegen.put(zcu.gpa, func.owner_nav, em); | 1138 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( |
| 1132 | return; | 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; | ||
| 1133 | }, | 1146 | }, |
| 1134 | }; | 1147 | }; |
| 1135 | 1148 | ||
| 1136 | try coff.updateNavCode(pt, func.owner_nav, code, .FUNCTION); | 1149 | try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION); |
| 1137 | 1150 | ||
| 1138 | // Exports will be updated by `Zcu.processExports` after the update. | 1151 | // Exports will be updated by `Zcu.processExports` after the update. |
| 1139 | } | 1152 | } |
| ... | @@ -1154,24 +1167,21 @@ fn lowerConst( | ... | @@ -1154,24 +1167,21 @@ fn lowerConst( |
| 1154 | ) !LowerConstResult { | 1167 | ) !LowerConstResult { |
| 1155 | const gpa = coff.base.comp.gpa; | 1168 | const gpa = coff.base.comp.gpa; |
| 1156 | 1169 | ||
| 1157 | var code_buffer = std.ArrayList(u8).init(gpa); | 1170 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1158 | defer code_buffer.deinit(); | 1171 | defer code_buffer.deinit(gpa); |
| 1159 | 1172 | ||
| 1160 | const atom_index = try coff.createAtom(); | 1173 | const atom_index = try coff.createAtom(); |
| 1161 | const sym = coff.getAtom(atom_index).getSymbolPtr(coff); | 1174 | const sym = coff.getAtom(atom_index).getSymbolPtr(coff); |
| 1162 | try coff.setSymbolName(sym, name); | 1175 | try coff.setSymbolName(sym, name); |
| 1163 | sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_id + 1)); | 1176 | sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_id + 1)); |
| 1164 | 1177 | ||
| 1165 | const res = try codegen.generateSymbol(&coff.base, pt, src_loc, val, &code_buffer, .{ | 1178 | try codegen.generateSymbol(&coff.base, pt, src_loc, val, &code_buffer, .{ |
| 1166 | .atom_index = coff.getAtom(atom_index).getSymbolIndex().?, | 1179 | .atom_index = coff.getAtom(atom_index).getSymbolIndex().?, |
| 1167 | }); | 1180 | }); |
| 1168 | const code = switch (res) { | 1181 | const code = code_buffer.items; |
| 1169 | .ok => code_buffer.items, | ||
| 1170 | .fail => |em| return .{ .fail = em }, | ||
| 1171 | }; | ||
| 1172 | 1182 | ||
| 1173 | const atom = coff.getAtomPtr(atom_index); | 1183 | const atom = coff.getAtomPtr(atom_index); |
| 1174 | atom.size = @as(u32, @intCast(code.len)); | 1184 | atom.size = @intCast(code.len); |
| 1175 | atom.getSymbolPtr(coff).value = try coff.allocateAtom( | 1185 | atom.getSymbolPtr(coff).value = try coff.allocateAtom( |
| 1176 | atom_index, | 1186 | atom_index, |
| 1177 | atom.size, | 1187 | atom.size, |
| ... | @@ -1227,10 +1237,10 @@ pub fn updateNav( | ... | @@ -1227,10 +1237,10 @@ pub fn updateNav( |
| 1227 | 1237 | ||
| 1228 | coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index); | 1238 | coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index); |
| 1229 | 1239 | ||
| 1230 | var code_buffer = std.ArrayList(u8).init(gpa); | 1240 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1231 | defer code_buffer.deinit(); | 1241 | defer code_buffer.deinit(gpa); |
| 1232 | 1242 | ||
| 1233 | const res = try codegen.generateSymbol( | 1243 | try codegen.generateSymbol( |
| 1234 | &coff.base, | 1244 | &coff.base, |
| 1235 | pt, | 1245 | pt, |
| 1236 | zcu.navSrcLoc(nav_index), | 1246 | zcu.navSrcLoc(nav_index), |
| ... | @@ -1238,15 +1248,8 @@ pub fn updateNav( | ... | @@ -1238,15 +1248,8 @@ pub fn updateNav( |
| 1238 | &code_buffer, | 1248 | &code_buffer, |
| 1239 | .{ .atom_index = atom.getSymbolIndex().? }, | 1249 | .{ .atom_index = atom.getSymbolIndex().? }, |
| 1240 | ); | 1250 | ); |
| 1241 | const code = switch (res) { | ||
| 1242 | .ok => code_buffer.items, | ||
| 1243 | .fail => |em| { | ||
| 1244 | try zcu.failed_codegen.put(gpa, nav_index, em); | ||
| 1245 | return; | ||
| 1246 | }, | ||
| 1247 | }; | ||
| 1248 | 1251 | ||
| 1249 | try coff.updateNavCode(pt, nav_index, code, .NULL); | 1252 | try coff.updateNavCode(pt, nav_index, code_buffer.items, .NULL); |
| 1250 | } | 1253 | } |
| 1251 | 1254 | ||
| 1252 | // Exports will be updated by `Zcu.processExports` after the update. | 1255 | // Exports will be updated by `Zcu.processExports` after the update. |
| ... | @@ -1260,11 +1263,12 @@ fn updateLazySymbolAtom( | ... | @@ -1260,11 +1263,12 @@ fn updateLazySymbolAtom( |
| 1260 | section_index: u16, | 1263 | section_index: u16, |
| 1261 | ) !void { | 1264 | ) !void { |
| 1262 | const zcu = pt.zcu; | 1265 | const zcu = pt.zcu; |
| 1263 | const gpa = zcu.gpa; | 1266 | const comp = coff.base.comp; |
| 1267 | const gpa = comp.gpa; | ||
| 1264 | 1268 | ||
| 1265 | var required_alignment: InternPool.Alignment = .none; | 1269 | var required_alignment: InternPool.Alignment = .none; |
| 1266 | var code_buffer = std.ArrayList(u8).init(gpa); | 1270 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1267 | defer code_buffer.deinit(); | 1271 | defer code_buffer.deinit(gpa); |
| 1268 | 1272 | ||
| 1269 | const name = try allocPrint(gpa, "__lazy_{s}_{}", .{ | 1273 | const name = try allocPrint(gpa, "__lazy_{s}_{}", .{ |
| 1270 | @tagName(sym.kind), | 1274 | @tagName(sym.kind), |
| ... | @@ -1276,7 +1280,7 @@ fn updateLazySymbolAtom( | ... | @@ -1276,7 +1280,7 @@ fn updateLazySymbolAtom( |
| 1276 | const local_sym_index = atom.getSymbolIndex().?; | 1280 | const local_sym_index = atom.getSymbolIndex().?; |
| 1277 | 1281 | ||
| 1278 | const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; | 1282 | const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1279 | const res = try codegen.generateLazySymbol( | 1283 | try codegen.generateLazySymbol( |
| 1280 | &coff.base, | 1284 | &coff.base, |
| 1281 | pt, | 1285 | pt, |
| 1282 | src, | 1286 | src, |
| ... | @@ -1286,13 +1290,7 @@ fn updateLazySymbolAtom( | ... | @@ -1286,13 +1290,7 @@ fn updateLazySymbolAtom( |
| 1286 | .none, | 1290 | .none, |
| 1287 | .{ .atom_index = local_sym_index }, | 1291 | .{ .atom_index = local_sym_index }, |
| 1288 | ); | 1292 | ); |
| 1289 | const code = switch (res) { | 1293 | const code = code_buffer.items; |
| 1290 | .ok => code_buffer.items, | ||
| 1291 | .fail => |em| { | ||
| 1292 | log.err("{s}", .{em.msg}); | ||
| 1293 | return error.CodegenFail; | ||
| 1294 | }, | ||
| 1295 | }; | ||
| 1296 | 1294 | ||
| 1297 | const code_len: u32 = @intCast(code.len); | 1295 | const code_len: u32 = @intCast(code.len); |
| 1298 | const symbol = atom.getSymbolPtr(coff); | 1296 | const symbol = atom.getSymbolPtr(coff); |
| ... | @@ -1387,7 +1385,7 @@ fn updateNavCode( | ... | @@ -1387,7 +1385,7 @@ fn updateNavCode( |
| 1387 | nav_index: InternPool.Nav.Index, | 1385 | nav_index: InternPool.Nav.Index, |
| 1388 | code: []u8, | 1386 | code: []u8, |
| 1389 | complex_type: coff_util.ComplexType, | 1387 | complex_type: coff_util.ComplexType, |
| 1390 | ) !void { | 1388 | ) link.File.UpdateNavError!void { |
| 1391 | const zcu = pt.zcu; | 1389 | const zcu = pt.zcu; |
| 1392 | const ip = &zcu.intern_pool; | 1390 | const ip = &zcu.intern_pool; |
| 1393 | const nav = ip.getNav(nav_index); | 1391 | const nav = ip.getNav(nav_index); |
| ... | @@ -1405,18 +1403,21 @@ fn updateNavCode( | ... | @@ -1405,18 +1403,21 @@ fn updateNavCode( |
| 1405 | const atom = coff.getAtom(atom_index); | 1403 | const atom = coff.getAtom(atom_index); |
| 1406 | const sym_index = atom.getSymbolIndex().?; | 1404 | const sym_index = atom.getSymbolIndex().?; |
| 1407 | const sect_index = nav_metadata.section; | 1405 | const sect_index = nav_metadata.section; |
| 1408 | const code_len = @as(u32, @intCast(code.len)); | 1406 | const code_len: u32 = @intCast(code.len); |
| 1409 | 1407 | ||
| 1410 | if (atom.size != 0) { | 1408 | if (atom.size != 0) { |
| 1411 | const sym = atom.getSymbolPtr(coff); | 1409 | const sym = atom.getSymbolPtr(coff); |
| 1412 | try coff.setSymbolName(sym, nav.fqn.toSlice(ip)); | 1410 | try coff.setSymbolName(sym, nav.fqn.toSlice(ip)); |
| 1413 | sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_index + 1)); | 1411 | sym.section_number = @enumFromInt(sect_index + 1); |
| 1414 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; | 1412 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; |
| 1415 | 1413 | ||
| 1416 | const capacity = atom.capacity(coff); | 1414 | const capacity = atom.capacity(coff); |
| 1417 | const need_realloc = code.len > capacity or !required_alignment.check(sym.value); | 1415 | const need_realloc = code.len > capacity or !required_alignment.check(sym.value); |
| 1418 | if (need_realloc) { | 1416 | if (need_realloc) { |
| 1419 | const vaddr = try coff.growAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0)); | 1417 | const vaddr = coff.growAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0)) catch |err| switch (err) { |
| 1418 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1419 | else => |e| return coff.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(e)}), | ||
| 1420 | }; | ||
| 1420 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), sym.value, vaddr }); | 1421 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), sym.value, vaddr }); |
| 1421 | log.debug(" (required alignment 0x{x}", .{required_alignment}); | 1422 | log.debug(" (required alignment 0x{x}", .{required_alignment}); |
| 1422 | 1423 | ||
| ... | @@ -1424,7 +1425,10 @@ fn updateNavCode( | ... | @@ -1424,7 +1425,10 @@ fn updateNavCode( |
| 1424 | sym.value = vaddr; | 1425 | sym.value = vaddr; |
| 1425 | log.debug(" (updating GOT entry)", .{}); | 1426 | log.debug(" (updating GOT entry)", .{}); |
| 1426 | const got_entry_index = coff.got_table.lookup.get(.{ .sym_index = sym_index }).?; | 1427 | const got_entry_index = coff.got_table.lookup.get(.{ .sym_index = sym_index }).?; |
| 1427 | try coff.writeOffsetTableEntry(got_entry_index); | 1428 | coff.writeOffsetTableEntry(got_entry_index) catch |err| switch (err) { |
| 1429 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1430 | else => |e| return coff.base.cgFail(nav_index, "failed to write offset table entry: {s}", .{@errorName(e)}), | ||
| 1431 | }; | ||
| 1428 | coff.markRelocsDirtyByTarget(.{ .sym_index = sym_index }); | 1432 | coff.markRelocsDirtyByTarget(.{ .sym_index = sym_index }); |
| 1429 | } | 1433 | } |
| 1430 | } else if (code_len < atom.size) { | 1434 | } else if (code_len < atom.size) { |
| ... | @@ -1434,26 +1438,34 @@ fn updateNavCode( | ... | @@ -1434,26 +1438,34 @@ fn updateNavCode( |
| 1434 | } else { | 1438 | } else { |
| 1435 | const sym = atom.getSymbolPtr(coff); | 1439 | const sym = atom.getSymbolPtr(coff); |
| 1436 | try coff.setSymbolName(sym, nav.fqn.toSlice(ip)); | 1440 | try coff.setSymbolName(sym, nav.fqn.toSlice(ip)); |
| 1437 | sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_index + 1)); | 1441 | sym.section_number = @enumFromInt(sect_index + 1); |
| 1438 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; | 1442 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; |
| 1439 | 1443 | ||
| 1440 | const vaddr = try coff.allocateAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0)); | 1444 | const vaddr = coff.allocateAtom(atom_index, code_len, @intCast(required_alignment.toByteUnits() orelse 0)) catch |err| switch (err) { |
| 1445 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1446 | else => |e| return coff.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(e)}), | ||
| 1447 | }; | ||
| 1441 | errdefer coff.freeAtom(atom_index); | 1448 | errdefer coff.freeAtom(atom_index); |
| 1442 | log.debug("allocated atom for {} at 0x{x}", .{ nav.fqn.fmt(ip), vaddr }); | 1449 | log.debug("allocated atom for {} at 0x{x}", .{ nav.fqn.fmt(ip), vaddr }); |
| 1443 | coff.getAtomPtr(atom_index).size = code_len; | 1450 | coff.getAtomPtr(atom_index).size = code_len; |
| 1444 | sym.value = vaddr; | 1451 | sym.value = vaddr; |
| 1445 | 1452 | ||
| 1446 | try coff.addGotEntry(.{ .sym_index = sym_index }); | 1453 | coff.addGotEntry(.{ .sym_index = sym_index }) catch |err| switch (err) { |
| 1454 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1455 | else => |e| return coff.base.cgFail(nav_index, "failed to add GOT entry: {s}", .{@errorName(e)}), | ||
| 1456 | }; | ||
| 1447 | } | 1457 | } |
| 1448 | 1458 | ||
| 1449 | try coff.writeAtom(atom_index, code); | 1459 | coff.writeAtom(atom_index, code) catch |err| switch (err) { |
| 1460 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1461 | else => |e| return coff.base.cgFail(nav_index, "failed to write atom: {s}", .{@errorName(e)}), | ||
| 1462 | }; | ||
| 1450 | } | 1463 | } |
| 1451 | 1464 | ||
| 1452 | pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void { | 1465 | pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void { |
| 1453 | if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index); | 1466 | if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index); |
| 1454 | 1467 | ||
| 1455 | const gpa = coff.base.comp.gpa; | 1468 | const gpa = coff.base.comp.gpa; |
| 1456 | log.debug("freeDecl 0x{x}", .{nav_index}); | ||
| 1457 | 1469 | ||
| 1458 | if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| { | 1470 | if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| { |
| 1459 | var kv = const_kv; | 1471 | var kv = const_kv; |
| ... | @@ -1466,7 +1478,7 @@ pub fn updateExports( | ... | @@ -1466,7 +1478,7 @@ pub fn updateExports( |
| 1466 | coff: *Coff, | 1478 | coff: *Coff, |
| 1467 | pt: Zcu.PerThread, | 1479 | pt: Zcu.PerThread, |
| 1468 | exported: Zcu.Exported, | 1480 | exported: Zcu.Exported, |
| 1469 | export_indices: []const u32, | 1481 | export_indices: []const Zcu.Export.Index, |
| 1470 | ) link.File.UpdateExportsError!void { | 1482 | ) link.File.UpdateExportsError!void { |
| 1471 | if (build_options.skip_non_native and builtin.object_format != .coff) { | 1483 | if (build_options.skip_non_native and builtin.object_format != .coff) { |
| 1472 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 1484 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | @@ -1481,7 +1493,7 @@ pub fn updateExports( | ... | @@ -1481,7 +1493,7 @@ pub fn updateExports( |
| 1481 | // Even in the case of LLVM, we need to notice certain exported symbols in order to | 1493 | // Even in the case of LLVM, we need to notice certain exported symbols in order to |
| 1482 | // detect the default subsystem. | 1494 | // detect the default subsystem. |
| 1483 | for (export_indices) |export_idx| { | 1495 | for (export_indices) |export_idx| { |
| 1484 | const exp = zcu.all_exports.items[export_idx]; | 1496 | const exp = export_idx.ptr(zcu); |
| 1485 | const exported_nav_index = switch (exp.exported) { | 1497 | const exported_nav_index = switch (exp.exported) { |
| 1486 | .nav => |nav| nav, | 1498 | .nav => |nav| nav, |
| 1487 | .uav => continue, | 1499 | .uav => continue, |
| ... | @@ -1524,7 +1536,7 @@ pub fn updateExports( | ... | @@ -1524,7 +1536,7 @@ pub fn updateExports( |
| 1524 | break :blk coff.navs.getPtr(nav).?; | 1536 | break :blk coff.navs.getPtr(nav).?; |
| 1525 | }, | 1537 | }, |
| 1526 | .uav => |uav| coff.uavs.getPtr(uav) orelse blk: { | 1538 | .uav => |uav| coff.uavs.getPtr(uav) orelse blk: { |
| 1527 | const first_exp = zcu.all_exports.items[export_indices[0]]; | 1539 | const first_exp = export_indices[0].ptr(zcu); |
| 1528 | const res = try coff.lowerUav(pt, uav, .none, first_exp.src); | 1540 | const res = try coff.lowerUav(pt, uav, .none, first_exp.src); |
| 1529 | switch (res) { | 1541 | switch (res) { |
| 1530 | .mcv => {}, | 1542 | .mcv => {}, |
| ... | @@ -1543,7 +1555,7 @@ pub fn updateExports( | ... | @@ -1543,7 +1555,7 @@ pub fn updateExports( |
| 1543 | const atom = coff.getAtom(atom_index); | 1555 | const atom = coff.getAtom(atom_index); |
| 1544 | 1556 | ||
| 1545 | for (export_indices) |export_idx| { | 1557 | for (export_indices) |export_idx| { |
| 1546 | const exp = zcu.all_exports.items[export_idx]; | 1558 | const exp = export_idx.ptr(zcu); |
| 1547 | log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)}); | 1559 | log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)}); |
| 1548 | 1560 | ||
| 1549 | if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| { | 1561 | if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| { |
| ... | @@ -1671,12 +1683,17 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void { | ... | @@ -1671,12 +1683,17 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void { |
| 1671 | pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 1683 | pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 1672 | const comp = coff.base.comp; | 1684 | const comp = coff.base.comp; |
| 1673 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 1685 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 1686 | const diags = &comp.link_diags; | ||
| 1674 | if (use_lld) { | 1687 | if (use_lld) { |
| 1675 | return coff.linkWithLLD(arena, tid, prog_node); | 1688 | return coff.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) { |
| 1689 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1690 | error.LinkFailure => return error.LinkFailure, | ||
| 1691 | else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), | ||
| 1692 | }; | ||
| 1676 | } | 1693 | } |
| 1677 | switch (comp.config.output_mode) { | 1694 | switch (comp.config.output_mode) { |
| 1678 | .Exe, .Obj => return coff.flushModule(arena, tid, prog_node), | 1695 | .Exe, .Obj => return coff.flushModule(arena, tid, prog_node), |
| 1679 | .Lib => return error.TODOImplementWritingLibFiles, | 1696 | .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}), |
| 1680 | } | 1697 | } |
| 1681 | } | 1698 | } |
| 1682 | 1699 | ||
| ... | @@ -2207,12 +2224,16 @@ fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Director | ... | @@ -2207,12 +2224,16 @@ fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Director |
| 2207 | return null; | 2224 | return null; |
| 2208 | } | 2225 | } |
| 2209 | 2226 | ||
| 2210 | pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 2227 | pub fn flushModule( |
| 2228 | coff: *Coff, | ||
| 2229 | arena: Allocator, | ||
| 2230 | tid: Zcu.PerThread.Id, | ||
| 2231 | prog_node: std.Progress.Node, | ||
| 2232 | ) link.File.FlushError!void { | ||
| 2211 | const tracy = trace(@src()); | 2233 | const tracy = trace(@src()); |
| 2212 | defer tracy.end(); | 2234 | defer tracy.end(); |
| 2213 | 2235 | ||
| 2214 | const comp = coff.base.comp; | 2236 | const comp = coff.base.comp; |
| 2215 | const gpa = comp.gpa; | ||
| 2216 | const diags = &comp.link_diags; | 2237 | const diags = &comp.link_diags; |
| 2217 | 2238 | ||
| 2218 | if (coff.llvm_object) |llvm_object| { | 2239 | if (coff.llvm_object) |llvm_object| { |
| ... | @@ -2223,8 +2244,22 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no | ... | @@ -2223,8 +2244,22 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no |
| 2223 | const sub_prog_node = prog_node.start("COFF Flush", 0); | 2244 | const sub_prog_node = prog_node.start("COFF Flush", 0); |
| 2224 | defer sub_prog_node.end(); | 2245 | defer sub_prog_node.end(); |
| 2225 | 2246 | ||
| 2247 | return flushModuleInner(coff, arena, tid) catch |err| switch (err) { | ||
| 2248 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2249 | error.LinkFailure => return error.LinkFailure, | ||
| 2250 | else => |e| return diags.fail("COFF flush failed: {s}", .{@errorName(e)}), | ||
| 2251 | }; | ||
| 2252 | } | ||
| 2253 | |||
| 2254 | fn flushModuleInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void { | ||
| 2255 | _ = arena; | ||
| 2256 | |||
| 2257 | const comp = coff.base.comp; | ||
| 2258 | const gpa = comp.gpa; | ||
| 2259 | const diags = &comp.link_diags; | ||
| 2260 | |||
| 2226 | const pt: Zcu.PerThread = .activate( | 2261 | const pt: Zcu.PerThread = .activate( |
| 2227 | comp.zcu orelse return error.LinkingWithoutZigSourceUnimplemented, | 2262 | comp.zcu orelse return diags.fail("linking without zig source is not yet implemented", .{}), |
| 2228 | tid, | 2263 | tid, |
| 2229 | ); | 2264 | ); |
| 2230 | defer pt.deactivate(); | 2265 | defer pt.deactivate(); |
| ... | @@ -2232,24 +2267,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no | ... | @@ -2232,24 +2267,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no |
| 2232 | if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| { | 2267 | if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| { |
| 2233 | // Most lazy symbols can be updated on first use, but | 2268 | // Most lazy symbols can be updated on first use, but |
| 2234 | // anyerror needs to wait for everything to be flushed. | 2269 | // anyerror needs to wait for everything to be flushed. |
| 2235 | if (metadata.text_state != .unused) coff.updateLazySymbolAtom( | 2270 | if (metadata.text_state != .unused) try coff.updateLazySymbolAtom( |
| 2236 | pt, | 2271 | pt, |
| 2237 | .{ .kind = .code, .ty = .anyerror_type }, | 2272 | .{ .kind = .code, .ty = .anyerror_type }, |
| 2238 | metadata.text_atom, | 2273 | metadata.text_atom, |
| 2239 | coff.text_section_index.?, | 2274 | coff.text_section_index.?, |
| 2240 | ) catch |err| return switch (err) { | 2275 | ); |
| 2241 | error.CodegenFail => error.FlushFailure, | 2276 | if (metadata.rdata_state != .unused) try coff.updateLazySymbolAtom( |
| 2242 | else => |e| e, | ||
| 2243 | }; | ||
| 2244 | if (metadata.rdata_state != .unused) coff.updateLazySymbolAtom( | ||
| 2245 | pt, | 2277 | pt, |
| 2246 | .{ .kind = .const_data, .ty = .anyerror_type }, | 2278 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 2247 | metadata.rdata_atom, | 2279 | metadata.rdata_atom, |
| 2248 | coff.rdata_section_index.?, | 2280 | coff.rdata_section_index.?, |
| 2249 | ) catch |err| return switch (err) { | 2281 | ); |
| 2250 | error.CodegenFail => error.FlushFailure, | ||
| 2251 | else => |e| e, | ||
| 2252 | }; | ||
| 2253 | } | 2282 | } |
| 2254 | for (coff.lazy_syms.values()) |*metadata| { | 2283 | for (coff.lazy_syms.values()) |*metadata| { |
| 2255 | if (metadata.text_state != .unused) metadata.text_state = .flushed; | 2284 | if (metadata.text_state != .unused) metadata.text_state = .flushed; |
| ... | @@ -2594,7 +2623,7 @@ fn writeBaseRelocations(coff: *Coff) !void { | ... | @@ -2594,7 +2623,7 @@ fn writeBaseRelocations(coff: *Coff) !void { |
| 2594 | const needed_size = @as(u32, @intCast(buffer.items.len)); | 2623 | const needed_size = @as(u32, @intCast(buffer.items.len)); |
| 2595 | try coff.growSection(coff.reloc_section_index.?, needed_size); | 2624 | try coff.growSection(coff.reloc_section_index.?, needed_size); |
| 2596 | 2625 | ||
| 2597 | try coff.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); | 2626 | try coff.pwriteAll(buffer.items, header.pointer_to_raw_data); |
| 2598 | 2627 | ||
| 2599 | coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{ | 2628 | coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{ |
| 2600 | .virtual_address = header.virtual_address, | 2629 | .virtual_address = header.virtual_address, |
| ... | @@ -2727,7 +2756,7 @@ fn writeImportTables(coff: *Coff) !void { | ... | @@ -2727,7 +2756,7 @@ fn writeImportTables(coff: *Coff) !void { |
| 2727 | 2756 | ||
| 2728 | assert(dll_names_offset == needed_size); | 2757 | assert(dll_names_offset == needed_size); |
| 2729 | 2758 | ||
| 2730 | try coff.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); | 2759 | try coff.pwriteAll(buffer.items, header.pointer_to_raw_data); |
| 2731 | 2760 | ||
| 2732 | coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{ | 2761 | coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{ |
| 2733 | .virtual_address = header.virtual_address + iat_size, | 2762 | .virtual_address = header.virtual_address + iat_size, |
| ... | @@ -2744,17 +2773,19 @@ fn writeImportTables(coff: *Coff) !void { | ... | @@ -2744,17 +2773,19 @@ fn writeImportTables(coff: *Coff) !void { |
| 2744 | fn writeStrtab(coff: *Coff) !void { | 2773 | fn writeStrtab(coff: *Coff) !void { |
| 2745 | if (coff.strtab_offset == null) return; | 2774 | if (coff.strtab_offset == null) return; |
| 2746 | 2775 | ||
| 2776 | const comp = coff.base.comp; | ||
| 2777 | const gpa = comp.gpa; | ||
| 2778 | const diags = &comp.link_diags; | ||
| 2747 | const allocated_size = coff.allocatedSize(coff.strtab_offset.?); | 2779 | const allocated_size = coff.allocatedSize(coff.strtab_offset.?); |
| 2748 | const needed_size = @as(u32, @intCast(coff.strtab.buffer.items.len)); | 2780 | const needed_size: u32 = @intCast(coff.strtab.buffer.items.len); |
| 2749 | 2781 | ||
| 2750 | if (needed_size > allocated_size) { | 2782 | if (needed_size > allocated_size) { |
| 2751 | coff.strtab_offset = null; | 2783 | coff.strtab_offset = null; |
| 2752 | coff.strtab_offset = @as(u32, @intCast(coff.findFreeSpace(needed_size, @alignOf(u32)))); | 2784 | coff.strtab_offset = @intCast(coff.findFreeSpace(needed_size, @alignOf(u32))); |
| 2753 | } | 2785 | } |
| 2754 | 2786 | ||
| 2755 | log.debug("writing strtab from 0x{x} to 0x{x}", .{ coff.strtab_offset.?, coff.strtab_offset.? + needed_size }); | 2787 | log.debug("writing strtab from 0x{x} to 0x{x}", .{ coff.strtab_offset.?, coff.strtab_offset.? + needed_size }); |
| 2756 | 2788 | ||
| 2757 | const gpa = coff.base.comp.gpa; | ||
| 2758 | var buffer = std.ArrayList(u8).init(gpa); | 2789 | var buffer = std.ArrayList(u8).init(gpa); |
| 2759 | defer buffer.deinit(); | 2790 | defer buffer.deinit(); |
| 2760 | try buffer.ensureTotalCapacityPrecise(needed_size); | 2791 | try buffer.ensureTotalCapacityPrecise(needed_size); |
| ... | @@ -2763,17 +2794,19 @@ fn writeStrtab(coff: *Coff) !void { | ... | @@ -2763,17 +2794,19 @@ fn writeStrtab(coff: *Coff) !void { |
| 2763 | // we write the length of the strtab to a temporary buffer that goes to file. | 2794 | // 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); | 2795 | mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(coff.strtab.buffer.items.len)), .little); |
| 2765 | 2796 | ||
| 2766 | try coff.base.file.?.pwriteAll(buffer.items, coff.strtab_offset.?); | 2797 | coff.pwriteAll(buffer.items, coff.strtab_offset.?) catch |err| { |
| 2798 | return diags.fail("failed to write: {s}", .{@errorName(err)}); | ||
| 2799 | }; | ||
| 2767 | } | 2800 | } |
| 2768 | 2801 | ||
| 2769 | fn writeSectionHeaders(coff: *Coff) !void { | 2802 | fn writeSectionHeaders(coff: *Coff) !void { |
| 2770 | const offset = coff.getSectionHeadersOffset(); | 2803 | const offset = coff.getSectionHeadersOffset(); |
| 2771 | try coff.base.file.?.pwriteAll(mem.sliceAsBytes(coff.sections.items(.header)), offset); | 2804 | try coff.pwriteAll(mem.sliceAsBytes(coff.sections.items(.header)), offset); |
| 2772 | } | 2805 | } |
| 2773 | 2806 | ||
| 2774 | fn writeDataDirectoriesHeaders(coff: *Coff) !void { | 2807 | fn writeDataDirectoriesHeaders(coff: *Coff) !void { |
| 2775 | const offset = coff.getDataDirectoryHeadersOffset(); | 2808 | const offset = coff.getDataDirectoryHeadersOffset(); |
| 2776 | try coff.base.file.?.pwriteAll(mem.sliceAsBytes(&coff.data_directories), offset); | 2809 | try coff.pwriteAll(mem.sliceAsBytes(&coff.data_directories), offset); |
| 2777 | } | 2810 | } |
| 2778 | 2811 | ||
| 2779 | fn writeHeader(coff: *Coff) !void { | 2812 | fn writeHeader(coff: *Coff) !void { |
| ... | @@ -2913,7 +2946,7 @@ fn writeHeader(coff: *Coff) !void { | ... | @@ -2913,7 +2946,7 @@ fn writeHeader(coff: *Coff) !void { |
| 2913 | }, | 2946 | }, |
| 2914 | } | 2947 | } |
| 2915 | 2948 | ||
| 2916 | try coff.base.file.?.pwriteAll(buffer.items, 0); | 2949 | try coff.pwriteAll(buffer.items, 0); |
| 2917 | } | 2950 | } |
| 2918 | 2951 | ||
| 2919 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | 2952 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| ... | @@ -3710,6 +3743,14 @@ const ImportTable = struct { | ... | @@ -3710,6 +3743,14 @@ const ImportTable = struct { |
| 3710 | const ImportIndex = u32; | 3743 | const ImportIndex = u32; |
| 3711 | }; | 3744 | }; |
| 3712 | 3745 | ||
| 3746 | fn pwriteAll(coff: *Coff, bytes: []const u8, offset: u64) error{LinkFailure}!void { | ||
| 3747 | const comp = coff.base.comp; | ||
| 3748 | const diags = &comp.link_diags; | ||
| 3749 | coff.base.file.?.pwriteAll(bytes, offset) catch |err| { | ||
| 3750 | return diags.fail("failed to write: {s}", .{@errorName(err)}); | ||
| 3751 | }; | ||
| 3752 | } | ||
| 3753 | |||
| 3713 | const Coff = @This(); | 3754 | const Coff = @This(); |
| 3714 | 3755 | ||
| 3715 | const std = @import("std"); | 3756 | const std = @import("std"); |
src/link/Dwarf.zig+35-15| ... | @@ -21,7 +21,6 @@ debug_rnglists: DebugRngLists, | ... | @@ -21,7 +21,6 @@ debug_rnglists: DebugRngLists, |
| 21 | debug_str: StringSection, | 21 | debug_str: StringSection, |
| 22 | 22 | ||
| 23 | pub const UpdateError = error{ | 23 | pub const UpdateError = error{ |
| 24 | CodegenFail, | ||
| 25 | ReinterpretDeclRef, | 24 | ReinterpretDeclRef, |
| 26 | Unimplemented, | 25 | Unimplemented, |
| 27 | OutOfMemory, | 26 | OutOfMemory, |
| ... | @@ -451,7 +450,6 @@ pub const Section = struct { | ... | @@ -451,7 +450,6 @@ pub const Section = struct { |
| 451 | const zo = elf_file.zigObjectPtr().?; | 450 | const zo = elf_file.zigObjectPtr().?; |
| 452 | const atom = zo.symbol(sec.index).atom(elf_file).?; | 451 | const atom = zo.symbol(sec.index).atom(elf_file).?; |
| 453 | if (atom.prevAtom(elf_file)) |_| { | 452 | if (atom.prevAtom(elf_file)) |_| { |
| 454 | // FIXME:JK trimming/shrinking has to be reworked on ZigObject/Elf level | ||
| 455 | atom.value += len; | 453 | atom.value += len; |
| 456 | } else { | 454 | } else { |
| 457 | const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index]; | 455 | const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index]; |
| ... | @@ -600,12 +598,13 @@ const Unit = struct { | ... | @@ -600,12 +598,13 @@ const Unit = struct { |
| 600 | 598 | ||
| 601 | fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void { | 599 | fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void { |
| 602 | if (unit.off == new_off) return; | 600 | if (unit.off == new_off) return; |
| 603 | if (try dwarf.getFile().?.copyRangeAll( | 601 | const n = try dwarf.getFile().?.copyRangeAll( |
| 604 | sec.off(dwarf) + unit.off, | 602 | sec.off(dwarf) + unit.off, |
| 605 | dwarf.getFile().?, | 603 | dwarf.getFile().?, |
| 606 | sec.off(dwarf) + new_off, | 604 | sec.off(dwarf) + new_off, |
| 607 | unit.len, | 605 | unit.len, |
| 608 | ) != unit.len) return error.InputOutput; | 606 | ); |
| 607 | if (n != unit.len) return error.InputOutput; | ||
| 609 | unit.off = new_off; | 608 | unit.off = new_off; |
| 610 | } | 609 | } |
| 611 | 610 | ||
| ... | @@ -1891,19 +1890,16 @@ pub const WipNav = struct { | ... | @@ -1891,19 +1890,16 @@ pub const WipNav = struct { |
| 1891 | const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0; | 1890 | const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0; |
| 1892 | try uleb128(diw, bytes); | 1891 | try uleb128(diw, bytes); |
| 1893 | if (bytes == 0) return; | 1892 | if (bytes == 0) return; |
| 1894 | var dim = wip_nav.debug_info.toManaged(wip_nav.dwarf.gpa); | 1893 | const old_len = wip_nav.debug_info.items.len; |
| 1895 | defer wip_nav.debug_info = dim.moveToUnmanaged(); | 1894 | try codegen.generateSymbol( |
| 1896 | switch (try codegen.generateSymbol( | ||
| 1897 | wip_nav.dwarf.bin_file, | 1895 | wip_nav.dwarf.bin_file, |
| 1898 | wip_nav.pt, | 1896 | wip_nav.pt, |
| 1899 | src_loc, | 1897 | src_loc, |
| 1900 | val, | 1898 | val, |
| 1901 | &dim, | 1899 | &wip_nav.debug_info, |
| 1902 | .{ .debug_output = .{ .dwarf = wip_nav } }, | 1900 | .{ .debug_output = .{ .dwarf = wip_nav } }, |
| 1903 | )) { | 1901 | ); |
| 1904 | .ok => assert(dim.items.len == wip_nav.debug_info.items.len + bytes), | 1902 | assert(old_len + bytes == wip_nav.debug_info.items.len); |
| 1905 | .fail => unreachable, | ||
| 1906 | } | ||
| 1907 | } | 1903 | } |
| 1908 | 1904 | ||
| 1909 | const AbbrevCodeForForm = struct { | 1905 | const AbbrevCodeForForm = struct { |
| ... | @@ -2278,7 +2274,7 @@ pub fn deinit(dwarf: *Dwarf) void { | ... | @@ -2278,7 +2274,7 @@ pub fn deinit(dwarf: *Dwarf) void { |
| 2278 | dwarf.* = undefined; | 2274 | dwarf.* = undefined; |
| 2279 | } | 2275 | } |
| 2280 | 2276 | ||
| 2281 | fn getUnit(dwarf: *Dwarf, mod: *Module) UpdateError!Unit.Index { | 2277 | fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index { |
| 2282 | const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); | 2278 | const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); |
| 2283 | const unit: Unit.Index = @enumFromInt(mod_gop.index); | 2279 | const unit: Unit.Index = @enumFromInt(mod_gop.index); |
| 2284 | if (!mod_gop.found_existing) { | 2280 | if (!mod_gop.found_existing) { |
| ... | @@ -2338,7 +2334,24 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo { | ... | @@ -2338,7 +2334,24 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo { |
| 2338 | return &dwarf.mods.values()[@intFromEnum(unit)]; | 2334 | return &dwarf.mods.values()[@intFromEnum(unit)]; |
| 2339 | } | 2335 | } |
| 2340 | 2336 | ||
| 2341 | pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, sym_index: u32) UpdateError!?WipNav { | 2337 | pub fn initWipNav( |
| 2338 | dwarf: *Dwarf, | ||
| 2339 | pt: Zcu.PerThread, | ||
| 2340 | nav_index: InternPool.Nav.Index, | ||
| 2341 | sym_index: u32, | ||
| 2342 | ) error{ OutOfMemory, CodegenFail }!?WipNav { | ||
| 2343 | return initWipNavInner(dwarf, pt, nav_index, sym_index) catch |err| switch (err) { | ||
| 2344 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2345 | else => |e| return pt.zcu.codegenFail(nav_index, "failed to init dwarf: {s}", .{@errorName(e)}), | ||
| 2346 | }; | ||
| 2347 | } | ||
| 2348 | |||
| 2349 | fn initWipNavInner( | ||
| 2350 | dwarf: *Dwarf, | ||
| 2351 | pt: Zcu.PerThread, | ||
| 2352 | nav_index: InternPool.Nav.Index, | ||
| 2353 | sym_index: u32, | ||
| 2354 | ) !?WipNav { | ||
| 2342 | const zcu = pt.zcu; | 2355 | const zcu = pt.zcu; |
| 2343 | const ip = &zcu.intern_pool; | 2356 | const ip = &zcu.intern_pool; |
| 2344 | 2357 | ||
| ... | @@ -2667,7 +2680,14 @@ pub fn finishWipNav( | ... | @@ -2667,7 +2680,14 @@ pub fn finishWipNav( |
| 2667 | try wip_nav.updateLazy(zcu.navSrcLoc(nav_index)); | 2680 | try wip_nav.updateLazy(zcu.navSrcLoc(nav_index)); |
| 2668 | } | 2681 | } |
| 2669 | 2682 | ||
| 2670 | pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateError!void { | 2683 | pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, CodegenFail }!void { |
| 2684 | return updateComptimeNavInner(dwarf, pt, nav_index) catch |err| switch (err) { | ||
| 2685 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2686 | else => |e| return pt.zcu.codegenFail(nav_index, "failed to update dwarf: {s}", .{@errorName(e)}), | ||
| 2687 | }; | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | ||
| 2671 | const zcu = pt.zcu; | 2691 | const zcu = pt.zcu; |
| 2672 | const ip = &zcu.intern_pool; | 2692 | const ip = &zcu.intern_pool; |
| 2673 | const nav_src_loc = zcu.navSrcLoc(nav_index); | 2693 | const nav_src_loc = zcu.navSrcLoc(nav_index); |
src/link/Elf.zig+124-69| ... | @@ -795,9 +795,15 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { | ... | @@ -795,9 +795,15 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { |
| 795 | } | 795 | } |
| 796 | 796 | ||
| 797 | pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 797 | pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 798 | const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; | 798 | const comp = self.base.comp; |
| 799 | const use_lld = build_options.have_llvm and comp.config.use_lld; | ||
| 800 | const diags = &comp.link_diags; | ||
| 799 | if (use_lld) { | 801 | if (use_lld) { |
| 800 | return self.linkWithLLD(arena, tid, prog_node); | 802 | return self.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) { |
| 803 | error.OutOfMemory => return error.OutOfMemory, | ||
| 804 | error.LinkFailure => return error.LinkFailure, | ||
| 805 | else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), | ||
| 806 | }; | ||
| 801 | } | 807 | } |
| 802 | try self.flushModule(arena, tid, prog_node); | 808 | try self.flushModule(arena, tid, prog_node); |
| 803 | } | 809 | } |
| ... | @@ -807,7 +813,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -807,7 +813,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 807 | defer tracy.end(); | 813 | defer tracy.end(); |
| 808 | 814 | ||
| 809 | const comp = self.base.comp; | 815 | const comp = self.base.comp; |
| 810 | const gpa = comp.gpa; | ||
| 811 | const diags = &comp.link_diags; | 816 | const diags = &comp.link_diags; |
| 812 | 817 | ||
| 813 | if (self.llvm_object) |llvm_object| { | 818 | if (self.llvm_object) |llvm_object| { |
| ... | @@ -821,6 +826,18 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -821,6 +826,18 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 821 | const sub_prog_node = prog_node.start("ELF Flush", 0); | 826 | const sub_prog_node = prog_node.start("ELF Flush", 0); |
| 822 | defer sub_prog_node.end(); | 827 | defer sub_prog_node.end(); |
| 823 | 828 | ||
| 829 | return flushModuleInner(self, arena, tid) catch |err| switch (err) { | ||
| 830 | error.OutOfMemory => return error.OutOfMemory, | ||
| 831 | error.LinkFailure => return error.LinkFailure, | ||
| 832 | else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}), | ||
| 833 | }; | ||
| 834 | } | ||
| 835 | |||
| 836 | fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { | ||
| 837 | const comp = self.base.comp; | ||
| 838 | const gpa = comp.gpa; | ||
| 839 | const diags = &comp.link_diags; | ||
| 840 | |||
| 824 | const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ | 841 | const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ |
| 825 | .root_dir = self.base.emit.root_dir, | 842 | .root_dir = self.base.emit.root_dir, |
| 826 | .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| | 843 | .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| |
| ... | @@ -842,12 +859,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -842,12 +859,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 842 | .Exe => {}, | 859 | .Exe => {}, |
| 843 | } | 860 | } |
| 844 | 861 | ||
| 845 | if (diags.hasErrors()) return error.FlushFailure; | 862 | if (diags.hasErrors()) return error.LinkFailure; |
| 846 | 863 | ||
| 847 | // If we haven't already, create a linker-generated input file comprising of | 864 | // If we haven't already, create a linker-generated input file comprising of |
| 848 | // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. | 865 | // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. |
| 849 | if (self.linker_defined_index == null) { | 866 | if (self.linker_defined_index == null) { |
| 850 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); | 867 | const index: File.Index = @intCast(try self.files.addOne(gpa)); |
| 851 | self.files.set(index, .{ .linker_defined = .{ .index = index } }); | 868 | self.files.set(index, .{ .linker_defined = .{ .index = index } }); |
| 852 | self.linker_defined_index = index; | 869 | self.linker_defined_index = index; |
| 853 | const object = self.linkerDefinedPtr().?; | 870 | const object = self.linkerDefinedPtr().?; |
| ... | @@ -878,7 +895,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -878,7 +895,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 878 | } | 895 | } |
| 879 | 896 | ||
| 880 | self.checkDuplicates() catch |err| switch (err) { | 897 | self.checkDuplicates() catch |err| switch (err) { |
| 881 | error.HasDuplicates => return error.FlushFailure, | 898 | error.HasDuplicates => return error.LinkFailure, |
| 882 | else => |e| return e, | 899 | else => |e| return e, |
| 883 | }; | 900 | }; |
| 884 | 901 | ||
| ... | @@ -956,14 +973,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -956,14 +973,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 956 | error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, | 973 | error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, |
| 957 | error.UnsupportedCpuArch => { | 974 | error.UnsupportedCpuArch => { |
| 958 | try self.reportUnsupportedCpuArch(); | 975 | try self.reportUnsupportedCpuArch(); |
| 959 | return error.FlushFailure; | 976 | return error.LinkFailure; |
| 960 | }, | 977 | }, |
| 961 | else => |e| return e, | 978 | else => |e| return e, |
| 962 | }; | 979 | }; |
| 963 | try self.base.file.?.pwriteAll(code, file_offset); | 980 | try self.pwriteAll(code, file_offset); |
| 964 | } | 981 | } |
| 965 | 982 | ||
| 966 | if (has_reloc_errors) return error.FlushFailure; | 983 | if (has_reloc_errors) return error.LinkFailure; |
| 967 | } | 984 | } |
| 968 | 985 | ||
| 969 | try self.writePhdrTable(); | 986 | try self.writePhdrTable(); |
| ... | @@ -972,10 +989,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -972,10 +989,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 972 | try self.writeMergeSections(); | 989 | try self.writeMergeSections(); |
| 973 | 990 | ||
| 974 | self.writeSyntheticSections() catch |err| switch (err) { | 991 | self.writeSyntheticSections() catch |err| switch (err) { |
| 975 | error.RelocFailure => return error.FlushFailure, | 992 | error.RelocFailure => return error.LinkFailure, |
| 976 | error.UnsupportedCpuArch => { | 993 | error.UnsupportedCpuArch => { |
| 977 | try self.reportUnsupportedCpuArch(); | 994 | try self.reportUnsupportedCpuArch(); |
| 978 | return error.FlushFailure; | 995 | return error.LinkFailure; |
| 979 | }, | 996 | }, |
| 980 | else => |e| return e, | 997 | else => |e| return e, |
| 981 | }; | 998 | }; |
| ... | @@ -989,7 +1006,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod | ... | @@ -989,7 +1006,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 989 | try self.writeElfHeader(); | 1006 | try self.writeElfHeader(); |
| 990 | } | 1007 | } |
| 991 | 1008 | ||
| 992 | if (diags.hasErrors()) return error.FlushFailure; | 1009 | if (diags.hasErrors()) return error.LinkFailure; |
| 993 | } | 1010 | } |
| 994 | 1011 | ||
| 995 | fn dumpArgvInit(self: *Elf, arena: Allocator) !void { | 1012 | fn dumpArgvInit(self: *Elf, arena: Allocator) !void { |
| ... | @@ -1389,7 +1406,7 @@ fn scanRelocs(self: *Elf) !void { | ... | @@ -1389,7 +1406,7 @@ fn scanRelocs(self: *Elf) !void { |
| 1389 | error.RelaxFailure => unreachable, | 1406 | error.RelaxFailure => unreachable, |
| 1390 | error.UnsupportedCpuArch => { | 1407 | error.UnsupportedCpuArch => { |
| 1391 | try self.reportUnsupportedCpuArch(); | 1408 | try self.reportUnsupportedCpuArch(); |
| 1392 | return error.FlushFailure; | 1409 | return error.LinkFailure; |
| 1393 | }, | 1410 | }, |
| 1394 | error.RelocFailure => has_reloc_errors = true, | 1411 | error.RelocFailure => has_reloc_errors = true, |
| 1395 | else => |e| return e, | 1412 | else => |e| return e, |
| ... | @@ -1400,7 +1417,7 @@ fn scanRelocs(self: *Elf) !void { | ... | @@ -1400,7 +1417,7 @@ fn scanRelocs(self: *Elf) !void { |
| 1400 | error.RelaxFailure => unreachable, | 1417 | error.RelaxFailure => unreachable, |
| 1401 | error.UnsupportedCpuArch => { | 1418 | error.UnsupportedCpuArch => { |
| 1402 | try self.reportUnsupportedCpuArch(); | 1419 | try self.reportUnsupportedCpuArch(); |
| 1403 | return error.FlushFailure; | 1420 | return error.LinkFailure; |
| 1404 | }, | 1421 | }, |
| 1405 | error.RelocFailure => has_reloc_errors = true, | 1422 | error.RelocFailure => has_reloc_errors = true, |
| 1406 | else => |e| return e, | 1423 | else => |e| return e, |
| ... | @@ -1409,7 +1426,7 @@ fn scanRelocs(self: *Elf) !void { | ... | @@ -1409,7 +1426,7 @@ fn scanRelocs(self: *Elf) !void { |
| 1409 | 1426 | ||
| 1410 | try self.reportUndefinedSymbols(&undefs); | 1427 | try self.reportUndefinedSymbols(&undefs); |
| 1411 | 1428 | ||
| 1412 | if (has_reloc_errors) return error.FlushFailure; | 1429 | if (has_reloc_errors) return error.LinkFailure; |
| 1413 | 1430 | ||
| 1414 | if (self.zigObjectPtr()) |zo| { | 1431 | if (self.zigObjectPtr()) |zo| { |
| 1415 | try zo.asFile().createSymbolIndirection(self); | 1432 | try zo.asFile().createSymbolIndirection(self); |
| ... | @@ -2117,7 +2134,7 @@ pub fn writeShdrTable(self: *Elf) !void { | ... | @@ -2117,7 +2134,7 @@ pub fn writeShdrTable(self: *Elf) !void { |
| 2117 | mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); | 2134 | mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); |
| 2118 | } | 2135 | } |
| 2119 | } | 2136 | } |
| 2120 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); | 2137 | try self.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); |
| 2121 | }, | 2138 | }, |
| 2122 | .p64 => { | 2139 | .p64 => { |
| 2123 | const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len); | 2140 | const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len); |
| ... | @@ -2130,7 +2147,7 @@ pub fn writeShdrTable(self: *Elf) !void { | ... | @@ -2130,7 +2147,7 @@ pub fn writeShdrTable(self: *Elf) !void { |
| 2130 | mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); | 2147 | mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); |
| 2131 | } | 2148 | } |
| 2132 | } | 2149 | } |
| 2133 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); | 2150 | try self.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); |
| 2134 | }, | 2151 | }, |
| 2135 | } | 2152 | } |
| 2136 | } | 2153 | } |
| ... | @@ -2157,7 +2174,7 @@ fn writePhdrTable(self: *Elf) !void { | ... | @@ -2157,7 +2174,7 @@ fn writePhdrTable(self: *Elf) !void { |
| 2157 | mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); | 2174 | mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); |
| 2158 | } | 2175 | } |
| 2159 | } | 2176 | } |
| 2160 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); | 2177 | try self.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); |
| 2161 | }, | 2178 | }, |
| 2162 | .p64 => { | 2179 | .p64 => { |
| 2163 | const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len); | 2180 | const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len); |
| ... | @@ -2169,7 +2186,7 @@ fn writePhdrTable(self: *Elf) !void { | ... | @@ -2169,7 +2186,7 @@ fn writePhdrTable(self: *Elf) !void { |
| 2169 | mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); | 2186 | mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); |
| 2170 | } | 2187 | } |
| 2171 | } | 2188 | } |
| 2172 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); | 2189 | try self.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); |
| 2173 | }, | 2190 | }, |
| 2174 | } | 2191 | } |
| 2175 | } | 2192 | } |
| ... | @@ -2319,7 +2336,7 @@ pub fn writeElfHeader(self: *Elf) !void { | ... | @@ -2319,7 +2336,7 @@ pub fn writeElfHeader(self: *Elf) !void { |
| 2319 | 2336 | ||
| 2320 | assert(index == e_ehsize); | 2337 | assert(index == e_ehsize); |
| 2321 | 2338 | ||
| 2322 | try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); | 2339 | try self.pwriteAll(hdr_buf[0..index], 0); |
| 2323 | } | 2340 | } |
| 2324 | 2341 | ||
| 2325 | pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void { | 2342 | pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void { |
| ... | @@ -2327,7 +2344,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void { | ... | @@ -2327,7 +2344,13 @@ pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void { |
| 2327 | return self.zigObjectPtr().?.freeNav(self, nav); | 2344 | return self.zigObjectPtr().?.freeNav(self, nav); |
| 2328 | } | 2345 | } |
| 2329 | 2346 | ||
| 2330 | pub fn updateFunc(self: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 2347 | pub fn updateFunc( |
| 2348 | self: *Elf, | ||
| 2349 | pt: Zcu.PerThread, | ||
| 2350 | func_index: InternPool.Index, | ||
| 2351 | air: Air, | ||
| 2352 | liveness: Liveness, | ||
| 2353 | ) link.File.UpdateNavError!void { | ||
| 2331 | if (build_options.skip_non_native and builtin.object_format != .elf) { | 2354 | 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"); | 2355 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 2333 | } | 2356 | } |
| ... | @@ -2351,19 +2374,32 @@ pub fn updateContainerType( | ... | @@ -2351,19 +2374,32 @@ pub fn updateContainerType( |
| 2351 | self: *Elf, | 2374 | self: *Elf, |
| 2352 | pt: Zcu.PerThread, | 2375 | pt: Zcu.PerThread, |
| 2353 | ty: InternPool.Index, | 2376 | ty: InternPool.Index, |
| 2354 | ) link.File.UpdateNavError!void { | 2377 | ) link.File.UpdateContainerTypeError!void { |
| 2355 | if (build_options.skip_non_native and builtin.object_format != .elf) { | 2378 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2356 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 2379 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 2357 | } | 2380 | } |
| 2358 | if (self.llvm_object) |_| return; | 2381 | if (self.llvm_object) |_| return; |
| 2359 | return self.zigObjectPtr().?.updateContainerType(pt, ty); | 2382 | const zcu = pt.zcu; |
| 2383 | const gpa = zcu.gpa; | ||
| 2384 | return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) { | ||
| 2385 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2386 | else => |e| { | ||
| 2387 | try zcu.failed_types.putNoClobber(gpa, ty, try Zcu.ErrorMsg.create( | ||
| 2388 | gpa, | ||
| 2389 | zcu.typeSrcLoc(ty), | ||
| 2390 | "failed to update container type: {s}", | ||
| 2391 | .{@errorName(e)}, | ||
| 2392 | )); | ||
| 2393 | return error.TypeFailureReported; | ||
| 2394 | }, | ||
| 2395 | }; | ||
| 2360 | } | 2396 | } |
| 2361 | 2397 | ||
| 2362 | pub fn updateExports( | 2398 | pub fn updateExports( |
| 2363 | self: *Elf, | 2399 | self: *Elf, |
| 2364 | pt: Zcu.PerThread, | 2400 | pt: Zcu.PerThread, |
| 2365 | exported: Zcu.Exported, | 2401 | exported: Zcu.Exported, |
| 2366 | export_indices: []const u32, | 2402 | export_indices: []const Zcu.Export.Index, |
| 2367 | ) link.File.UpdateExportsError!void { | 2403 | ) link.File.UpdateExportsError!void { |
| 2368 | if (build_options.skip_non_native and builtin.object_format != .elf) { | 2404 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2369 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 2405 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | @@ -2441,7 +2477,7 @@ pub fn resolveMergeSections(self: *Elf) !void { | ... | @@ -2441,7 +2477,7 @@ pub fn resolveMergeSections(self: *Elf) !void { |
| 2441 | }; | 2477 | }; |
| 2442 | } | 2478 | } |
| 2443 | 2479 | ||
| 2444 | if (has_errors) return error.FlushFailure; | 2480 | if (has_errors) return error.LinkFailure; |
| 2445 | 2481 | ||
| 2446 | for (self.objects.items) |index| { | 2482 | for (self.objects.items) |index| { |
| 2447 | const object = self.file(index).?.object; | 2483 | const object = self.file(index).?.object; |
| ... | @@ -2491,8 +2527,8 @@ pub fn writeMergeSections(self: *Elf) !void { | ... | @@ -2491,8 +2527,8 @@ pub fn writeMergeSections(self: *Elf) !void { |
| 2491 | 2527 | ||
| 2492 | for (self.merge_sections.items) |*msec| { | 2528 | for (self.merge_sections.items) |*msec| { |
| 2493 | const shdr = self.sections.items(.shdr)[msec.output_section_index]; | 2529 | const shdr = self.sections.items(.shdr)[msec.output_section_index]; |
| 2494 | const fileoff = math.cast(usize, msec.value + shdr.sh_offset) orelse return error.Overflow; | 2530 | const fileoff = try self.cast(usize, msec.value + shdr.sh_offset); |
| 2495 | const size = math.cast(usize, msec.size) orelse return error.Overflow; | 2531 | const size = try self.cast(usize, msec.size); |
| 2496 | try buffer.ensureTotalCapacity(size); | 2532 | try buffer.ensureTotalCapacity(size); |
| 2497 | buffer.appendNTimesAssumeCapacity(0, size); | 2533 | buffer.appendNTimesAssumeCapacity(0, size); |
| 2498 | 2534 | ||
| ... | @@ -2500,11 +2536,11 @@ pub fn writeMergeSections(self: *Elf) !void { | ... | @@ -2500,11 +2536,11 @@ pub fn writeMergeSections(self: *Elf) !void { |
| 2500 | const msub = msec.mergeSubsection(msub_index); | 2536 | const msub = msec.mergeSubsection(msub_index); |
| 2501 | assert(msub.alive); | 2537 | assert(msub.alive); |
| 2502 | const string = msub.getString(self); | 2538 | const string = msub.getString(self); |
| 2503 | const off = math.cast(usize, msub.value) orelse return error.Overflow; | 2539 | const off = try self.cast(usize, msub.value); |
| 2504 | @memcpy(buffer.items[off..][0..string.len], string); | 2540 | @memcpy(buffer.items[off..][0..string.len], string); |
| 2505 | } | 2541 | } |
| 2506 | 2542 | ||
| 2507 | try self.base.file.?.pwriteAll(buffer.items, fileoff); | 2543 | try self.pwriteAll(buffer.items, fileoff); |
| 2508 | buffer.clearRetainingCapacity(); | 2544 | buffer.clearRetainingCapacity(); |
| 2509 | } | 2545 | } |
| 2510 | } | 2546 | } |
| ... | @@ -3121,9 +3157,6 @@ pub fn sortShdrs( | ... | @@ -3121,9 +3157,6 @@ pub fn sortShdrs( |
| 3121 | fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index; | 3157 | fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index; |
| 3122 | } | 3158 | } |
| 3123 | if (shdr.sh_type == elf.SHT_RELA) { | 3159 | if (shdr.sh_type == elf.SHT_RELA) { |
| 3124 | // FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections | ||
| 3125 | // to point at symtab | ||
| 3126 | // shdr.sh_link = backlinks[shdr.sh_link]; | ||
| 3127 | shdr.sh_link = section_indexes.symtab.?; | 3160 | shdr.sh_link = section_indexes.symtab.?; |
| 3128 | shdr.sh_info = backlinks[shdr.sh_info]; | 3161 | shdr.sh_info = backlinks[shdr.sh_info]; |
| 3129 | } | 3162 | } |
| ... | @@ -3211,7 +3244,7 @@ fn updateSectionSizes(self: *Elf) !void { | ... | @@ -3211,7 +3244,7 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3211 | atom_list.dirty = false; | 3244 | atom_list.dirty = false; |
| 3212 | } | 3245 | } |
| 3213 | 3246 | ||
| 3214 | // FIXME:JK this will hopefully not be needed once we create a link from Atom/Thunk to AtomList. | 3247 | // This might not be needed if there was a link from Atom/Thunk to AtomList. |
| 3215 | for (self.thunks.items) |*th| { | 3248 | for (self.thunks.items) |*th| { |
| 3216 | th.value += slice.items(.atom_list_2)[th.output_section_index].value; | 3249 | th.value += slice.items(.atom_list_2)[th.output_section_index].value; |
| 3217 | } | 3250 | } |
| ... | @@ -3297,7 +3330,6 @@ fn updateSectionSizes(self: *Elf) !void { | ... | @@ -3297,7 +3330,6 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3297 | self.updateShStrtabSize(); | 3330 | self.updateShStrtabSize(); |
| 3298 | } | 3331 | } |
| 3299 | 3332 | ||
| 3300 | // FIXME:JK this is very much obsolete, remove! | ||
| 3301 | pub fn updateShStrtabSize(self: *Elf) void { | 3333 | pub fn updateShStrtabSize(self: *Elf) void { |
| 3302 | if (self.section_indexes.shstrtab) |index| { | 3334 | if (self.section_indexes.shstrtab) |index| { |
| 3303 | self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len; | 3335 | self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len; |
| ... | @@ -3362,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void { | ... | @@ -3362,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void { |
| 3362 | // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op | 3394 | // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op |
| 3363 | var err = try diags.addErrorWithNotes(1); | 3395 | var err = try diags.addErrorWithNotes(1); |
| 3364 | try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{}); | 3396 | try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{}); |
| 3365 | try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space }); | 3397 | err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space }); |
| 3366 | } | 3398 | } |
| 3367 | 3399 | ||
| 3368 | phdr_table_load.p_filesz = needed_size + ehsize; | 3400 | phdr_table_load.p_filesz = needed_size + ehsize; |
| ... | @@ -3658,7 +3690,7 @@ fn writeAtoms(self: *Elf) !void { | ... | @@ -3658,7 +3690,7 @@ fn writeAtoms(self: *Elf) !void { |
| 3658 | atom_list.write(&buffer, &undefs, self) catch |err| switch (err) { | 3690 | atom_list.write(&buffer, &undefs, self) catch |err| switch (err) { |
| 3659 | error.UnsupportedCpuArch => { | 3691 | error.UnsupportedCpuArch => { |
| 3660 | try self.reportUnsupportedCpuArch(); | 3692 | try self.reportUnsupportedCpuArch(); |
| 3661 | return error.FlushFailure; | 3693 | return error.LinkFailure; |
| 3662 | }, | 3694 | }, |
| 3663 | error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, | 3695 | error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, |
| 3664 | else => |e| return e, | 3696 | else => |e| return e, |
| ... | @@ -3666,7 +3698,7 @@ fn writeAtoms(self: *Elf) !void { | ... | @@ -3666,7 +3698,7 @@ fn writeAtoms(self: *Elf) !void { |
| 3666 | } | 3698 | } |
| 3667 | 3699 | ||
| 3668 | try self.reportUndefinedSymbols(&undefs); | 3700 | try self.reportUndefinedSymbols(&undefs); |
| 3669 | if (has_reloc_errors) return error.FlushFailure; | 3701 | if (has_reloc_errors) return error.LinkFailure; |
| 3670 | 3702 | ||
| 3671 | if (self.requiresThunks()) { | 3703 | if (self.requiresThunks()) { |
| 3672 | for (self.thunks.items) |th| { | 3704 | for (self.thunks.items) |th| { |
| ... | @@ -3676,7 +3708,7 @@ fn writeAtoms(self: *Elf) !void { | ... | @@ -3676,7 +3708,7 @@ fn writeAtoms(self: *Elf) !void { |
| 3676 | const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset; | 3708 | const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset; |
| 3677 | try th.write(self, buffer.writer()); | 3709 | try th.write(self, buffer.writer()); |
| 3678 | assert(buffer.items.len == thunk_size); | 3710 | assert(buffer.items.len == thunk_size); |
| 3679 | try self.base.file.?.pwriteAll(buffer.items, offset); | 3711 | try self.pwriteAll(buffer.items, offset); |
| 3680 | buffer.clearRetainingCapacity(); | 3712 | buffer.clearRetainingCapacity(); |
| 3681 | } | 3713 | } |
| 3682 | } | 3714 | } |
| ... | @@ -3784,12 +3816,12 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3784,12 +3816,12 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3784 | const contents = buffer[0 .. interp.len + 1]; | 3816 | const contents = buffer[0 .. interp.len + 1]; |
| 3785 | const shdr = slice.items(.shdr)[shndx]; | 3817 | const shdr = slice.items(.shdr)[shndx]; |
| 3786 | assert(shdr.sh_size == contents.len); | 3818 | assert(shdr.sh_size == contents.len); |
| 3787 | try self.base.file.?.pwriteAll(contents, shdr.sh_offset); | 3819 | try self.pwriteAll(contents, shdr.sh_offset); |
| 3788 | } | 3820 | } |
| 3789 | 3821 | ||
| 3790 | if (self.section_indexes.hash) |shndx| { | 3822 | if (self.section_indexes.hash) |shndx| { |
| 3791 | const shdr = slice.items(.shdr)[shndx]; | 3823 | const shdr = slice.items(.shdr)[shndx]; |
| 3792 | try self.base.file.?.pwriteAll(self.hash.buffer.items, shdr.sh_offset); | 3824 | try self.pwriteAll(self.hash.buffer.items, shdr.sh_offset); |
| 3793 | } | 3825 | } |
| 3794 | 3826 | ||
| 3795 | if (self.section_indexes.gnu_hash) |shndx| { | 3827 | if (self.section_indexes.gnu_hash) |shndx| { |
| ... | @@ -3797,12 +3829,12 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3797,12 +3829,12 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3797 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size()); | 3829 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size()); |
| 3798 | defer buffer.deinit(); | 3830 | defer buffer.deinit(); |
| 3799 | try self.gnu_hash.write(self, buffer.writer()); | 3831 | try self.gnu_hash.write(self, buffer.writer()); |
| 3800 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3832 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3801 | } | 3833 | } |
| 3802 | 3834 | ||
| 3803 | if (self.section_indexes.versym) |shndx| { | 3835 | if (self.section_indexes.versym) |shndx| { |
| 3804 | const shdr = slice.items(.shdr)[shndx]; | 3836 | const shdr = slice.items(.shdr)[shndx]; |
| 3805 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset); | 3837 | try self.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset); |
| 3806 | } | 3838 | } |
| 3807 | 3839 | ||
| 3808 | if (self.section_indexes.verneed) |shndx| { | 3840 | if (self.section_indexes.verneed) |shndx| { |
| ... | @@ -3810,7 +3842,7 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3810,7 +3842,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3810 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size()); | 3842 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size()); |
| 3811 | defer buffer.deinit(); | 3843 | defer buffer.deinit(); |
| 3812 | try self.verneed.write(buffer.writer()); | 3844 | try self.verneed.write(buffer.writer()); |
| 3813 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3845 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3814 | } | 3846 | } |
| 3815 | 3847 | ||
| 3816 | if (self.section_indexes.dynamic) |shndx| { | 3848 | if (self.section_indexes.dynamic) |shndx| { |
| ... | @@ -3818,7 +3850,7 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3818,7 +3850,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3818 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self)); | 3850 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self)); |
| 3819 | defer buffer.deinit(); | 3851 | defer buffer.deinit(); |
| 3820 | try self.dynamic.write(self, buffer.writer()); | 3852 | try self.dynamic.write(self, buffer.writer()); |
| 3821 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3853 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3822 | } | 3854 | } |
| 3823 | 3855 | ||
| 3824 | if (self.section_indexes.dynsymtab) |shndx| { | 3856 | if (self.section_indexes.dynsymtab) |shndx| { |
| ... | @@ -3826,12 +3858,12 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3826,12 +3858,12 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3826 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size()); | 3858 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size()); |
| 3827 | defer buffer.deinit(); | 3859 | defer buffer.deinit(); |
| 3828 | try self.dynsym.write(self, buffer.writer()); | 3860 | try self.dynsym.write(self, buffer.writer()); |
| 3829 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3861 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3830 | } | 3862 | } |
| 3831 | 3863 | ||
| 3832 | if (self.section_indexes.dynstrtab) |shndx| { | 3864 | if (self.section_indexes.dynstrtab) |shndx| { |
| 3833 | const shdr = slice.items(.shdr)[shndx]; | 3865 | const shdr = slice.items(.shdr)[shndx]; |
| 3834 | try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset); | 3866 | try self.pwriteAll(self.dynstrtab.items, shdr.sh_offset); |
| 3835 | } | 3867 | } |
| 3836 | 3868 | ||
| 3837 | if (self.section_indexes.eh_frame) |shndx| { | 3869 | if (self.section_indexes.eh_frame) |shndx| { |
| ... | @@ -3841,21 +3873,21 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3841,21 +3873,21 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3841 | break :existing_size sym.atom(self).?.size; | 3873 | break :existing_size sym.atom(self).?.size; |
| 3842 | }; | 3874 | }; |
| 3843 | const shdr = slice.items(.shdr)[shndx]; | 3875 | const shdr = slice.items(.shdr)[shndx]; |
| 3844 | const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; | 3876 | const sh_size = try self.cast(usize, shdr.sh_size); |
| 3845 | var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size)); | 3877 | var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size)); |
| 3846 | defer buffer.deinit(); | 3878 | defer buffer.deinit(); |
| 3847 | try eh_frame.writeEhFrame(self, buffer.writer()); | 3879 | try eh_frame.writeEhFrame(self, buffer.writer()); |
| 3848 | assert(buffer.items.len == sh_size - existing_size); | 3880 | assert(buffer.items.len == sh_size - existing_size); |
| 3849 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset + existing_size); | 3881 | try self.pwriteAll(buffer.items, shdr.sh_offset + existing_size); |
| 3850 | } | 3882 | } |
| 3851 | 3883 | ||
| 3852 | if (self.section_indexes.eh_frame_hdr) |shndx| { | 3884 | if (self.section_indexes.eh_frame_hdr) |shndx| { |
| 3853 | const shdr = slice.items(.shdr)[shndx]; | 3885 | const shdr = slice.items(.shdr)[shndx]; |
| 3854 | const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; | 3886 | const sh_size = try self.cast(usize, shdr.sh_size); |
| 3855 | var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); | 3887 | var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); |
| 3856 | defer buffer.deinit(); | 3888 | defer buffer.deinit(); |
| 3857 | try eh_frame.writeEhFrameHdr(self, buffer.writer()); | 3889 | try eh_frame.writeEhFrameHdr(self, buffer.writer()); |
| 3858 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3890 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3859 | } | 3891 | } |
| 3860 | 3892 | ||
| 3861 | if (self.section_indexes.got) |index| { | 3893 | if (self.section_indexes.got) |index| { |
| ... | @@ -3863,7 +3895,7 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3863,7 +3895,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3863 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); | 3895 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); |
| 3864 | defer buffer.deinit(); | 3896 | defer buffer.deinit(); |
| 3865 | try self.got.write(self, buffer.writer()); | 3897 | try self.got.write(self, buffer.writer()); |
| 3866 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3898 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3867 | } | 3899 | } |
| 3868 | 3900 | ||
| 3869 | if (self.section_indexes.rela_dyn) |shndx| { | 3901 | if (self.section_indexes.rela_dyn) |shndx| { |
| ... | @@ -3871,7 +3903,7 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3871,7 +3903,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3871 | try self.got.addRela(self); | 3903 | try self.got.addRela(self); |
| 3872 | try self.copy_rel.addRela(self); | 3904 | try self.copy_rel.addRela(self); |
| 3873 | self.sortRelaDyn(); | 3905 | self.sortRelaDyn(); |
| 3874 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); | 3906 | try self.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); |
| 3875 | } | 3907 | } |
| 3876 | 3908 | ||
| 3877 | if (self.section_indexes.plt) |shndx| { | 3909 | if (self.section_indexes.plt) |shndx| { |
| ... | @@ -3879,7 +3911,7 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3879,7 +3911,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3879 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self)); | 3911 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self)); |
| 3880 | defer buffer.deinit(); | 3912 | defer buffer.deinit(); |
| 3881 | try self.plt.write(self, buffer.writer()); | 3913 | try self.plt.write(self, buffer.writer()); |
| 3882 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3914 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3883 | } | 3915 | } |
| 3884 | 3916 | ||
| 3885 | if (self.section_indexes.got_plt) |shndx| { | 3917 | if (self.section_indexes.got_plt) |shndx| { |
| ... | @@ -3887,7 +3919,7 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3887,7 +3919,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3887 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self)); | 3919 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self)); |
| 3888 | defer buffer.deinit(); | 3920 | defer buffer.deinit(); |
| 3889 | try self.got_plt.write(self, buffer.writer()); | 3921 | try self.got_plt.write(self, buffer.writer()); |
| 3890 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3922 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3891 | } | 3923 | } |
| 3892 | 3924 | ||
| 3893 | if (self.section_indexes.plt_got) |shndx| { | 3925 | if (self.section_indexes.plt_got) |shndx| { |
| ... | @@ -3895,25 +3927,24 @@ fn writeSyntheticSections(self: *Elf) !void { | ... | @@ -3895,25 +3927,24 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3895 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self)); | 3927 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self)); |
| 3896 | defer buffer.deinit(); | 3928 | defer buffer.deinit(); |
| 3897 | try self.plt_got.write(self, buffer.writer()); | 3929 | try self.plt_got.write(self, buffer.writer()); |
| 3898 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | 3930 | try self.pwriteAll(buffer.items, shdr.sh_offset); |
| 3899 | } | 3931 | } |
| 3900 | 3932 | ||
| 3901 | if (self.section_indexes.rela_plt) |shndx| { | 3933 | if (self.section_indexes.rela_plt) |shndx| { |
| 3902 | const shdr = slice.items(.shdr)[shndx]; | 3934 | const shdr = slice.items(.shdr)[shndx]; |
| 3903 | try self.plt.addRela(self); | 3935 | try self.plt.addRela(self); |
| 3904 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset); | 3936 | try self.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset); |
| 3905 | } | 3937 | } |
| 3906 | 3938 | ||
| 3907 | try self.writeSymtab(); | 3939 | try self.writeSymtab(); |
| 3908 | try self.writeShStrtab(); | 3940 | try self.writeShStrtab(); |
| 3909 | } | 3941 | } |
| 3910 | 3942 | ||
| 3911 | // FIXME:JK again, why is this needed? | ||
| 3912 | pub fn writeShStrtab(self: *Elf) !void { | 3943 | pub fn writeShStrtab(self: *Elf) !void { |
| 3913 | if (self.section_indexes.shstrtab) |index| { | 3944 | if (self.section_indexes.shstrtab) |index| { |
| 3914 | const shdr = self.sections.items(.shdr)[index]; | 3945 | const shdr = self.sections.items(.shdr)[index]; |
| 3915 | log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size }); | 3946 | log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size }); |
| 3916 | try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset); | 3947 | try self.pwriteAll(self.shstrtab.items, shdr.sh_offset); |
| 3917 | } | 3948 | } |
| 3918 | } | 3949 | } |
| 3919 | 3950 | ||
| ... | @@ -3928,7 +3959,7 @@ pub fn writeSymtab(self: *Elf) !void { | ... | @@ -3928,7 +3959,7 @@ pub fn writeSymtab(self: *Elf) !void { |
| 3928 | .p32 => @sizeOf(elf.Elf32_Sym), | 3959 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 3929 | .p64 => @sizeOf(elf.Elf64_Sym), | 3960 | .p64 => @sizeOf(elf.Elf64_Sym), |
| 3930 | }; | 3961 | }; |
| 3931 | const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow; | 3962 | const nsyms = try self.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)); |
| 3932 | 3963 | ||
| 3933 | log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{ | 3964 | log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{ |
| 3934 | nsyms, | 3965 | nsyms, |
| ... | @@ -3941,7 +3972,7 @@ pub fn writeSymtab(self: *Elf) !void { | ... | @@ -3941,7 +3972,7 @@ pub fn writeSymtab(self: *Elf) !void { |
| 3941 | }); | 3972 | }); |
| 3942 | 3973 | ||
| 3943 | try self.symtab.resize(gpa, nsyms); | 3974 | try self.symtab.resize(gpa, nsyms); |
| 3944 | const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; | 3975 | const needed_strtab_size = try self.cast(usize, strtab_shdr.sh_size - 1); |
| 3945 | // TODO we could resize instead and in ZigObject/Object always access as slice | 3976 | // TODO we could resize instead and in ZigObject/Object always access as slice |
| 3946 | self.strtab.clearRetainingCapacity(); | 3977 | self.strtab.clearRetainingCapacity(); |
| 3947 | self.strtab.appendAssumeCapacity(0); | 3978 | self.strtab.appendAssumeCapacity(0); |
| ... | @@ -4010,17 +4041,17 @@ pub fn writeSymtab(self: *Elf) !void { | ... | @@ -4010,17 +4041,17 @@ pub fn writeSymtab(self: *Elf) !void { |
| 4010 | }; | 4041 | }; |
| 4011 | if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); | 4042 | if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); |
| 4012 | } | 4043 | } |
| 4013 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset); | 4044 | try self.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset); |
| 4014 | }, | 4045 | }, |
| 4015 | .p64 => { | 4046 | .p64 => { |
| 4016 | if (foreign_endian) { | 4047 | if (foreign_endian) { |
| 4017 | for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); | 4048 | for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); |
| 4018 | } | 4049 | } |
| 4019 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset); | 4050 | try self.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset); |
| 4020 | }, | 4051 | }, |
| 4021 | } | 4052 | } |
| 4022 | 4053 | ||
| 4023 | try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); | 4054 | try self.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); |
| 4024 | } | 4055 | } |
| 4025 | 4056 | ||
| 4026 | /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. | 4057 | /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. |
| ... | @@ -4514,12 +4545,12 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { | ... | @@ -4514,12 +4545,12 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { |
| 4514 | for (refs.items[0..nrefs]) |ref| { | 4545 | for (refs.items[0..nrefs]) |ref| { |
| 4515 | const atom_ptr = self.atom(ref).?; | 4546 | const atom_ptr = self.atom(ref).?; |
| 4516 | const file_ptr = atom_ptr.file(self).?; | 4547 | const file_ptr = atom_ptr.file(self).?; |
| 4517 | try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); | 4548 | err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); |
| 4518 | } | 4549 | } |
| 4519 | 4550 | ||
| 4520 | if (refs.items.len > max_notes) { | 4551 | if (refs.items.len > max_notes) { |
| 4521 | const remaining = refs.items.len - max_notes; | 4552 | const remaining = refs.items.len - max_notes; |
| 4522 | try err.addNote("referenced {d} more times", .{remaining}); | 4553 | err.addNote("referenced {d} more times", .{remaining}); |
| 4523 | } | 4554 | } |
| 4524 | } | 4555 | } |
| 4525 | } | 4556 | } |
| ... | @@ -4536,17 +4567,17 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor | ... | @@ -4536,17 +4567,17 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor |
| 4536 | 4567 | ||
| 4537 | var err = try diags.addErrorWithNotes(nnotes + 1); | 4568 | var err = try diags.addErrorWithNotes(nnotes + 1); |
| 4538 | try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)}); | 4569 | try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)}); |
| 4539 | try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()}); | 4570 | err.addNote("defined by {}", .{sym.file(self).?.fmtPath()}); |
| 4540 | 4571 | ||
| 4541 | var inote: usize = 0; | 4572 | var inote: usize = 0; |
| 4542 | while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { | 4573 | while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { |
| 4543 | const file_ptr = self.file(notes.items[inote]).?; | 4574 | const file_ptr = self.file(notes.items[inote]).?; |
| 4544 | try err.addNote("defined by {}", .{file_ptr.fmtPath()}); | 4575 | err.addNote("defined by {}", .{file_ptr.fmtPath()}); |
| 4545 | } | 4576 | } |
| 4546 | 4577 | ||
| 4547 | if (notes.items.len > max_notes) { | 4578 | if (notes.items.len > max_notes) { |
| 4548 | const remaining = notes.items.len - max_notes; | 4579 | const remaining = notes.items.len - max_notes; |
| 4549 | try err.addNote("defined {d} more times", .{remaining}); | 4580 | err.addNote("defined {d} more times", .{remaining}); |
| 4550 | } | 4581 | } |
| 4551 | } | 4582 | } |
| 4552 | 4583 | ||
| ... | @@ -4570,7 +4601,7 @@ pub fn addFileError( | ... | @@ -4570,7 +4601,7 @@ pub fn addFileError( |
| 4570 | const diags = &self.base.comp.link_diags; | 4601 | const diags = &self.base.comp.link_diags; |
| 4571 | var err = try diags.addErrorWithNotes(1); | 4602 | var err = try diags.addErrorWithNotes(1); |
| 4572 | try err.addMsg(format, args); | 4603 | try err.addMsg(format, args); |
| 4573 | try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); | 4604 | err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); |
| 4574 | } | 4605 | } |
| 4575 | 4606 | ||
| 4576 | pub fn failFile( | 4607 | pub fn failFile( |
| ... | @@ -5184,6 +5215,30 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 { | ... | @@ -5184,6 +5215,30 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 { |
| 5184 | return slice[0..mem.indexOfScalar(u8, slice, 0).? :0]; | 5215 | return slice[0..mem.indexOfScalar(u8, slice, 0).? :0]; |
| 5185 | } | 5216 | } |
| 5186 | 5217 | ||
| 5218 | pub fn pwriteAll(elf_file: *Elf, bytes: []const u8, offset: u64) error{LinkFailure}!void { | ||
| 5219 | const comp = elf_file.base.comp; | ||
| 5220 | const diags = &comp.link_diags; | ||
| 5221 | elf_file.base.file.?.pwriteAll(bytes, offset) catch |err| { | ||
| 5222 | return diags.fail("failed to write: {s}", .{@errorName(err)}); | ||
| 5223 | }; | ||
| 5224 | } | ||
| 5225 | |||
| 5226 | pub fn setEndPos(elf_file: *Elf, length: u64) error{LinkFailure}!void { | ||
| 5227 | const comp = elf_file.base.comp; | ||
| 5228 | const diags = &comp.link_diags; | ||
| 5229 | elf_file.base.file.?.setEndPos(length) catch |err| { | ||
| 5230 | return diags.fail("failed to set file end pos: {s}", .{@errorName(err)}); | ||
| 5231 | }; | ||
| 5232 | } | ||
| 5233 | |||
| 5234 | pub fn cast(elf_file: *Elf, comptime T: type, x: anytype) error{LinkFailure}!T { | ||
| 5235 | return std.math.cast(T, x) orelse { | ||
| 5236 | const comp = elf_file.base.comp; | ||
| 5237 | const diags = &comp.link_diags; | ||
| 5238 | return diags.fail("encountered {d}, overflowing {d}-bit value", .{ x, @bitSizeOf(T) }); | ||
| 5239 | }; | ||
| 5240 | } | ||
| 5241 | |||
| 5187 | const std = @import("std"); | 5242 | const std = @import("std"); |
| 5188 | const build_options = @import("build_options"); | 5243 | const build_options = @import("build_options"); |
| 5189 | const builtin = @import("builtin"); | 5244 | const builtin = @import("builtin"); |
src/link/Elf/Atom.zig+12-12| ... | @@ -523,7 +523,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) Re | ... | @@ -523,7 +523,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) Re |
| 523 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), | 523 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), |
| 524 | rel.r_offset, | 524 | rel.r_offset, |
| 525 | }); | 525 | }); |
| 526 | try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | 526 | err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); |
| 527 | return error.RelocFailure; | 527 | return error.RelocFailure; |
| 528 | } | 528 | } |
| 529 | 529 | ||
| ... | @@ -539,7 +539,7 @@ fn reportTextRelocError( | ... | @@ -539,7 +539,7 @@ fn reportTextRelocError( |
| 539 | rel.r_offset, | 539 | rel.r_offset, |
| 540 | symbol.name(elf_file), | 540 | symbol.name(elf_file), |
| 541 | }); | 541 | }); |
| 542 | try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | 542 | err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); |
| 543 | return error.RelocFailure; | 543 | return error.RelocFailure; |
| 544 | } | 544 | } |
| 545 | 545 | ||
| ... | @@ -555,8 +555,8 @@ fn reportPicError( | ... | @@ -555,8 +555,8 @@ fn reportPicError( |
| 555 | rel.r_offset, | 555 | rel.r_offset, |
| 556 | symbol.name(elf_file), | 556 | symbol.name(elf_file), |
| 557 | }); | 557 | }); |
| 558 | try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | 558 | err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); |
| 559 | try err.addNote("recompile with -fPIC", .{}); | 559 | err.addNote("recompile with -fPIC", .{}); |
| 560 | return error.RelocFailure; | 560 | return error.RelocFailure; |
| 561 | } | 561 | } |
| 562 | 562 | ||
| ... | @@ -572,8 +572,8 @@ fn reportNoPicError( | ... | @@ -572,8 +572,8 @@ fn reportNoPicError( |
| 572 | rel.r_offset, | 572 | rel.r_offset, |
| 573 | symbol.name(elf_file), | 573 | symbol.name(elf_file), |
| 574 | }); | 574 | }); |
| 575 | try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | 575 | err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); |
| 576 | try err.addNote("recompile with -fno-PIC", .{}); | 576 | err.addNote("recompile with -fno-PIC", .{}); |
| 577 | return error.RelocFailure; | 577 | return error.RelocFailure; |
| 578 | } | 578 | } |
| 579 | 579 | ||
| ... | @@ -1187,7 +1187,7 @@ const x86_64 = struct { | ... | @@ -1187,7 +1187,7 @@ const x86_64 = struct { |
| 1187 | x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch { | 1187 | x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch { |
| 1188 | var err = try diags.addErrorWithNotes(1); | 1188 | var err = try diags.addErrorWithNotes(1); |
| 1189 | try err.addMsg("could not relax {s}", .{@tagName(r_type)}); | 1189 | try err.addMsg("could not relax {s}", .{@tagName(r_type)}); |
| 1190 | try err.addNote("in {}:{s} at offset 0x{x}", .{ | 1190 | err.addNote("in {}:{s} at offset 0x{x}", .{ |
| 1191 | atom.file(elf_file).?.fmtPath(), | 1191 | atom.file(elf_file).?.fmtPath(), |
| 1192 | atom.name(elf_file), | 1192 | atom.name(elf_file), |
| 1193 | rel.r_offset, | 1193 | rel.r_offset, |
| ... | @@ -1332,7 +1332,7 @@ const x86_64 = struct { | ... | @@ -1332,7 +1332,7 @@ const x86_64 = struct { |
| 1332 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), | 1332 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1333 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), | 1333 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1334 | }); | 1334 | }); |
| 1335 | try err.addNote("in {}:{s} at offset 0x{x}", .{ | 1335 | err.addNote("in {}:{s} at offset 0x{x}", .{ |
| 1336 | self.file(elf_file).?.fmtPath(), | 1336 | self.file(elf_file).?.fmtPath(), |
| 1337 | self.name(elf_file), | 1337 | self.name(elf_file), |
| 1338 | rels[0].r_offset, | 1338 | rels[0].r_offset, |
| ... | @@ -1388,7 +1388,7 @@ const x86_64 = struct { | ... | @@ -1388,7 +1388,7 @@ const x86_64 = struct { |
| 1388 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), | 1388 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1389 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), | 1389 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1390 | }); | 1390 | }); |
| 1391 | try err.addNote("in {}:{s} at offset 0x{x}", .{ | 1391 | err.addNote("in {}:{s} at offset 0x{x}", .{ |
| 1392 | self.file(elf_file).?.fmtPath(), | 1392 | self.file(elf_file).?.fmtPath(), |
| 1393 | self.name(elf_file), | 1393 | self.name(elf_file), |
| 1394 | rels[0].r_offset, | 1394 | rels[0].r_offset, |
| ... | @@ -1485,7 +1485,7 @@ const x86_64 = struct { | ... | @@ -1485,7 +1485,7 @@ const x86_64 = struct { |
| 1485 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), | 1485 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1486 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), | 1486 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1487 | }); | 1487 | }); |
| 1488 | try err.addNote("in {}:{s} at offset 0x{x}", .{ | 1488 | err.addNote("in {}:{s} at offset 0x{x}", .{ |
| 1489 | self.file(elf_file).?.fmtPath(), | 1489 | self.file(elf_file).?.fmtPath(), |
| 1490 | self.name(elf_file), | 1490 | self.name(elf_file), |
| 1491 | rels[0].r_offset, | 1491 | rels[0].r_offset, |
| ... | @@ -1672,7 +1672,7 @@ const aarch64 = struct { | ... | @@ -1672,7 +1672,7 @@ const aarch64 = struct { |
| 1672 | // TODO: relax | 1672 | // TODO: relax |
| 1673 | var err = try diags.addErrorWithNotes(1); | 1673 | var err = try diags.addErrorWithNotes(1); |
| 1674 | try err.addMsg("TODO: relax ADR_GOT_PAGE", .{}); | 1674 | try err.addMsg("TODO: relax ADR_GOT_PAGE", .{}); |
| 1675 | try err.addNote("in {}:{s} at offset 0x{x}", .{ | 1675 | err.addNote("in {}:{s} at offset 0x{x}", .{ |
| 1676 | atom.file(elf_file).?.fmtPath(), | 1676 | atom.file(elf_file).?.fmtPath(), |
| 1677 | atom.name(elf_file), | 1677 | atom.name(elf_file), |
| 1678 | r_offset, | 1678 | r_offset, |
| ... | @@ -1959,7 +1959,7 @@ const riscv = struct { | ... | @@ -1959,7 +1959,7 @@ const riscv = struct { |
| 1959 | // TODO: implement searching forward | 1959 | // TODO: implement searching forward |
| 1960 | var err = try diags.addErrorWithNotes(1); | 1960 | var err = try diags.addErrorWithNotes(1); |
| 1961 | try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{}); | 1961 | try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{}); |
| 1962 | try err.addNote("in {}:{s} at offset 0x{x}", .{ | 1962 | err.addNote("in {}:{s} at offset 0x{x}", .{ |
| 1963 | atom.file(elf_file).?.fmtPath(), | 1963 | atom.file(elf_file).?.fmtPath(), |
| 1964 | atom.name(elf_file), | 1964 | atom.name(elf_file), |
| 1965 | rel.r_offset, | 1965 | rel.r_offset, |
src/link/Elf/AtomList.zig+3-2| ... | @@ -58,7 +58,7 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void { | ... | @@ -58,7 +58,7 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void { |
| 58 | if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref(); | 58 | if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref(); |
| 59 | shdr.sh_addralign = @max(shdr.sh_addralign, list.alignment.toByteUnits().?); | 59 | shdr.sh_addralign = @max(shdr.sh_addralign, list.alignment.toByteUnits().?); |
| 60 | 60 | ||
| 61 | // FIXME:JK this currently ignores Thunks as valid chunks. | 61 | // This currently ignores Thunks as valid chunks. |
| 62 | { | 62 | { |
| 63 | var idx: usize = 0; | 63 | var idx: usize = 0; |
| 64 | while (idx < list.atoms.keys().len) : (idx += 1) { | 64 | while (idx < list.atoms.keys().len) : (idx += 1) { |
| ... | @@ -78,7 +78,8 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void { | ... | @@ -78,7 +78,8 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void { |
| 78 | placement_atom.next_atom_ref = list.firstAtom(elf_file).ref(); | 78 | placement_atom.next_atom_ref = list.firstAtom(elf_file).ref(); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | // FIXME:JK if we had a link from Atom to parent AtomList we would not need to update Atom's value or osec index | 81 | // If we had a link from Atom to parent AtomList we would not need to |
| 82 | // update Atom's value or osec index. | ||
| 82 | for (list.atoms.keys()) |ref| { | 83 | for (list.atoms.keys()) |ref| { |
| 83 | const atom_ptr = elf_file.atom(ref).?; | 84 | const atom_ptr = elf_file.atom(ref).?; |
| 84 | atom_ptr.output_section_index = list.output_section_index; | 85 | atom_ptr.output_section_index = list.output_section_index; |
src/link/Elf/Object.zig+5-5| ... | @@ -797,7 +797,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { | ... | @@ -797,7 +797,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { |
| 797 | if (!isNull(data[end .. end + sh_entsize])) { | 797 | if (!isNull(data[end .. end + sh_entsize])) { |
| 798 | var err = try diags.addErrorWithNotes(1); | 798 | var err = try diags.addErrorWithNotes(1); |
| 799 | try err.addMsg("string not null terminated", .{}); | 799 | try err.addMsg("string not null terminated", .{}); |
| 800 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); | 800 | err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); |
| 801 | return error.LinkFailure; | 801 | return error.LinkFailure; |
| 802 | } | 802 | } |
| 803 | end += sh_entsize; | 803 | end += sh_entsize; |
| ... | @@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { | ... | @@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { |
| 812 | if (shdr.sh_size % sh_entsize != 0) { | 812 | if (shdr.sh_size % sh_entsize != 0) { |
| 813 | var err = try diags.addErrorWithNotes(1); | 813 | var err = try diags.addErrorWithNotes(1); |
| 814 | try err.addMsg("size not a multiple of sh_entsize", .{}); | 814 | try err.addMsg("size not a multiple of sh_entsize", .{}); |
| 815 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); | 815 | err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); |
| 816 | return error.LinkFailure; | 816 | return error.LinkFailure; |
| 817 | } | 817 | } |
| 818 | 818 | ||
| ... | @@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ | ... | @@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ |
| 889 | const res = imsec.findSubsection(@intCast(esym.st_value)) orelse { | 889 | const res = imsec.findSubsection(@intCast(esym.st_value)) orelse { |
| 890 | var err = try diags.addErrorWithNotes(2); | 890 | var err = try diags.addErrorWithNotes(2); |
| 891 | try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); | 891 | try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); |
| 892 | try err.addNote("for symbol {s}", .{sym.name(elf_file)}); | 892 | err.addNote("for symbol {s}", .{sym.name(elf_file)}); |
| 893 | try err.addNote("in {}", .{self.fmtPath()}); | 893 | err.addNote("in {}", .{self.fmtPath()}); |
| 894 | return error.LinkFailure; | 894 | return error.LinkFailure; |
| 895 | }; | 895 | }; |
| 896 | 896 | ||
| ... | @@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ | ... | @@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ |
| 915 | const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { | 915 | const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { |
| 916 | var err = try diags.addErrorWithNotes(1); | 916 | var err = try diags.addErrorWithNotes(1); |
| 917 | try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); | 917 | try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); |
| 918 | try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); | 918 | err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); |
| 919 | return error.LinkFailure; | 919 | return error.LinkFailure; |
| 920 | }; | 920 | }; |
| 921 | 921 |
src/link/Elf/ZigObject.zig+67-65| ... | @@ -278,8 +278,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { | ... | @@ -278,8 +278,8 @@ 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| return e, |
| 283 | }; | 283 | }; |
| 284 | if (metadata.rodata_state != .unused) self.updateLazySymbol( | 284 | if (metadata.rodata_state != .unused) self.updateLazySymbol( |
| 285 | elf_file, | 285 | elf_file, |
| ... | @@ -287,8 +287,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { | ... | @@ -287,8 +287,8 @@ 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| return e, |
| 292 | }; | 292 | }; |
| 293 | } | 293 | } |
| 294 | for (self.lazy_syms.values()) |*metadata| { | 294 | for (self.lazy_syms.values()) |*metadata| { |
| ... | @@ -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); |
| ... | @@ -1261,7 +1263,7 @@ fn updateNavCode( | ... | @@ -1261,7 +1263,7 @@ fn updateNavCode( |
| 1261 | shdr_index: u32, | 1263 | shdr_index: u32, |
| 1262 | code: []const u8, | 1264 | code: []const u8, |
| 1263 | stt_bits: u8, | 1265 | stt_bits: u8, |
| 1264 | ) !void { | 1266 | ) link.File.UpdateNavError!void { |
| 1265 | const zcu = pt.zcu; | 1267 | const zcu = pt.zcu; |
| 1266 | const gpa = zcu.gpa; | 1268 | const gpa = zcu.gpa; |
| 1267 | const ip = &zcu.intern_pool; | 1269 | const ip = &zcu.intern_pool; |
| ... | @@ -1298,7 +1300,9 @@ fn updateNavCode( | ... | @@ -1298,7 +1300,9 @@ fn updateNavCode( |
| 1298 | const capacity = atom_ptr.capacity(elf_file); | 1300 | const capacity = atom_ptr.capacity(elf_file); |
| 1299 | const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value)); | 1301 | const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value)); |
| 1300 | if (need_realloc) { | 1302 | if (need_realloc) { |
| 1301 | try self.allocateAtom(atom_ptr, true, elf_file); | 1303 | self.allocateAtom(atom_ptr, true, elf_file) catch |err| |
| 1304 | return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)}); | ||
| 1305 | |||
| 1302 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value }); | 1306 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value }); |
| 1303 | if (old_vaddr != atom_ptr.value) { | 1307 | if (old_vaddr != atom_ptr.value) { |
| 1304 | sym.value = 0; | 1308 | sym.value = 0; |
| ... | @@ -1308,7 +1312,9 @@ fn updateNavCode( | ... | @@ -1308,7 +1312,9 @@ fn updateNavCode( |
| 1308 | // TODO shrink section size | 1312 | // TODO shrink section size |
| 1309 | } | 1313 | } |
| 1310 | } else { | 1314 | } else { |
| 1311 | try self.allocateAtom(atom_ptr, true, elf_file); | 1315 | self.allocateAtom(atom_ptr, true, elf_file) catch |err| |
| 1316 | return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)}); | ||
| 1317 | |||
| 1312 | errdefer self.freeNavMetadata(elf_file, sym_index); | 1318 | errdefer self.freeNavMetadata(elf_file, sym_index); |
| 1313 | sym.value = 0; | 1319 | sym.value = 0; |
| 1314 | esym.st_value = 0; | 1320 | esym.st_value = 0; |
| ... | @@ -1333,14 +1339,15 @@ fn updateNavCode( | ... | @@ -1333,14 +1339,15 @@ fn updateNavCode( |
| 1333 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | 1339 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), |
| 1334 | } | 1340 | } |
| 1335 | }, | 1341 | }, |
| 1336 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | 1342 | else => return elf_file.base.cgFail(nav_index, "ELF hot swap unavailable on host operating system '{s}'", .{@tagName(builtin.os.tag)}), |
| 1337 | } | 1343 | } |
| 1338 | } | 1344 | } |
| 1339 | 1345 | ||
| 1340 | const shdr = elf_file.sections.items(.shdr)[shdr_index]; | 1346 | const shdr = elf_file.sections.items(.shdr)[shdr_index]; |
| 1341 | if (shdr.sh_type != elf.SHT_NOBITS) { | 1347 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 1342 | const file_offset = atom_ptr.offset(elf_file); | 1348 | const file_offset = atom_ptr.offset(elf_file); |
| 1343 | try elf_file.base.file.?.pwriteAll(code, file_offset); | 1349 | elf_file.base.file.?.pwriteAll(code, file_offset) catch |err| |
| 1350 | return elf_file.base.cgFail(nav_index, "failed to write to output file: {s}", .{@errorName(err)}); | ||
| 1344 | log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len }); | 1351 | log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len }); |
| 1345 | } | 1352 | } |
| 1346 | } | 1353 | } |
| ... | @@ -1353,7 +1360,7 @@ fn updateTlv( | ... | @@ -1353,7 +1360,7 @@ fn updateTlv( |
| 1353 | sym_index: Symbol.Index, | 1360 | sym_index: Symbol.Index, |
| 1354 | shndx: u32, | 1361 | shndx: u32, |
| 1355 | code: []const u8, | 1362 | code: []const u8, |
| 1356 | ) !void { | 1363 | ) link.File.UpdateNavError!void { |
| 1357 | const zcu = pt.zcu; | 1364 | const zcu = pt.zcu; |
| 1358 | const ip = &zcu.intern_pool; | 1365 | const ip = &zcu.intern_pool; |
| 1359 | const gpa = zcu.gpa; | 1366 | const gpa = zcu.gpa; |
| ... | @@ -1383,7 +1390,8 @@ fn updateTlv( | ... | @@ -1383,7 +1390,8 @@ fn updateTlv( |
| 1383 | const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index); | 1390 | const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index); |
| 1384 | assert(!gop.found_existing); // TODO incremental updates | 1391 | assert(!gop.found_existing); // TODO incremental updates |
| 1385 | 1392 | ||
| 1386 | try self.allocateAtom(atom_ptr, true, elf_file); | 1393 | self.allocateAtom(atom_ptr, true, elf_file) catch |err| |
| 1394 | return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)}); | ||
| 1387 | sym.value = 0; | 1395 | sym.value = 0; |
| 1388 | esym.st_value = 0; | 1396 | esym.st_value = 0; |
| 1389 | 1397 | ||
| ... | @@ -1392,7 +1400,8 @@ fn updateTlv( | ... | @@ -1392,7 +1400,8 @@ fn updateTlv( |
| 1392 | const shdr = elf_file.sections.items(.shdr)[shndx]; | 1400 | const shdr = elf_file.sections.items(.shdr)[shndx]; |
| 1393 | if (shdr.sh_type != elf.SHT_NOBITS) { | 1401 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 1394 | const file_offset = atom_ptr.offset(elf_file); | 1402 | const file_offset = atom_ptr.offset(elf_file); |
| 1395 | try elf_file.base.file.?.pwriteAll(code, file_offset); | 1403 | elf_file.base.file.?.pwriteAll(code, file_offset) catch |err| |
| 1404 | return elf_file.base.cgFail(nav_index, "failed to write to output file: {s}", .{@errorName(err)}); | ||
| 1396 | log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{ | 1405 | log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{ |
| 1397 | atom_ptr.name(elf_file), | 1406 | atom_ptr.name(elf_file), |
| 1398 | file_offset, | 1407 | file_offset, |
| ... | @@ -1408,7 +1417,7 @@ pub fn updateFunc( | ... | @@ -1408,7 +1417,7 @@ pub fn updateFunc( |
| 1408 | func_index: InternPool.Index, | 1417 | func_index: InternPool.Index, |
| 1409 | air: Air, | 1418 | air: Air, |
| 1410 | liveness: Liveness, | 1419 | liveness: Liveness, |
| 1411 | ) !void { | 1420 | ) link.File.UpdateNavError!void { |
| 1412 | const tracy = trace(@src()); | 1421 | const tracy = trace(@src()); |
| 1413 | defer tracy.end(); | 1422 | defer tracy.end(); |
| 1414 | 1423 | ||
| ... | @@ -1422,13 +1431,13 @@ pub fn updateFunc( | ... | @@ -1422,13 +1431,13 @@ pub fn updateFunc( |
| 1422 | const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav); | 1431 | const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav); |
| 1423 | self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self); | 1432 | self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self); |
| 1424 | 1433 | ||
| 1425 | var code_buffer = std.ArrayList(u8).init(gpa); | 1434 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1426 | defer code_buffer.deinit(); | 1435 | defer code_buffer.deinit(gpa); |
| 1427 | 1436 | ||
| 1428 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; | 1437 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; |
| 1429 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); | 1438 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 1430 | 1439 | ||
| 1431 | const res = try codegen.generateFunction( | 1440 | try codegen.generateFunction( |
| 1432 | &elf_file.base, | 1441 | &elf_file.base, |
| 1433 | pt, | 1442 | pt, |
| 1434 | zcu.navSrcLoc(func.owner_nav), | 1443 | zcu.navSrcLoc(func.owner_nav), |
| ... | @@ -1438,14 +1447,7 @@ pub fn updateFunc( | ... | @@ -1438,14 +1447,7 @@ pub fn updateFunc( |
| 1438 | &code_buffer, | 1447 | &code_buffer, |
| 1439 | if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none, | 1448 | if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none, |
| 1440 | ); | 1449 | ); |
| 1441 | 1450 | const code = code_buffer.items; | |
| 1442 | const code = switch (res) { | ||
| 1443 | .ok => code_buffer.items, | ||
| 1444 | .fail => |em| { | ||
| 1445 | try zcu.failed_codegen.put(gpa, func.owner_nav, em); | ||
| 1446 | return; | ||
| 1447 | }, | ||
| 1448 | }; | ||
| 1449 | 1451 | ||
| 1450 | const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code); | 1452 | const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code); |
| 1451 | log.debug("setting shdr({x},{s}) for {}", .{ | 1453 | log.debug("setting shdr({x},{s}) for {}", .{ |
| ... | @@ -1463,7 +1465,8 @@ pub fn updateFunc( | ... | @@ -1463,7 +1465,8 @@ pub fn updateFunc( |
| 1463 | break :blk .{ atom_ptr.value, atom_ptr.alignment }; | 1465 | break :blk .{ atom_ptr.value, atom_ptr.alignment }; |
| 1464 | }; | 1466 | }; |
| 1465 | 1467 | ||
| 1466 | if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav); | 1468 | if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err| |
| 1469 | return elf_file.base.cgFail(func.owner_nav, "failed to finish dwarf function: {s}", .{@errorName(err)}); | ||
| 1467 | 1470 | ||
| 1468 | // Exports will be updated by `Zcu.processExports` after the update. | 1471 | // Exports will be updated by `Zcu.processExports` after the update. |
| 1469 | 1472 | ||
| ... | @@ -1511,7 +1514,8 @@ pub fn updateFunc( | ... | @@ -1511,7 +1514,8 @@ pub fn updateFunc( |
| 1511 | target_sym.flags.has_trampoline = true; | 1514 | target_sym.flags.has_trampoline = true; |
| 1512 | } | 1515 | } |
| 1513 | const target_sym = self.symbol(sym_index); | 1516 | const target_sym = self.symbol(sym_index); |
| 1514 | try writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file); | 1517 | writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file) catch |err| |
| 1518 | return elf_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)}); | ||
| 1515 | } | 1519 | } |
| 1516 | } | 1520 | } |
| 1517 | 1521 | ||
| ... | @@ -1547,7 +1551,11 @@ pub fn updateNav( | ... | @@ -1547,7 +1551,11 @@ pub fn updateNav( |
| 1547 | if (self.dwarf) |*dwarf| dwarf: { | 1551 | if (self.dwarf) |*dwarf| dwarf: { |
| 1548 | var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; | 1552 | var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; |
| 1549 | defer debug_wip_nav.deinit(); | 1553 | defer debug_wip_nav.deinit(); |
| 1550 | try dwarf.finishWipNav(pt, nav_index, &debug_wip_nav); | 1554 | dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) { |
| 1555 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1556 | error.Overflow => return error.Overflow, | ||
| 1557 | else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), | ||
| 1558 | }; | ||
| 1551 | } | 1559 | } |
| 1552 | return; | 1560 | return; |
| 1553 | }, | 1561 | }, |
| ... | @@ -1558,13 +1566,13 @@ pub fn updateNav( | ... | @@ -1558,13 +1566,13 @@ pub fn updateNav( |
| 1558 | const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index); | 1566 | const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index); |
| 1559 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); | 1567 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); |
| 1560 | 1568 | ||
| 1561 | var code_buffer = std.ArrayList(u8).init(zcu.gpa); | 1569 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1562 | defer code_buffer.deinit(); | 1570 | defer code_buffer.deinit(zcu.gpa); |
| 1563 | 1571 | ||
| 1564 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null; | 1572 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null; |
| 1565 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); | 1573 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 1566 | 1574 | ||
| 1567 | const res = try codegen.generateSymbol( | 1575 | try codegen.generateSymbol( |
| 1568 | &elf_file.base, | 1576 | &elf_file.base, |
| 1569 | pt, | 1577 | pt, |
| 1570 | zcu.navSrcLoc(nav_index), | 1578 | zcu.navSrcLoc(nav_index), |
| ... | @@ -1572,14 +1580,7 @@ pub fn updateNav( | ... | @@ -1572,14 +1580,7 @@ pub fn updateNav( |
| 1572 | &code_buffer, | 1580 | &code_buffer, |
| 1573 | .{ .atom_index = sym_index }, | 1581 | .{ .atom_index = sym_index }, |
| 1574 | ); | 1582 | ); |
| 1575 | 1583 | const code = code_buffer.items; | |
| 1576 | const code = switch (res) { | ||
| 1577 | .ok => code_buffer.items, | ||
| 1578 | .fail => |em| { | ||
| 1579 | try zcu.failed_codegen.put(zcu.gpa, nav_index, em); | ||
| 1580 | return; | ||
| 1581 | }, | ||
| 1582 | }; | ||
| 1583 | 1584 | ||
| 1584 | const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code); | 1585 | const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code); |
| 1585 | log.debug("setting shdr({x},{s}) for {}", .{ | 1586 | log.debug("setting shdr({x},{s}) for {}", .{ |
| ... | @@ -1592,7 +1593,11 @@ pub fn updateNav( | ... | @@ -1592,7 +1593,11 @@ pub fn updateNav( |
| 1592 | else | 1593 | else |
| 1593 | try self.updateNavCode(elf_file, pt, nav_index, sym_index, shndx, code, elf.STT_OBJECT); | 1594 | try self.updateNavCode(elf_file, pt, nav_index, sym_index, shndx, code, elf.STT_OBJECT); |
| 1594 | 1595 | ||
| 1595 | if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNav(pt, nav_index, wip_nav); | 1596 | if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) { |
| 1597 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1598 | error.Overflow => return error.Overflow, | ||
| 1599 | else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), | ||
| 1600 | }; | ||
| 1596 | } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); | 1601 | } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); |
| 1597 | 1602 | ||
| 1598 | // Exports will be updated by `Zcu.processExports` after the update. | 1603 | // Exports will be updated by `Zcu.processExports` after the update. |
| ... | @@ -1602,7 +1607,7 @@ pub fn updateContainerType( | ... | @@ -1602,7 +1607,7 @@ pub fn updateContainerType( |
| 1602 | self: *ZigObject, | 1607 | self: *ZigObject, |
| 1603 | pt: Zcu.PerThread, | 1608 | pt: Zcu.PerThread, |
| 1604 | ty: InternPool.Index, | 1609 | ty: InternPool.Index, |
| 1605 | ) link.File.UpdateNavError!void { | 1610 | ) !void { |
| 1606 | const tracy = trace(@src()); | 1611 | const tracy = trace(@src()); |
| 1607 | defer tracy.end(); | 1612 | defer tracy.end(); |
| 1608 | 1613 | ||
| ... | @@ -1620,8 +1625,8 @@ fn updateLazySymbol( | ... | @@ -1620,8 +1625,8 @@ fn updateLazySymbol( |
| 1620 | const gpa = zcu.gpa; | 1625 | const gpa = zcu.gpa; |
| 1621 | 1626 | ||
| 1622 | var required_alignment: InternPool.Alignment = .none; | 1627 | var required_alignment: InternPool.Alignment = .none; |
| 1623 | var code_buffer = std.ArrayList(u8).init(gpa); | 1628 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1624 | defer code_buffer.deinit(); | 1629 | defer code_buffer.deinit(gpa); |
| 1625 | 1630 | ||
| 1626 | const name_str_index = blk: { | 1631 | const name_str_index = blk: { |
| 1627 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ | 1632 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ |
| ... | @@ -1633,7 +1638,7 @@ fn updateLazySymbol( | ... | @@ -1633,7 +1638,7 @@ fn updateLazySymbol( |
| 1633 | }; | 1638 | }; |
| 1634 | 1639 | ||
| 1635 | const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; | 1640 | const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1636 | const res = try codegen.generateLazySymbol( | 1641 | try codegen.generateLazySymbol( |
| 1637 | &elf_file.base, | 1642 | &elf_file.base, |
| 1638 | pt, | 1643 | pt, |
| 1639 | src, | 1644 | src, |
| ... | @@ -1643,13 +1648,7 @@ fn updateLazySymbol( | ... | @@ -1643,13 +1648,7 @@ fn updateLazySymbol( |
| 1643 | .none, | 1648 | .none, |
| 1644 | .{ .atom_index = symbol_index }, | 1649 | .{ .atom_index = symbol_index }, |
| 1645 | ); | 1650 | ); |
| 1646 | const code = switch (res) { | 1651 | const code = code_buffer.items; |
| 1647 | .ok => code_buffer.items, | ||
| 1648 | .fail => |em| { | ||
| 1649 | log.err("{s}", .{em.msg}); | ||
| 1650 | return error.CodegenFail; | ||
| 1651 | }, | ||
| 1652 | }; | ||
| 1653 | 1652 | ||
| 1654 | const output_section_index = switch (sym.kind) { | 1653 | const output_section_index = switch (sym.kind) { |
| 1655 | .code => if (self.text_index) |sym_index| | 1654 | .code => if (self.text_index) |sym_index| |
| ... | @@ -1696,7 +1695,7 @@ fn updateLazySymbol( | ... | @@ -1696,7 +1695,7 @@ fn updateLazySymbol( |
| 1696 | local_sym.value = 0; | 1695 | local_sym.value = 0; |
| 1697 | local_esym.st_value = 0; | 1696 | local_esym.st_value = 0; |
| 1698 | 1697 | ||
| 1699 | try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file)); | 1698 | try elf_file.pwriteAll(code, atom_ptr.offset(elf_file)); |
| 1700 | } | 1699 | } |
| 1701 | 1700 | ||
| 1702 | const LowerConstResult = union(enum) { | 1701 | const LowerConstResult = union(enum) { |
| ... | @@ -1716,13 +1715,13 @@ fn lowerConst( | ... | @@ -1716,13 +1715,13 @@ fn lowerConst( |
| 1716 | ) !LowerConstResult { | 1715 | ) !LowerConstResult { |
| 1717 | const gpa = pt.zcu.gpa; | 1716 | const gpa = pt.zcu.gpa; |
| 1718 | 1717 | ||
| 1719 | var code_buffer = std.ArrayList(u8).init(gpa); | 1718 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1720 | defer code_buffer.deinit(); | 1719 | defer code_buffer.deinit(gpa); |
| 1721 | 1720 | ||
| 1722 | const name_off = try self.addString(gpa, name); | 1721 | const name_off = try self.addString(gpa, name); |
| 1723 | const sym_index = try self.newSymbolWithAtom(gpa, name_off); | 1722 | const sym_index = try self.newSymbolWithAtom(gpa, name_off); |
| 1724 | 1723 | ||
| 1725 | const res = try codegen.generateSymbol( | 1724 | try codegen.generateSymbol( |
| 1726 | &elf_file.base, | 1725 | &elf_file.base, |
| 1727 | pt, | 1726 | pt, |
| 1728 | src_loc, | 1727 | src_loc, |
| ... | @@ -1730,10 +1729,7 @@ fn lowerConst( | ... | @@ -1730,10 +1729,7 @@ fn lowerConst( |
| 1730 | &code_buffer, | 1729 | &code_buffer, |
| 1731 | .{ .atom_index = sym_index }, | 1730 | .{ .atom_index = sym_index }, |
| 1732 | ); | 1731 | ); |
| 1733 | const code = switch (res) { | 1732 | const code = code_buffer.items; |
| 1734 | .ok => code_buffer.items, | ||
| 1735 | .fail => |em| return .{ .fail = em }, | ||
| 1736 | }; | ||
| 1737 | 1733 | ||
| 1738 | const local_sym = self.symbol(sym_index); | 1734 | const local_sym = self.symbol(sym_index); |
| 1739 | const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index]; | 1735 | const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index]; |
| ... | @@ -1748,7 +1744,7 @@ fn lowerConst( | ... | @@ -1748,7 +1744,7 @@ fn lowerConst( |
| 1748 | try self.allocateAtom(atom_ptr, true, elf_file); | 1744 | try self.allocateAtom(atom_ptr, true, elf_file); |
| 1749 | errdefer self.freeNavMetadata(elf_file, sym_index); | 1745 | errdefer self.freeNavMetadata(elf_file, sym_index); |
| 1750 | 1746 | ||
| 1751 | try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file)); | 1747 | try elf_file.pwriteAll(code, atom_ptr.offset(elf_file)); |
| 1752 | 1748 | ||
| 1753 | return .{ .ok = sym_index }; | 1749 | return .{ .ok = sym_index }; |
| 1754 | } | 1750 | } |
| ... | @@ -1758,7 +1754,7 @@ pub fn updateExports( | ... | @@ -1758,7 +1754,7 @@ pub fn updateExports( |
| 1758 | elf_file: *Elf, | 1754 | elf_file: *Elf, |
| 1759 | pt: Zcu.PerThread, | 1755 | pt: Zcu.PerThread, |
| 1760 | exported: Zcu.Exported, | 1756 | exported: Zcu.Exported, |
| 1761 | export_indices: []const u32, | 1757 | export_indices: []const Zcu.Export.Index, |
| 1762 | ) link.File.UpdateExportsError!void { | 1758 | ) link.File.UpdateExportsError!void { |
| 1763 | const tracy = trace(@src()); | 1759 | const tracy = trace(@src()); |
| 1764 | defer tracy.end(); | 1760 | defer tracy.end(); |
| ... | @@ -1771,7 +1767,7 @@ pub fn updateExports( | ... | @@ -1771,7 +1767,7 @@ pub fn updateExports( |
| 1771 | break :blk self.navs.getPtr(nav).?; | 1767 | break :blk self.navs.getPtr(nav).?; |
| 1772 | }, | 1768 | }, |
| 1773 | .uav => |uav| self.uavs.getPtr(uav) orelse blk: { | 1769 | .uav => |uav| self.uavs.getPtr(uav) orelse blk: { |
| 1774 | const first_exp = zcu.all_exports.items[export_indices[0]]; | 1770 | const first_exp = export_indices[0].ptr(zcu); |
| 1775 | const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src); | 1771 | const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src); |
| 1776 | switch (res) { | 1772 | switch (res) { |
| 1777 | .mcv => {}, | 1773 | .mcv => {}, |
| ... | @@ -1792,7 +1788,7 @@ pub fn updateExports( | ... | @@ -1792,7 +1788,7 @@ pub fn updateExports( |
| 1792 | const esym_shndx = self.symtab.items(.shndx)[esym_index]; | 1788 | const esym_shndx = self.symtab.items(.shndx)[esym_index]; |
| 1793 | 1789 | ||
| 1794 | for (export_indices) |export_idx| { | 1790 | for (export_indices) |export_idx| { |
| 1795 | const exp = zcu.all_exports.items[export_idx]; | 1791 | const exp = export_idx.ptr(zcu); |
| 1796 | if (exp.opts.section.unwrap()) |section_name| { | 1792 | if (exp.opts.section.unwrap()) |section_name| { |
| 1797 | if (!section_name.eqlSlice(".text", &zcu.intern_pool)) { | 1793 | if (!section_name.eqlSlice(".text", &zcu.intern_pool)) { |
| 1798 | try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); | 1794 | try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); |
| ... | @@ -1849,7 +1845,13 @@ pub fn updateExports( | ... | @@ -1849,7 +1845,13 @@ pub fn updateExports( |
| 1849 | 1845 | ||
| 1850 | pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { | 1846 | pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { |
| 1851 | if (self.dwarf) |*dwarf| { | 1847 | if (self.dwarf) |*dwarf| { |
| 1852 | try dwarf.updateLineNumber(pt.zcu, ti_id); | 1848 | const comp = dwarf.bin_file.comp; |
| 1849 | const diags = &comp.link_diags; | ||
| 1850 | dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { | ||
| 1851 | error.Overflow => return error.Overflow, | ||
| 1852 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1853 | else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), | ||
| 1854 | }; | ||
| 1853 | } | 1855 | } |
| 1854 | } | 1856 | } |
| 1855 | 1857 | ||
| ... | @@ -1935,8 +1937,8 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e | ... | @@ -1935,8 +1937,8 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e |
| 1935 | const shdr = &slice.items(.shdr)[atom_ptr.output_section_index]; | 1937 | const shdr = &slice.items(.shdr)[atom_ptr.output_section_index]; |
| 1936 | const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index]; | 1938 | const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index]; |
| 1937 | 1939 | ||
| 1938 | // FIXME:JK this only works if this atom is the only atom in the output section | 1940 | // This only works if this atom is the only atom in the output section. In |
| 1939 | // In every other case, we need to redo the prev/next links | 1941 | // every other case, we need to redo the prev/next links. |
| 1940 | if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{}; | 1942 | if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{}; |
| 1941 | 1943 | ||
| 1942 | const alloc_res = try elf_file.allocateChunk(.{ | 1944 | const alloc_res = try elf_file.allocateChunk(.{ |
src/link/Elf/eh_frame.zig+1-1| ... | @@ -611,7 +611,7 @@ fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void { | ... | @@ -611,7 +611,7 @@ fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void { |
| 611 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), | 611 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), |
| 612 | rel.r_offset, | 612 | rel.r_offset, |
| 613 | }); | 613 | }); |
| 614 | try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()}); | 614 | err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()}); |
| 615 | return error.RelocFailure; | 615 | return error.RelocFailure; |
| 616 | } | 616 | } |
| 617 | 617 |
src/link/Elf/relocatable.zig+6-6| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void { | 1 | pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void { |
| 2 | const gpa = comp.gpa; | 2 | const gpa = comp.gpa; |
| 3 | const diags = &comp.link_diags; | 3 | const diags = &comp.link_diags; |
| 4 | 4 | ||
| 5 | if (diags.hasErrors()) return error.FlushFailure; | 5 | if (diags.hasErrors()) return error.LinkFailure; |
| 6 | 6 | ||
| 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); |
| 129 | 129 | ||
| 130 | if (diags.hasErrors()) return error.FlushFailure; | 130 | if (diags.hasErrors()) return error.LinkFailure; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void { | 133 | pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void { |
| 134 | const diags = &comp.link_diags; | 134 | const diags = &comp.link_diags; |
| 135 | 135 | ||
| 136 | if (diags.hasErrors()) return error.FlushFailure; | 136 | if (diags.hasErrors()) return error.LinkFailure; |
| 137 | 137 | ||
| 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 parsed | 139 | // 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(); |
| 181 | 181 | ||
| 182 | if (diags.hasErrors()) return error.FlushFailure; | 182 | if (diags.hasErrors()) return error.LinkFailure; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | fn claimUnresolved(elf_file: *Elf) void { | 185 | fn claimUnresolved(elf_file: *Elf) void { |
src/link/MachO.zig+125-56| ... | @@ -434,7 +434,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -434,7 +434,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 434 | // libc/libSystem dep | 434 | // libc/libSystem dep |
| 435 | self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { | 435 | self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { |
| 436 | error.MissingLibSystem => {}, // already reported | 436 | error.MissingLibSystem => {}, // already reported |
| 437 | else => |e| return e, // TODO: convert into an error | 437 | else => |e| return diags.fail("failed to resolve libSystem: {s}", .{@errorName(e)}), |
| 438 | }; | 438 | }; |
| 439 | 439 | ||
| 440 | for (comp.link_inputs) |link_input| switch (link_input) { | 440 | for (comp.link_inputs) |link_input| switch (link_input) { |
| ... | @@ -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 | }; |
| 483 | 483 | ||
| 484 | if (diags.hasErrors()) return error.FlushFailure; | 484 | if (diags.hasErrors()) return error.LinkFailure; |
| 485 | 485 | ||
| 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))); |
| ... | @@ -494,14 +494,17 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -494,14 +494,17 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 494 | 494 | ||
| 495 | try self.resolveSymbols(); | 495 | try self.resolveSymbols(); |
| 496 | try self.convertTentativeDefsAndResolveSpecialSymbols(); | 496 | try self.convertTentativeDefsAndResolveSpecialSymbols(); |
| 497 | try self.dedupLiterals(); | 497 | self.dedupLiterals() catch |err| switch (err) { |
| 498 | error.LinkFailure => return error.LinkFailure, | ||
| 499 | else => |e| return diags.fail("failed to deduplicate literals: {s}", .{@errorName(e)}), | ||
| 500 | }; | ||
| 498 | 501 | ||
| 499 | if (self.base.gc_sections) { | 502 | if (self.base.gc_sections) { |
| 500 | try dead_strip.gcAtoms(self); | 503 | try dead_strip.gcAtoms(self); |
| 501 | } | 504 | } |
| 502 | 505 | ||
| 503 | self.checkDuplicates() catch |err| switch (err) { | 506 | self.checkDuplicates() catch |err| switch (err) { |
| 504 | error.HasDuplicates => return error.FlushFailure, | 507 | error.HasDuplicates => return error.LinkFailure, |
| 505 | else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}), | 508 | else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}), |
| 506 | }; | 509 | }; |
| 507 | 510 | ||
| ... | @@ -516,7 +519,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -516,7 +519,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 516 | self.claimUnresolved(); | 519 | self.claimUnresolved(); |
| 517 | 520 | ||
| 518 | self.scanRelocs() catch |err| switch (err) { | 521 | self.scanRelocs() catch |err| switch (err) { |
| 519 | error.HasUndefinedSymbols => return error.FlushFailure, | 522 | error.HasUndefinedSymbols => return error.LinkFailure, |
| 520 | else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}), | 523 | else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}), |
| 521 | }; | 524 | }; |
| 522 | 525 | ||
| ... | @@ -529,7 +532,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -529,7 +532,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 529 | try self.generateUnwindInfo(); | 532 | try self.generateUnwindInfo(); |
| 530 | 533 | ||
| 531 | try self.initSegments(); | 534 | try self.initSegments(); |
| 532 | try self.allocateSections(); | 535 | self.allocateSections() catch |err| switch (err) { |
| 536 | error.LinkFailure => return error.LinkFailure, | ||
| 537 | else => |e| return diags.fail("failed to allocate sections: {s}", .{@errorName(e)}), | ||
| 538 | }; | ||
| 533 | self.allocateSegments(); | 539 | self.allocateSegments(); |
| 534 | self.allocateSyntheticSymbols(); | 540 | self.allocateSyntheticSymbols(); |
| 535 | 541 | ||
| ... | @@ -543,7 +549,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -543,7 +549,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 543 | 549 | ||
| 544 | if (self.getZigObject()) |zo| { | 550 | if (self.getZigObject()) |zo| { |
| 545 | zo.resolveRelocs(self) catch |err| switch (err) { | 551 | zo.resolveRelocs(self) catch |err| switch (err) { |
| 546 | error.ResolveFailed => return error.FlushFailure, | 552 | error.ResolveFailed => return error.LinkFailure, |
| 547 | else => |e| return e, | 553 | else => |e| return e, |
| 548 | }; | 554 | }; |
| 549 | } | 555 | } |
| ... | @@ -551,7 +557,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -551,7 +557,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 551 | 557 | ||
| 552 | try self.writeSectionsToFile(); | 558 | try self.writeSectionsToFile(); |
| 553 | try self.allocateLinkeditSegment(); | 559 | try self.allocateLinkeditSegment(); |
| 554 | try self.writeLinkeditSectionsToFile(); | 560 | self.writeLinkeditSectionsToFile() catch |err| switch (err) { |
| 561 | error.OutOfMemory => return error.OutOfMemory, | ||
| 562 | error.LinkFailure => return error.LinkFailure, | ||
| 563 | else => |e| return diags.fail("failed to write linkedit sections to file: {s}", .{@errorName(e)}), | ||
| 564 | }; | ||
| 555 | 565 | ||
| 556 | var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: { | 566 | var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: { |
| 557 | // Preallocate space for the code signature. | 567 | // Preallocate space for the code signature. |
| ... | @@ -561,7 +571,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -561,7 +571,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 561 | // where the code signature goes into. | 571 | // where the code signature goes into. |
| 562 | var codesig = CodeSignature.init(self.getPageSize()); | 572 | var codesig = CodeSignature.init(self.getPageSize()); |
| 563 | codesig.code_directory.ident = fs.path.basename(self.base.emit.sub_path); | 573 | codesig.code_directory.ident = fs.path.basename(self.base.emit.sub_path); |
| 564 | if (self.entitlements) |path| try codesig.addEntitlements(gpa, path); | 574 | if (self.entitlements) |path| codesig.addEntitlements(gpa, path) catch |err| |
| 575 | return diags.fail("failed to add entitlements from {s}: {s}", .{ path, @errorName(err) }); | ||
| 565 | try self.writeCodeSignaturePadding(&codesig); | 576 | try self.writeCodeSignaturePadding(&codesig); |
| 566 | break :blk codesig; | 577 | break :blk codesig; |
| 567 | } else null; | 578 | } else null; |
| ... | @@ -573,15 +584,34 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -573,15 +584,34 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 573 | self.getPageSize(), | 584 | self.getPageSize(), |
| 574 | ); | 585 | ); |
| 575 | 586 | ||
| 576 | const ncmds, const sizeofcmds, const uuid_cmd_offset = try self.writeLoadCommands(); | 587 | const ncmds, const sizeofcmds, const uuid_cmd_offset = self.writeLoadCommands() catch |err| switch (err) { |
| 588 | error.NoSpaceLeft => unreachable, | ||
| 589 | error.OutOfMemory => return error.OutOfMemory, | ||
| 590 | error.LinkFailure => return error.LinkFailure, | ||
| 591 | }; | ||
| 577 | try self.writeHeader(ncmds, sizeofcmds); | 592 | try self.writeHeader(ncmds, sizeofcmds); |
| 578 | try self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()); | 593 | self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) { |
| 579 | if (self.getDebugSymbols()) |dsym| try dsym.flushModule(self); | 594 | error.OutOfMemory => return error.OutOfMemory, |
| 595 | error.LinkFailure => return error.LinkFailure, | ||
| 596 | else => |e| return diags.fail("failed to calculate and write uuid: {s}", .{@errorName(e)}), | ||
| 597 | }; | ||
| 598 | if (self.getDebugSymbols()) |dsym| dsym.flushModule(self) catch |err| switch (err) { | ||
| 599 | error.OutOfMemory => return error.OutOfMemory, | ||
| 600 | else => |e| return diags.fail("failed to get debug symbols: {s}", .{@errorName(e)}), | ||
| 601 | }; | ||
| 580 | 602 | ||
| 603 | // Code signing always comes last. | ||
| 581 | if (codesig) |*csig| { | 604 | if (codesig) |*csig| { |
| 582 | try self.writeCodeSignature(csig); // code signing always comes last | 605 | self.writeCodeSignature(csig) catch |err| switch (err) { |
| 606 | error.OutOfMemory => return error.OutOfMemory, | ||
| 607 | error.LinkFailure => return error.LinkFailure, | ||
| 608 | else => |e| return diags.fail("failed to write code signature: {s}", .{@errorName(e)}), | ||
| 609 | }; | ||
| 583 | const emit = self.base.emit; | 610 | const emit = self.base.emit; |
| 584 | try invalidateKernelCache(emit.root_dir.handle, emit.sub_path); | 611 | invalidateKernelCache(emit.root_dir.handle, emit.sub_path) catch |err| switch (err) { |
| 612 | error.OutOfMemory => return error.OutOfMemory, | ||
| 613 | else => |e| return diags.fail("failed to invalidate kernel cache: {s}", .{@errorName(e)}), | ||
| 614 | }; | ||
| 585 | } | 615 | } |
| 586 | } | 616 | } |
| 587 | 617 | ||
| ... | @@ -1545,21 +1575,21 @@ fn reportUndefs(self: *MachO) !void { | ... | @@ -1545,21 +1575,21 @@ fn reportUndefs(self: *MachO) !void { |
| 1545 | try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); | 1575 | try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); |
| 1546 | 1576 | ||
| 1547 | switch (notes) { | 1577 | switch (notes) { |
| 1548 | .force_undefined => try err.addNote("referenced with linker flag -u", .{}), | 1578 | .force_undefined => err.addNote("referenced with linker flag -u", .{}), |
| 1549 | .entry => try err.addNote("referenced with linker flag -e", .{}), | 1579 | .entry => err.addNote("referenced with linker flag -e", .{}), |
| 1550 | .dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}), | 1580 | .dyld_stub_binder, .objc_msgsend => err.addNote("referenced implicitly", .{}), |
| 1551 | .refs => |refs| { | 1581 | .refs => |refs| { |
| 1552 | var inote: usize = 0; | 1582 | var inote: usize = 0; |
| 1553 | while (inote < @min(refs.items.len, max_notes)) : (inote += 1) { | 1583 | while (inote < @min(refs.items.len, max_notes)) : (inote += 1) { |
| 1554 | const ref = refs.items[inote]; | 1584 | const ref = refs.items[inote]; |
| 1555 | const file = self.getFile(ref.file).?; | 1585 | const file = self.getFile(ref.file).?; |
| 1556 | const atom = ref.getAtom(self).?; | 1586 | const atom = ref.getAtom(self).?; |
| 1557 | try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) }); | 1587 | err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) }); |
| 1558 | } | 1588 | } |
| 1559 | 1589 | ||
| 1560 | if (refs.items.len > max_notes) { | 1590 | if (refs.items.len > max_notes) { |
| 1561 | const remaining = refs.items.len - max_notes; | 1591 | const remaining = refs.items.len - max_notes; |
| 1562 | try err.addNote("referenced {d} more times", .{remaining}); | 1592 | err.addNote("referenced {d} more times", .{remaining}); |
| 1563 | } | 1593 | } |
| 1564 | }, | 1594 | }, |
| 1565 | } | 1595 | } |
| ... | @@ -2171,7 +2201,7 @@ fn allocateSections(self: *MachO) !void { | ... | @@ -2171,7 +2201,7 @@ fn allocateSections(self: *MachO) !void { |
| 2171 | fileoff = mem.alignForward(u32, fileoff, page_size); | 2201 | fileoff = mem.alignForward(u32, fileoff, page_size); |
| 2172 | } | 2202 | } |
| 2173 | 2203 | ||
| 2174 | const alignment = try math.powi(u32, 2, header.@"align"); | 2204 | const alignment = try self.alignPow(header.@"align"); |
| 2175 | 2205 | ||
| 2176 | vmaddr = mem.alignForward(u64, vmaddr, alignment); | 2206 | vmaddr = mem.alignForward(u64, vmaddr, alignment); |
| 2177 | header.addr = vmaddr; | 2207 | header.addr = vmaddr; |
| ... | @@ -2327,7 +2357,7 @@ fn allocateLinkeditSegment(self: *MachO) !void { | ... | @@ -2327,7 +2357,7 @@ fn allocateLinkeditSegment(self: *MachO) !void { |
| 2327 | seg.vmaddr = mem.alignForward(u64, vmaddr, page_size); | 2357 | seg.vmaddr = mem.alignForward(u64, vmaddr, page_size); |
| 2328 | seg.fileoff = mem.alignForward(u64, fileoff, page_size); | 2358 | seg.fileoff = mem.alignForward(u64, fileoff, page_size); |
| 2329 | 2359 | ||
| 2330 | var off = math.cast(u32, seg.fileoff) orelse return error.Overflow; | 2360 | var off = try self.cast(u32, seg.fileoff); |
| 2331 | // DYLD_INFO_ONLY | 2361 | // DYLD_INFO_ONLY |
| 2332 | { | 2362 | { |
| 2333 | const cmd = &self.dyld_info_cmd; | 2363 | const cmd = &self.dyld_info_cmd; |
| ... | @@ -2392,7 +2422,7 @@ fn resizeSections(self: *MachO) !void { | ... | @@ -2392,7 +2422,7 @@ fn resizeSections(self: *MachO) !void { |
| 2392 | if (header.isZerofill()) continue; | 2422 | if (header.isZerofill()) continue; |
| 2393 | if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible | 2423 | if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible |
| 2394 | const cpu_arch = self.getTarget().cpu.arch; | 2424 | const cpu_arch = self.getTarget().cpu.arch; |
| 2395 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 2425 | const size = try self.cast(usize, header.size); |
| 2396 | try out.resize(self.base.comp.gpa, size); | 2426 | try out.resize(self.base.comp.gpa, size); |
| 2397 | const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; | 2427 | const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; |
| 2398 | @memset(out.items, padding_byte); | 2428 | @memset(out.items, padding_byte); |
| ... | @@ -2489,7 +2519,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void { | ... | @@ -2489,7 +2519,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void { |
| 2489 | 2519 | ||
| 2490 | const doWork = struct { | 2520 | const doWork = struct { |
| 2491 | fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void { | 2521 | fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void { |
| 2492 | const off = math.cast(usize, th.value) orelse return error.Overflow; | 2522 | const off = try macho_file.cast(usize, th.value); |
| 2493 | const size = th.size(); | 2523 | const size = th.size(); |
| 2494 | var stream = std.io.fixedBufferStream(buffer[off..][0..size]); | 2524 | var stream = std.io.fixedBufferStream(buffer[off..][0..size]); |
| 2495 | try th.write(macho_file, stream.writer()); | 2525 | try th.write(macho_file, stream.writer()); |
| ... | @@ -2601,7 +2631,7 @@ fn writeSectionsToFile(self: *MachO) !void { | ... | @@ -2601,7 +2631,7 @@ fn writeSectionsToFile(self: *MachO) !void { |
| 2601 | 2631 | ||
| 2602 | const slice = self.sections.slice(); | 2632 | const slice = self.sections.slice(); |
| 2603 | for (slice.items(.header), slice.items(.out)) |header, out| { | 2633 | for (slice.items(.header), slice.items(.out)) |header, out| { |
| 2604 | try self.base.file.?.pwriteAll(out.items, header.offset); | 2634 | try self.pwriteAll(out.items, header.offset); |
| 2605 | } | 2635 | } |
| 2606 | } | 2636 | } |
| 2607 | 2637 | ||
| ... | @@ -2644,7 +2674,7 @@ fn writeDyldInfo(self: *MachO) !void { | ... | @@ -2644,7 +2674,7 @@ fn writeDyldInfo(self: *MachO) !void { |
| 2644 | try self.lazy_bind_section.write(writer); | 2674 | try self.lazy_bind_section.write(writer); |
| 2645 | try stream.seekTo(cmd.export_off - base_off); | 2675 | try stream.seekTo(cmd.export_off - base_off); |
| 2646 | try self.export_trie.write(writer); | 2676 | try self.export_trie.write(writer); |
| 2647 | try self.base.file.?.pwriteAll(buffer, cmd.rebase_off); | 2677 | try self.pwriteAll(buffer, cmd.rebase_off); |
| 2648 | } | 2678 | } |
| 2649 | 2679 | ||
| 2650 | pub fn writeDataInCode(self: *MachO) !void { | 2680 | pub fn writeDataInCode(self: *MachO) !void { |
| ... | @@ -2655,7 +2685,7 @@ pub fn writeDataInCode(self: *MachO) !void { | ... | @@ -2655,7 +2685,7 @@ pub fn writeDataInCode(self: *MachO) !void { |
| 2655 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size()); | 2685 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size()); |
| 2656 | defer buffer.deinit(); | 2686 | defer buffer.deinit(); |
| 2657 | try self.data_in_code.write(self, buffer.writer()); | 2687 | try self.data_in_code.write(self, buffer.writer()); |
| 2658 | try self.base.file.?.pwriteAll(buffer.items, cmd.dataoff); | 2688 | try self.pwriteAll(buffer.items, cmd.dataoff); |
| 2659 | } | 2689 | } |
| 2660 | 2690 | ||
| 2661 | fn writeIndsymtab(self: *MachO) !void { | 2691 | fn writeIndsymtab(self: *MachO) !void { |
| ... | @@ -2667,15 +2697,15 @@ fn writeIndsymtab(self: *MachO) !void { | ... | @@ -2667,15 +2697,15 @@ fn writeIndsymtab(self: *MachO) !void { |
| 2667 | var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); | 2697 | var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); |
| 2668 | defer buffer.deinit(); | 2698 | defer buffer.deinit(); |
| 2669 | try self.indsymtab.write(self, buffer.writer()); | 2699 | try self.indsymtab.write(self, buffer.writer()); |
| 2670 | try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff); | 2700 | try self.pwriteAll(buffer.items, cmd.indirectsymoff); |
| 2671 | } | 2701 | } |
| 2672 | 2702 | ||
| 2673 | pub fn writeSymtabToFile(self: *MachO) !void { | 2703 | pub fn writeSymtabToFile(self: *MachO) !void { |
| 2674 | const tracy = trace(@src()); | 2704 | const tracy = trace(@src()); |
| 2675 | defer tracy.end(); | 2705 | defer tracy.end(); |
| 2676 | const cmd = self.symtab_cmd; | 2706 | const cmd = self.symtab_cmd; |
| 2677 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); | 2707 | try self.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); |
| 2678 | try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff); | 2708 | try self.pwriteAll(self.strtab.items, cmd.stroff); |
| 2679 | } | 2709 | } |
| 2680 | 2710 | ||
| 2681 | fn writeUnwindInfo(self: *MachO) !void { | 2711 | fn writeUnwindInfo(self: *MachO) !void { |
| ... | @@ -2686,20 +2716,20 @@ fn writeUnwindInfo(self: *MachO) !void { | ... | @@ -2686,20 +2716,20 @@ fn writeUnwindInfo(self: *MachO) !void { |
| 2686 | 2716 | ||
| 2687 | if (self.eh_frame_sect_index) |index| { | 2717 | if (self.eh_frame_sect_index) |index| { |
| 2688 | const header = self.sections.items(.header)[index]; | 2718 | const header = self.sections.items(.header)[index]; |
| 2689 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 2719 | const size = try self.cast(usize, header.size); |
| 2690 | const buffer = try gpa.alloc(u8, size); | 2720 | const buffer = try gpa.alloc(u8, size); |
| 2691 | defer gpa.free(buffer); | 2721 | defer gpa.free(buffer); |
| 2692 | eh_frame.write(self, buffer); | 2722 | eh_frame.write(self, buffer); |
| 2693 | try self.base.file.?.pwriteAll(buffer, header.offset); | 2723 | try self.pwriteAll(buffer, header.offset); |
| 2694 | } | 2724 | } |
| 2695 | 2725 | ||
| 2696 | if (self.unwind_info_sect_index) |index| { | 2726 | if (self.unwind_info_sect_index) |index| { |
| 2697 | const header = self.sections.items(.header)[index]; | 2727 | const header = self.sections.items(.header)[index]; |
| 2698 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 2728 | const size = try self.cast(usize, header.size); |
| 2699 | const buffer = try gpa.alloc(u8, size); | 2729 | const buffer = try gpa.alloc(u8, size); |
| 2700 | defer gpa.free(buffer); | 2730 | defer gpa.free(buffer); |
| 2701 | try self.unwind_info.write(self, buffer); | 2731 | try self.unwind_info.write(self, buffer); |
| 2702 | try self.base.file.?.pwriteAll(buffer, header.offset); | 2732 | try self.pwriteAll(buffer, header.offset); |
| 2703 | } | 2733 | } |
| 2704 | } | 2734 | } |
| 2705 | 2735 | ||
| ... | @@ -2890,7 +2920,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { | ... | @@ -2890,7 +2920,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { |
| 2890 | 2920 | ||
| 2891 | assert(stream.pos == needed_size); | 2921 | assert(stream.pos == needed_size); |
| 2892 | 2922 | ||
| 2893 | try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); | 2923 | try self.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); |
| 2894 | 2924 | ||
| 2895 | return .{ ncmds, buffer.len, uuid_cmd_offset }; | 2925 | return .{ ncmds, buffer.len, uuid_cmd_offset }; |
| 2896 | } | 2926 | } |
| ... | @@ -2944,7 +2974,7 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void { | ... | @@ -2944,7 +2974,7 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void { |
| 2944 | 2974 | ||
| 2945 | log.debug("writing Mach-O header {}", .{header}); | 2975 | log.debug("writing Mach-O header {}", .{header}); |
| 2946 | 2976 | ||
| 2947 | try self.base.file.?.pwriteAll(mem.asBytes(&header), 0); | 2977 | try self.pwriteAll(mem.asBytes(&header), 0); |
| 2948 | } | 2978 | } |
| 2949 | 2979 | ||
| 2950 | fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void { | 2980 | fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void { |
| ... | @@ -2954,7 +2984,7 @@ fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void { | ... | @@ -2954,7 +2984,7 @@ fn writeUuid(self: *MachO, uuid_cmd_offset: u64, has_codesig: bool) !void { |
| 2954 | } else self.codesig_cmd.dataoff; | 2984 | } else self.codesig_cmd.dataoff; |
| 2955 | try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid); | 2985 | try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid); |
| 2956 | const offset = uuid_cmd_offset + @sizeOf(macho.load_command); | 2986 | const offset = uuid_cmd_offset + @sizeOf(macho.load_command); |
| 2957 | try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset); | 2987 | try self.pwriteAll(&self.uuid_cmd.uuid, offset); |
| 2958 | } | 2988 | } |
| 2959 | 2989 | ||
| 2960 | pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { | 2990 | pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| ... | @@ -2968,7 +2998,7 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { | ... | @@ -2968,7 +2998,7 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 2968 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | 2998 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 2969 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | 2999 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| 2970 | // except for code signature data. | 3000 | // except for code signature data. |
| 2971 | try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1); | 3001 | try self.pwriteAll(&[_]u8{0}, offset + needed_size - 1); |
| 2972 | 3002 | ||
| 2973 | self.codesig_cmd.dataoff = @as(u32, @intCast(offset)); | 3003 | self.codesig_cmd.dataoff = @as(u32, @intCast(offset)); |
| 2974 | self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); | 3004 | self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); |
| ... | @@ -2995,10 +3025,16 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { | ... | @@ -2995,10 +3025,16 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { |
| 2995 | offset + buffer.items.len, | 3025 | offset + buffer.items.len, |
| 2996 | }); | 3026 | }); |
| 2997 | 3027 | ||
| 2998 | try self.base.file.?.pwriteAll(buffer.items, offset); | 3028 | try self.pwriteAll(buffer.items, offset); |
| 2999 | } | 3029 | } |
| 3000 | 3030 | ||
| 3001 | pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 3031 | pub fn updateFunc( |
| 3032 | self: *MachO, | ||
| 3033 | pt: Zcu.PerThread, | ||
| 3034 | func_index: InternPool.Index, | ||
| 3035 | air: Air, | ||
| 3036 | liveness: Liveness, | ||
| 3037 | ) link.File.UpdateNavError!void { | ||
| 3002 | if (build_options.skip_non_native and builtin.object_format != .macho) { | 3038 | 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"); | 3039 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 3004 | } | 3040 | } |
| ... | @@ -3006,7 +3042,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, | ... | @@ -3006,7 +3042,7 @@ pub fn updateFunc(self: *MachO, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 3006 | return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness); | 3042 | return self.getZigObject().?.updateFunc(self, pt, func_index, air, liveness); |
| 3007 | } | 3043 | } |
| 3008 | 3044 | ||
| 3009 | pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { | 3045 | pub 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) { | 3046 | 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"); | 3047 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 3012 | } | 3048 | } |
| ... | @@ -3023,7 +3059,7 @@ pub fn updateExports( | ... | @@ -3023,7 +3059,7 @@ pub fn updateExports( |
| 3023 | self: *MachO, | 3059 | self: *MachO, |
| 3024 | pt: Zcu.PerThread, | 3060 | pt: Zcu.PerThread, |
| 3025 | exported: Zcu.Exported, | 3061 | exported: Zcu.Exported, |
| 3026 | export_indices: []const u32, | 3062 | export_indices: []const Zcu.Export.Index, |
| 3027 | ) link.File.UpdateExportsError!void { | 3063 | ) link.File.UpdateExportsError!void { |
| 3028 | if (build_options.skip_non_native and builtin.object_format != .macho) { | 3064 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 3029 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 3065 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | @@ -3199,7 +3235,7 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64 | ... | @@ -3199,7 +3235,7 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64 |
| 3199 | const gpa = self.base.comp.gpa; | 3235 | const gpa = self.base.comp.gpa; |
| 3200 | try self.copyRangeAll(old_offset, new_offset, size); | 3236 | try self.copyRangeAll(old_offset, new_offset, size); |
| 3201 | const size_u = math.cast(usize, size) orelse return error.Overflow; | 3237 | const size_u = math.cast(usize, size) orelse return error.Overflow; |
| 3202 | const zeroes = try gpa.alloc(u8, size_u); | 3238 | const zeroes = try gpa.alloc(u8, size_u); // TODO no need to allocate here. |
| 3203 | defer gpa.free(zeroes); | 3239 | defer gpa.free(zeroes); |
| 3204 | @memset(zeroes, 0); | 3240 | @memset(zeroes, 0); |
| 3205 | try self.base.file.?.pwriteAll(zeroes, old_offset); | 3241 | try self.base.file.?.pwriteAll(zeroes, old_offset); |
| ... | @@ -3306,10 +3342,9 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { | ... | @@ -3306,10 +3342,9 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3306 | const allocSect = struct { | 3342 | const allocSect = struct { |
| 3307 | fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void { | 3343 | fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void { |
| 3308 | const sect = &macho_file.sections.items(.header)[sect_id]; | 3344 | const sect = &macho_file.sections.items(.header)[sect_id]; |
| 3309 | const alignment = try math.powi(u32, 2, sect.@"align"); | 3345 | const alignment = try macho_file.alignPow(sect.@"align"); |
| 3310 | if (!sect.isZerofill()) { | 3346 | if (!sect.isZerofill()) { |
| 3311 | sect.offset = math.cast(u32, try macho_file.findFreeSpace(size, alignment)) orelse | 3347 | sect.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(size, alignment)); |
| 3312 | return error.Overflow; | ||
| 3313 | } | 3348 | } |
| 3314 | sect.addr = macho_file.findFreeSpaceVirtual(size, alignment); | 3349 | sect.addr = macho_file.findFreeSpaceVirtual(size, alignment); |
| 3315 | sect.size = size; | 3350 | sect.size = size; |
| ... | @@ -3441,8 +3476,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo | ... | @@ -3441,8 +3476,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo |
| 3441 | seg_id, | 3476 | seg_id, |
| 3442 | seg.segName(), | 3477 | seg.segName(), |
| 3443 | }); | 3478 | }); |
| 3444 | try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{}); | 3479 | err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{}); |
| 3445 | try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); | 3480 | err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); |
| 3446 | } | 3481 | } |
| 3447 | 3482 | ||
| 3448 | seg.vmsize = needed_size; | 3483 | seg.vmsize = needed_size; |
| ... | @@ -3744,7 +3779,7 @@ pub fn reportParseError2( | ... | @@ -3744,7 +3779,7 @@ pub fn reportParseError2( |
| 3744 | const diags = &self.base.comp.link_diags; | 3779 | const diags = &self.base.comp.link_diags; |
| 3745 | var err = try diags.addErrorWithNotes(1); | 3780 | var err = try diags.addErrorWithNotes(1); |
| 3746 | try err.addMsg(format, args); | 3781 | try err.addMsg(format, args); |
| 3747 | try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()}); | 3782 | err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()}); |
| 3748 | } | 3783 | } |
| 3749 | 3784 | ||
| 3750 | fn reportMissingDependencyError( | 3785 | fn reportMissingDependencyError( |
| ... | @@ -3758,10 +3793,10 @@ fn reportMissingDependencyError( | ... | @@ -3758,10 +3793,10 @@ fn reportMissingDependencyError( |
| 3758 | const diags = &self.base.comp.link_diags; | 3793 | const diags = &self.base.comp.link_diags; |
| 3759 | var err = try diags.addErrorWithNotes(2 + checked_paths.len); | 3794 | var err = try diags.addErrorWithNotes(2 + checked_paths.len); |
| 3760 | try err.addMsg(format, args); | 3795 | try err.addMsg(format, args); |
| 3761 | try err.addNote("while resolving {s}", .{path}); | 3796 | err.addNote("while resolving {s}", .{path}); |
| 3762 | try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | 3797 | err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3763 | for (checked_paths) |p| { | 3798 | for (checked_paths) |p| { |
| 3764 | try err.addNote("tried {s}", .{p}); | 3799 | err.addNote("tried {s}", .{p}); |
| 3765 | } | 3800 | } |
| 3766 | } | 3801 | } |
| 3767 | 3802 | ||
| ... | @@ -3775,8 +3810,8 @@ fn reportDependencyError( | ... | @@ -3775,8 +3810,8 @@ fn reportDependencyError( |
| 3775 | const diags = &self.base.comp.link_diags; | 3810 | const diags = &self.base.comp.link_diags; |
| 3776 | var err = try diags.addErrorWithNotes(2); | 3811 | var err = try diags.addErrorWithNotes(2); |
| 3777 | try err.addMsg(format, args); | 3812 | try err.addMsg(format, args); |
| 3778 | try err.addNote("while parsing {s}", .{path}); | 3813 | err.addNote("while parsing {s}", .{path}); |
| 3779 | try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | 3814 | err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); |
| 3780 | } | 3815 | } |
| 3781 | 3816 | ||
| 3782 | fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { | 3817 | fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { |
| ... | @@ -3806,17 +3841,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { | ... | @@ -3806,17 +3841,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { |
| 3806 | 3841 | ||
| 3807 | var err = try diags.addErrorWithNotes(nnotes + 1); | 3842 | var err = try diags.addErrorWithNotes(nnotes + 1); |
| 3808 | try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); | 3843 | try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); |
| 3809 | try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); | 3844 | err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); |
| 3810 | 3845 | ||
| 3811 | var inote: usize = 0; | 3846 | var inote: usize = 0; |
| 3812 | while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { | 3847 | while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { |
| 3813 | const file = self.getFile(notes.items[inote]).?; | 3848 | const file = self.getFile(notes.items[inote]).?; |
| 3814 | try err.addNote("defined by {}", .{file.fmtPath()}); | 3849 | err.addNote("defined by {}", .{file.fmtPath()}); |
| 3815 | } | 3850 | } |
| 3816 | 3851 | ||
| 3817 | if (notes.items.len > max_notes) { | 3852 | if (notes.items.len > max_notes) { |
| 3818 | const remaining = notes.items.len - max_notes; | 3853 | const remaining = notes.items.len - max_notes; |
| 3819 | try err.addNote("defined {d} more times", .{remaining}); | 3854 | err.addNote("defined {d} more times", .{remaining}); |
| 3820 | } | 3855 | } |
| 3821 | } | 3856 | } |
| 3822 | return error.HasDuplicates; | 3857 | return error.HasDuplicates; |
| ... | @@ -5310,6 +5345,40 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { | ... | @@ -5310,6 +5345,40 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { |
| 5310 | return true; | 5345 | return true; |
| 5311 | } | 5346 | } |
| 5312 | 5347 | ||
| 5348 | pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{LinkFailure}!void { | ||
| 5349 | const comp = macho_file.base.comp; | ||
| 5350 | const diags = &comp.link_diags; | ||
| 5351 | macho_file.base.file.?.pwriteAll(bytes, offset) catch |err| { | ||
| 5352 | return diags.fail("failed to write: {s}", .{@errorName(err)}); | ||
| 5353 | }; | ||
| 5354 | } | ||
| 5355 | |||
| 5356 | pub fn setEndPos(macho_file: *MachO, length: u64) error{LinkFailure}!void { | ||
| 5357 | const comp = macho_file.base.comp; | ||
| 5358 | const diags = &comp.link_diags; | ||
| 5359 | macho_file.base.file.?.setEndPos(length) catch |err| { | ||
| 5360 | return diags.fail("failed to set file end pos: {s}", .{@errorName(err)}); | ||
| 5361 | }; | ||
| 5362 | } | ||
| 5363 | |||
| 5364 | pub fn cast(macho_file: *MachO, comptime T: type, x: anytype) error{LinkFailure}!T { | ||
| 5365 | return std.math.cast(T, x) orelse { | ||
| 5366 | const comp = macho_file.base.comp; | ||
| 5367 | const diags = &comp.link_diags; | ||
| 5368 | return diags.fail("encountered {d}, overflowing {d}-bit value", .{ x, @bitSizeOf(T) }); | ||
| 5369 | }; | ||
| 5370 | } | ||
| 5371 | |||
| 5372 | pub fn alignPow(macho_file: *MachO, x: u32) error{LinkFailure}!u32 { | ||
| 5373 | const result, const ov = @shlWithOverflow(@as(u32, 1), try cast(macho_file, u5, x)); | ||
| 5374 | if (ov != 0) { | ||
| 5375 | const comp = macho_file.base.comp; | ||
| 5376 | const diags = &comp.link_diags; | ||
| 5377 | return diags.fail("alignment overflow", .{}); | ||
| 5378 | } | ||
| 5379 | return result; | ||
| 5380 | } | ||
| 5381 | |||
| 5313 | /// Branch instruction has 26 bits immediate but is 4 byte aligned. | 5382 | /// Branch instruction has 26 bits immediate but is 4 byte aligned. |
| 5314 | const jump_bits = @bitSizeOf(i28); | 5383 | const jump_bits = @bitSizeOf(i28); |
| 5315 | const max_distance = (1 << (jump_bits - 1)); | 5384 | const max_distance = (1 << (jump_bits - 1)); |
src/link/MachO/Atom.zig+7-7| ... | @@ -909,8 +909,8 @@ const x86_64 = struct { | ... | @@ -909,8 +909,8 @@ const x86_64 = struct { |
| 909 | rel.offset, | 909 | rel.offset, |
| 910 | rel.fmtPretty(.x86_64), | 910 | rel.fmtPretty(.x86_64), |
| 911 | }); | 911 | }); |
| 912 | try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); | 912 | err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); |
| 913 | try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); | 913 | err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); |
| 914 | return error.RelaxFailUnexpectedInstruction; | 914 | return error.RelaxFailUnexpectedInstruction; |
| 915 | }, | 915 | }, |
| 916 | } | 916 | } |
| ... | @@ -971,7 +971,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { | ... | @@ -971,7 +971,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { |
| 971 | } | 971 | } |
| 972 | } | 972 | } |
| 973 | 973 | ||
| 974 | pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void { | 974 | pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) error{ LinkFailure, OutOfMemory }!void { |
| 975 | const tracy = trace(@src()); | 975 | const tracy = trace(@src()); |
| 976 | defer tracy.end(); | 976 | defer tracy.end(); |
| 977 | 977 | ||
| ... | @@ -983,15 +983,15 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r | ... | @@ -983,15 +983,15 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r |
| 983 | var i: usize = 0; | 983 | var i: usize = 0; |
| 984 | for (relocs) |rel| { | 984 | for (relocs) |rel| { |
| 985 | defer i += 1; | 985 | defer i += 1; |
| 986 | const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; | 986 | const rel_offset = try macho_file.cast(usize, rel.offset - self.off); |
| 987 | const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; | 987 | const r_address: i32 = try macho_file.cast(i32, self.value + rel_offset); |
| 988 | assert(r_address >= 0); | 988 | assert(r_address >= 0); |
| 989 | const r_symbolnum = r_symbolnum: { | 989 | const r_symbolnum = r_symbolnum: { |
| 990 | const r_symbolnum: u32 = switch (rel.tag) { | 990 | const r_symbolnum: u32 = switch (rel.tag) { |
| 991 | .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1, | 991 | .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1, |
| 992 | .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?, | 992 | .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?, |
| 993 | }; | 993 | }; |
| 994 | break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow; | 994 | break :r_symbolnum try macho_file.cast(u24, r_symbolnum); |
| 995 | }; | 995 | }; |
| 996 | const r_extern = rel.tag == .@"extern"; | 996 | const r_extern = rel.tag == .@"extern"; |
| 997 | var addend = rel.addend + rel.getRelocAddend(cpu_arch); | 997 | var addend = rel.addend + rel.getRelocAddend(cpu_arch); |
| ... | @@ -1027,7 +1027,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r | ... | @@ -1027,7 +1027,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r |
| 1027 | } else if (addend > 0) { | 1027 | } else if (addend > 0) { |
| 1028 | buffer[i] = .{ | 1028 | buffer[i] = .{ |
| 1029 | .r_address = r_address, | 1029 | .r_address = r_address, |
| 1030 | .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow), | 1030 | .r_symbolnum = @bitCast(try macho_file.cast(i24, addend)), |
| 1031 | .r_pcrel = 0, | 1031 | .r_pcrel = 0, |
| 1032 | .r_length = 2, | 1032 | .r_length = 2, |
| 1033 | .r_extern = 0, | 1033 | .r_extern = 0, |
src/link/MachO/InternalObject.zig+9-7| ... | @@ -414,10 +414,11 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file | ... | @@ -414,10 +414,11 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file |
| 414 | const rel = relocs[0]; | 414 | const rel = relocs[0]; |
| 415 | assert(rel.tag == .@"extern"); | 415 | assert(rel.tag == .@"extern"); |
| 416 | const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?; | 416 | const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?; |
| 417 | const target_size = std.math.cast(usize, target.size) orelse return error.Overflow; | 417 | const target_size = try macho_file.cast(usize, target.size); |
| 418 | try buffer.ensureUnusedCapacity(target_size); | 418 | try buffer.ensureUnusedCapacity(target_size); |
| 419 | buffer.resize(target_size) catch unreachable; | 419 | buffer.resize(target_size) catch unreachable; |
| 420 | @memcpy(buffer.items, try self.getSectionData(target.n_sect)); | 420 | const section_data = try self.getSectionData(target.n_sect, macho_file); |
| 421 | @memcpy(buffer.items, section_data); | ||
| 421 | const res = try lp.insert(gpa, header.type(), buffer.items); | 422 | const res = try lp.insert(gpa, header.type(), buffer.items); |
| 422 | buffer.clearRetainingCapacity(); | 423 | buffer.clearRetainingCapacity(); |
| 423 | if (!res.found_existing) { | 424 | if (!res.found_existing) { |
| ... | @@ -607,10 +608,11 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void { | ... | @@ -607,10 +608,11 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void { |
| 607 | if (!atom.isAlive()) continue; | 608 | if (!atom.isAlive()) continue; |
| 608 | const sect = atom.getInputSection(macho_file); | 609 | const sect = atom.getInputSection(macho_file); |
| 609 | if (sect.isZerofill()) continue; | 610 | if (sect.isZerofill()) continue; |
| 610 | const off = std.math.cast(usize, atom.value) orelse return error.Overflow; | 611 | const off = try macho_file.cast(usize, atom.value); |
| 611 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | 612 | const size = try macho_file.cast(usize, atom.size); |
| 612 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size]; | 613 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size]; |
| 613 | @memcpy(buffer, try self.getSectionData(atom.n_sect)); | 614 | const section_data = try self.getSectionData(atom.n_sect, macho_file); |
| 615 | @memcpy(buffer, section_data); | ||
| 614 | try atom.resolveRelocs(macho_file, buffer); | 616 | try atom.resolveRelocs(macho_file, buffer); |
| 615 | } | 617 | } |
| 616 | } | 618 | } |
| ... | @@ -644,13 +646,13 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8, | ... | @@ -644,13 +646,13 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8, |
| 644 | return n_sect; | 646 | return n_sect; |
| 645 | } | 647 | } |
| 646 | 648 | ||
| 647 | fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]const u8 { | 649 | fn getSectionData(self: *const InternalObject, index: u32, macho_file: *MachO) error{LinkFailure}![]const u8 { |
| 648 | const slice = self.sections.slice(); | 650 | const slice = self.sections.slice(); |
| 649 | assert(index < slice.items(.header).len); | 651 | assert(index < slice.items(.header).len); |
| 650 | const sect = slice.items(.header)[index]; | 652 | const sect = slice.items(.header)[index]; |
| 651 | const extra = slice.items(.extra)[index]; | 653 | const extra = slice.items(.extra)[index]; |
| 652 | if (extra.is_objc_methname) { | 654 | if (extra.is_objc_methname) { |
| 653 | const size = std.math.cast(usize, sect.size) orelse return error.Overflow; | 655 | const size = try macho_file.cast(usize, sect.size); |
| 654 | return self.objc_methnames.items[sect.offset..][0..size]; | 656 | return self.objc_methnames.items[sect.offset..][0..size]; |
| 655 | } else if (extra.is_objc_selref) | 657 | } else if (extra.is_objc_selref) |
| 656 | return &self.objc_selrefs | 658 | return &self.objc_selrefs |
src/link/MachO/Object.zig+32-34| ... | @@ -582,7 +582,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) | ... | @@ -582,7 +582,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) |
| 582 | ); | 582 | ); |
| 583 | return error.MalformedObject; | 583 | return error.MalformedObject; |
| 584 | } | 584 | } |
| 585 | const num_ptrs = math.cast(usize, @divExact(sect.size, rec_size)) orelse return error.Overflow; | 585 | const num_ptrs = try macho_file.cast(usize, @divExact(sect.size, rec_size)); |
| 586 | 586 | ||
| 587 | for (0..num_ptrs) |i| { | 587 | for (0..num_ptrs) |i| { |
| 588 | const pos: u32 = @as(u32, @intCast(i)) * rec_size; | 588 | const pos: u32 = @as(u32, @intCast(i)) * rec_size; |
| ... | @@ -650,8 +650,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO | ... | @@ -650,8 +650,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO |
| 650 | 650 | ||
| 651 | for (subs.items) |sub| { | 651 | for (subs.items) |sub| { |
| 652 | const atom = self.getAtom(sub.atom).?; | 652 | const atom = self.getAtom(sub.atom).?; |
| 653 | const atom_off = math.cast(usize, atom.off) orelse return error.Overflow; | 653 | const atom_off = try macho_file.cast(usize, atom.off); |
| 654 | const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; | 654 | const atom_size = try macho_file.cast(usize, atom.size); |
| 655 | const atom_data = data[atom_off..][0..atom_size]; | 655 | const atom_data = data[atom_off..][0..atom_size]; |
| 656 | const res = try lp.insert(gpa, header.type(), atom_data); | 656 | const res = try lp.insert(gpa, header.type(), atom_data); |
| 657 | if (!res.found_existing) { | 657 | if (!res.found_existing) { |
| ... | @@ -674,8 +674,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO | ... | @@ -674,8 +674,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO |
| 674 | .local => rel.getTargetAtom(atom.*, macho_file), | 674 | .local => rel.getTargetAtom(atom.*, macho_file), |
| 675 | .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?, | 675 | .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?, |
| 676 | }; | 676 | }; |
| 677 | const addend = math.cast(u32, rel.addend) orelse return error.Overflow; | 677 | const addend = try macho_file.cast(u32, rel.addend); |
| 678 | const target_size = math.cast(usize, target.size) orelse return error.Overflow; | 678 | const target_size = try macho_file.cast(usize, target.size); |
| 679 | try buffer.ensureUnusedCapacity(target_size); | 679 | try buffer.ensureUnusedCapacity(target_size); |
| 680 | buffer.resize(target_size) catch unreachable; | 680 | buffer.resize(target_size) catch unreachable; |
| 681 | const gop = try sections_data.getOrPut(target.n_sect); | 681 | const gop = try sections_data.getOrPut(target.n_sect); |
| ... | @@ -683,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO | ... | @@ -683,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO |
| 683 | gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect)); | 683 | gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect)); |
| 684 | } | 684 | } |
| 685 | const data = gop.value_ptr.*; | 685 | const data = gop.value_ptr.*; |
| 686 | const target_off = math.cast(usize, target.off) orelse return error.Overflow; | 686 | const target_off = try macho_file.cast(usize, target.off); |
| 687 | @memcpy(buffer.items, data[target_off..][0..target_size]); | 687 | @memcpy(buffer.items, data[target_off..][0..target_size]); |
| 688 | const res = try lp.insert(gpa, header.type(), buffer.items[addend..]); | 688 | const res = try lp.insert(gpa, header.type(), buffer.items[addend..]); |
| 689 | buffer.clearRetainingCapacity(); | 689 | buffer.clearRetainingCapacity(); |
| ... | @@ -1033,7 +1033,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi | ... | @@ -1033,7 +1033,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi |
| 1033 | const sect = slice.items(.header)[sect_id]; | 1033 | const sect = slice.items(.header)[sect_id]; |
| 1034 | const relocs = slice.items(.relocs)[sect_id]; | 1034 | const relocs = slice.items(.relocs)[sect_id]; |
| 1035 | 1035 | ||
| 1036 | const size = math.cast(usize, sect.size) orelse return error.Overflow; | 1036 | const size = try macho_file.cast(usize, sect.size); |
| 1037 | try self.eh_frame_data.resize(allocator, size); | 1037 | try self.eh_frame_data.resize(allocator, size); |
| 1038 | const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset); | 1038 | const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset); |
| 1039 | if (amt != self.eh_frame_data.items.len) return error.InputOutput; | 1039 | if (amt != self.eh_frame_data.items.len) return error.InputOutput; |
| ... | @@ -1696,7 +1696,7 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void { | ... | @@ -1696,7 +1696,7 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void { |
| 1696 | 1696 | ||
| 1697 | pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { | 1697 | pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { |
| 1698 | // Header | 1698 | // Header |
| 1699 | const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; | 1699 | const size = try macho_file.cast(usize, self.output_ar_state.size); |
| 1700 | const basename = std.fs.path.basename(self.path.sub_path); | 1700 | const basename = std.fs.path.basename(self.path.sub_path); |
| 1701 | try Archive.writeHeader(basename, size, ar_format, writer); | 1701 | try Archive.writeHeader(basename, size, ar_format, writer); |
| 1702 | // Data | 1702 | // Data |
| ... | @@ -1826,7 +1826,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { | ... | @@ -1826,7 +1826,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { |
| 1826 | 1826 | ||
| 1827 | for (headers, 0..) |header, n_sect| { | 1827 | for (headers, 0..) |header, n_sect| { |
| 1828 | if (header.isZerofill()) continue; | 1828 | if (header.isZerofill()) continue; |
| 1829 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 1829 | const size = try macho_file.cast(usize, header.size); |
| 1830 | const data = try gpa.alloc(u8, size); | 1830 | const data = try gpa.alloc(u8, size); |
| 1831 | const amt = try file.preadAll(data, header.offset + self.offset); | 1831 | const amt = try file.preadAll(data, header.offset + self.offset); |
| 1832 | if (amt != data.len) return error.InputOutput; | 1832 | if (amt != data.len) return error.InputOutput; |
| ... | @@ -1837,9 +1837,9 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { | ... | @@ -1837,9 +1837,9 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { |
| 1837 | if (!atom.isAlive()) continue; | 1837 | if (!atom.isAlive()) continue; |
| 1838 | const sect = atom.getInputSection(macho_file); | 1838 | const sect = atom.getInputSection(macho_file); |
| 1839 | if (sect.isZerofill()) continue; | 1839 | if (sect.isZerofill()) continue; |
| 1840 | const value = math.cast(usize, atom.value) orelse return error.Overflow; | 1840 | const value = try macho_file.cast(usize, atom.value); |
| 1841 | const off = math.cast(usize, atom.off) orelse return error.Overflow; | 1841 | const off = try macho_file.cast(usize, atom.off); |
| 1842 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | 1842 | const size = try macho_file.cast(usize, atom.size); |
| 1843 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; | 1843 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 1844 | const data = sections_data[atom.n_sect]; | 1844 | const data = sections_data[atom.n_sect]; |
| 1845 | @memcpy(buffer[value..][0..size], data[off..][0..size]); | 1845 | @memcpy(buffer[value..][0..size], data[off..][0..size]); |
| ... | @@ -1865,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { | ... | @@ -1865,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1865 | 1865 | ||
| 1866 | for (headers, 0..) |header, n_sect| { | 1866 | for (headers, 0..) |header, n_sect| { |
| 1867 | if (header.isZerofill()) continue; | 1867 | if (header.isZerofill()) continue; |
| 1868 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 1868 | const size = try macho_file.cast(usize, header.size); |
| 1869 | const data = try gpa.alloc(u8, size); | 1869 | const data = try gpa.alloc(u8, size); |
| 1870 | const amt = try file.preadAll(data, header.offset + self.offset); | 1870 | const amt = try file.preadAll(data, header.offset + self.offset); |
| 1871 | if (amt != data.len) return error.InputOutput; | 1871 | if (amt != data.len) return error.InputOutput; |
| ... | @@ -1876,9 +1876,9 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { | ... | @@ -1876,9 +1876,9 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1876 | if (!atom.isAlive()) continue; | 1876 | if (!atom.isAlive()) continue; |
| 1877 | const sect = atom.getInputSection(macho_file); | 1877 | const sect = atom.getInputSection(macho_file); |
| 1878 | if (sect.isZerofill()) continue; | 1878 | if (sect.isZerofill()) continue; |
| 1879 | const value = math.cast(usize, atom.value) orelse return error.Overflow; | 1879 | const value = try macho_file.cast(usize, atom.value); |
| 1880 | const off = math.cast(usize, atom.off) orelse return error.Overflow; | 1880 | const off = try macho_file.cast(usize, atom.off); |
| 1881 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | 1881 | const size = try macho_file.cast(usize, atom.size); |
| 1882 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; | 1882 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 1883 | const data = sections_data[atom.n_sect]; | 1883 | const data = sections_data[atom.n_sect]; |
| 1884 | @memcpy(buffer[value..][0..size], data[off..][0..size]); | 1884 | @memcpy(buffer[value..][0..size], data[off..][0..size]); |
| ... | @@ -1909,29 +1909,27 @@ pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void | ... | @@ -1909,29 +1909,27 @@ pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void |
| 1909 | } | 1909 | } |
| 1910 | } | 1910 | } |
| 1911 | 1911 | ||
| 1912 | fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info { | ||
| 1913 | return .{ | ||
| 1914 | .r_address = std.math.cast(i32, offset) orelse return error.Overflow, | ||
| 1915 | .r_symbolnum = 0, | ||
| 1916 | .r_pcrel = 0, | ||
| 1917 | .r_length = 3, | ||
| 1918 | .r_extern = 0, | ||
| 1919 | .r_type = switch (arch) { | ||
| 1920 | .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), | ||
| 1921 | .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), | ||
| 1922 | else => unreachable, | ||
| 1923 | }, | ||
| 1924 | }; | ||
| 1925 | } | ||
| 1926 | |||
| 1912 | pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { | 1927 | pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1913 | const tracy = trace(@src()); | 1928 | const tracy = trace(@src()); |
| 1914 | defer tracy.end(); | 1929 | defer tracy.end(); |
| 1915 | 1930 | ||
| 1916 | const cpu_arch = macho_file.getTarget().cpu.arch; | 1931 | const cpu_arch = macho_file.getTarget().cpu.arch; |
| 1917 | 1932 | ||
| 1918 | const addReloc = struct { | ||
| 1919 | fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info { | ||
| 1920 | return .{ | ||
| 1921 | .r_address = math.cast(i32, offset) orelse return error.Overflow, | ||
| 1922 | .r_symbolnum = 0, | ||
| 1923 | .r_pcrel = 0, | ||
| 1924 | .r_length = 3, | ||
| 1925 | .r_extern = 0, | ||
| 1926 | .r_type = switch (arch) { | ||
| 1927 | .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), | ||
| 1928 | .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), | ||
| 1929 | else => unreachable, | ||
| 1930 | }, | ||
| 1931 | }; | ||
| 1932 | } | ||
| 1933 | }.addReloc; | ||
| 1934 | |||
| 1935 | const nsect = macho_file.unwind_info_sect_index.?; | 1933 | const nsect = macho_file.unwind_info_sect_index.?; |
| 1936 | const buffer = macho_file.sections.items(.out)[nsect].items; | 1934 | const buffer = macho_file.sections.items(.out)[nsect].items; |
| 1937 | const relocs = macho_file.sections.items(.relocs)[nsect].items; | 1935 | const relocs = macho_file.sections.items(.relocs)[nsect].items; |
| ... | @@ -1967,7 +1965,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { | ... | @@ -1967,7 +1965,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1967 | 1965 | ||
| 1968 | // Personality function | 1966 | // Personality function |
| 1969 | if (rec.getPersonality(macho_file)) |sym| { | 1967 | if (rec.getPersonality(macho_file)) |sym| { |
| 1970 | const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow; | 1968 | const r_symbolnum = try macho_file.cast(u24, sym.getOutputSymtabIndex(macho_file).?); |
| 1971 | var reloc = try addReloc(offset + 16, cpu_arch); | 1969 | var reloc = try addReloc(offset + 16, cpu_arch); |
| 1972 | reloc.r_symbolnum = r_symbolnum; | 1970 | reloc.r_symbolnum = r_symbolnum; |
| 1973 | reloc.r_extern = 1; | 1971 | reloc.r_extern = 1; |
src/link/MachO/ZigObject.zig+81-72| ... | @@ -290,12 +290,15 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO | ... | @@ -290,12 +290,15 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO |
| 290 | /// We need this so that we can write to an archive. | 290 | /// We need this so that we can write to an archive. |
| 291 | /// TODO implement writing ZigObject data directly to a buffer instead. | 291 | /// TODO implement writing ZigObject data directly to a buffer instead. |
| 292 | pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void { | 292 | pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void { |
| 293 | const diags = &macho_file.base.comp.link_diags; | ||
| 293 | // Size of the output object file is always the offset + size of the strtab | 294 | // Size of the output object file is always the offset + size of the strtab |
| 294 | const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize; | 295 | const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize; |
| 295 | const gpa = macho_file.base.comp.gpa; | 296 | const gpa = macho_file.base.comp.gpa; |
| 296 | try self.data.resize(gpa, size); | 297 | try self.data.resize(gpa, size); |
| 297 | const amt = try macho_file.base.file.?.preadAll(self.data.items, 0); | 298 | const amt = macho_file.base.file.?.preadAll(self.data.items, 0) catch |err| |
| 298 | if (amt != size) return error.InputOutput; | 299 | return diags.fail("failed to read output file: {s}", .{@errorName(err)}); |
| 300 | if (amt != size) | ||
| 301 | return diags.fail("unexpected EOF reading from output file", .{}); | ||
| 299 | } | 302 | } |
| 300 | 303 | ||
| 301 | pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void { | 304 | pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void { |
| ... | @@ -376,7 +379,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { | ... | @@ -376,7 +379,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { |
| 376 | if (atom.getRelocs(macho_file).len == 0) continue; | 379 | if (atom.getRelocs(macho_file).len == 0) continue; |
| 377 | // TODO: we will resolve and write ZigObject's TLS data twice: | 380 | // TODO: we will resolve and write ZigObject's TLS data twice: |
| 378 | // once here, and once in writeAtoms | 381 | // once here, and once in writeAtoms |
| 379 | const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow; | 382 | const atom_size = try macho_file.cast(usize, atom.size); |
| 380 | const code = try gpa.alloc(u8, atom_size); | 383 | const code = try gpa.alloc(u8, atom_size); |
| 381 | defer gpa.free(code); | 384 | defer gpa.free(code); |
| 382 | self.getAtomData(macho_file, atom.*, code) catch |err| { | 385 | self.getAtomData(macho_file, atom.*, code) catch |err| { |
| ... | @@ -400,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { | ... | @@ -400,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { |
| 400 | has_error = true; | 403 | has_error = true; |
| 401 | continue; | 404 | continue; |
| 402 | }; | 405 | }; |
| 403 | try macho_file.base.file.?.pwriteAll(code, file_offset); | 406 | try macho_file.pwriteAll(code, file_offset); |
| 404 | } | 407 | } |
| 405 | 408 | ||
| 406 | if (has_error) return error.ResolveFailed; | 409 | if (has_error) return error.ResolveFailed; |
| ... | @@ -419,7 +422,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void { | ... | @@ -419,7 +422,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void { |
| 419 | } | 422 | } |
| 420 | } | 423 | } |
| 421 | 424 | ||
| 422 | pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void { | 425 | pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ LinkFailure, OutOfMemory }!void { |
| 423 | const gpa = macho_file.base.comp.gpa; | 426 | const gpa = macho_file.base.comp.gpa; |
| 424 | const diags = &macho_file.base.comp.link_diags; | 427 | const diags = &macho_file.base.comp.link_diags; |
| 425 | 428 | ||
| ... | @@ -432,14 +435,14 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void { | ... | @@ -432,14 +435,14 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void { |
| 432 | if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; | 435 | if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; |
| 433 | if (atom.getRelocs(macho_file).len == 0) continue; | 436 | if (atom.getRelocs(macho_file).len == 0) continue; |
| 434 | const extra = atom.getExtra(macho_file); | 437 | const extra = atom.getExtra(macho_file); |
| 435 | const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow; | 438 | const atom_size = try macho_file.cast(usize, atom.size); |
| 436 | const code = try gpa.alloc(u8, atom_size); | 439 | const code = try gpa.alloc(u8, atom_size); |
| 437 | defer gpa.free(code); | 440 | defer gpa.free(code); |
| 438 | self.getAtomData(macho_file, atom.*, code) catch |err| | 441 | self.getAtomData(macho_file, atom.*, code) catch |err| |
| 439 | return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) }); | 442 | return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) }); |
| 440 | const file_offset = header.offset + atom.value; | 443 | const file_offset = header.offset + atom.value; |
| 441 | try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]); | 444 | try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]); |
| 442 | try macho_file.base.file.?.pwriteAll(code, file_offset); | 445 | try macho_file.pwriteAll(code, file_offset); |
| 443 | } | 446 | } |
| 444 | } | 447 | } |
| 445 | 448 | ||
| ... | @@ -457,8 +460,8 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void { | ... | @@ -457,8 +460,8 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void { |
| 457 | if (sect.isZerofill()) continue; | 460 | if (sect.isZerofill()) continue; |
| 458 | if (macho_file.isZigSection(atom.out_n_sect)) continue; | 461 | if (macho_file.isZigSection(atom.out_n_sect)) continue; |
| 459 | if (atom.getRelocs(macho_file).len == 0) continue; | 462 | if (atom.getRelocs(macho_file).len == 0) continue; |
| 460 | const off = std.math.cast(usize, atom.value) orelse return error.Overflow; | 463 | const off = try macho_file.cast(usize, atom.value); |
| 461 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | 464 | const size = try macho_file.cast(usize, atom.size); |
| 462 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; | 465 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 463 | try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); | 466 | try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); |
| 464 | const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; | 467 | const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; |
| ... | @@ -480,8 +483,8 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void { | ... | @@ -480,8 +483,8 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void { |
| 480 | const sect = atom.getInputSection(macho_file); | 483 | const sect = atom.getInputSection(macho_file); |
| 481 | if (sect.isZerofill()) continue; | 484 | if (sect.isZerofill()) continue; |
| 482 | if (macho_file.isZigSection(atom.out_n_sect)) continue; | 485 | if (macho_file.isZigSection(atom.out_n_sect)) continue; |
| 483 | const off = std.math.cast(usize, atom.value) orelse return error.Overflow; | 486 | const off = try macho_file.cast(usize, atom.value); |
| 484 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | 487 | const size = try macho_file.cast(usize, atom.size); |
| 485 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; | 488 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 486 | try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); | 489 | try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); |
| 487 | try atom.resolveRelocs(macho_file, buffer[off..][0..size]); | 490 | try atom.resolveRelocs(macho_file, buffer[off..][0..size]); |
| ... | @@ -546,7 +549,9 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se | ... | @@ -546,7 +549,9 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se |
| 546 | return sect; | 549 | return sect; |
| 547 | } | 550 | } |
| 548 | 551 | ||
| 549 | pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) !void { | 552 | pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void { |
| 553 | const diags = &macho_file.base.comp.link_diags; | ||
| 554 | |||
| 550 | // Handle any lazy symbols that were emitted by incremental compilation. | 555 | // Handle any lazy symbols that were emitted by incremental compilation. |
| 551 | if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| { | 556 | if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| { |
| 552 | const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); | 557 | const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); |
| ... | @@ -559,18 +564,20 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) | ... | @@ -559,18 +564,20 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) |
| 559 | pt, | 564 | pt, |
| 560 | .{ .kind = .code, .ty = .anyerror_type }, | 565 | .{ .kind = .code, .ty = .anyerror_type }, |
| 561 | metadata.text_symbol_index, | 566 | metadata.text_symbol_index, |
| 562 | ) catch |err| return switch (err) { | 567 | ) catch |err| switch (err) { |
| 563 | error.CodegenFail => error.FlushFailure, | 568 | error.OutOfMemory => return error.OutOfMemory, |
| 564 | else => |e| e, | 569 | error.LinkFailure => return error.LinkFailure, |
| 570 | else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}), | ||
| 565 | }; | 571 | }; |
| 566 | if (metadata.const_state != .unused) self.updateLazySymbol( | 572 | if (metadata.const_state != .unused) self.updateLazySymbol( |
| 567 | macho_file, | 573 | macho_file, |
| 568 | pt, | 574 | pt, |
| 569 | .{ .kind = .const_data, .ty = .anyerror_type }, | 575 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 570 | metadata.const_symbol_index, | 576 | metadata.const_symbol_index, |
| 571 | ) catch |err| return switch (err) { | 577 | ) catch |err| switch (err) { |
| 572 | error.CodegenFail => error.FlushFailure, | 578 | error.OutOfMemory => return error.OutOfMemory, |
| 573 | else => |e| e, | 579 | error.LinkFailure => return error.LinkFailure, |
| 580 | else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}), | ||
| 574 | }; | 581 | }; |
| 575 | } | 582 | } |
| 576 | for (self.lazy_syms.values()) |*metadata| { | 583 | for (self.lazy_syms.values()) |*metadata| { |
| ... | @@ -581,7 +588,10 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) | ... | @@ -581,7 +588,10 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) |
| 581 | if (self.dwarf) |*dwarf| { | 588 | if (self.dwarf) |*dwarf| { |
| 582 | const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); | 589 | const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); |
| 583 | defer pt.deactivate(); | 590 | defer pt.deactivate(); |
| 584 | try dwarf.flushModule(pt); | 591 | dwarf.flushModule(pt) catch |err| switch (err) { |
| 592 | error.OutOfMemory => return error.OutOfMemory, | ||
| 593 | else => |e| return diags.fail("failed to flush dwarf module: {s}", .{@errorName(e)}), | ||
| 594 | }; | ||
| 585 | 595 | ||
| 586 | self.debug_abbrev_dirty = false; | 596 | self.debug_abbrev_dirty = false; |
| 587 | self.debug_aranges_dirty = false; | 597 | self.debug_aranges_dirty = false; |
| ... | @@ -616,6 +626,7 @@ pub fn getNavVAddr( | ... | @@ -616,6 +626,7 @@ pub fn getNavVAddr( |
| 616 | const sym = self.symbols.items[sym_index]; | 626 | const sym = self.symbols.items[sym_index]; |
| 617 | const vaddr = sym.getAddress(.{}, macho_file); | 627 | const vaddr = sym.getAddress(.{}, macho_file); |
| 618 | switch (reloc_info.parent) { | 628 | switch (reloc_info.parent) { |
| 629 | .none => unreachable, | ||
| 619 | .atom_index => |atom_index| { | 630 | .atom_index => |atom_index| { |
| 620 | const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?; | 631 | const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?; |
| 621 | try parent_atom.addReloc(macho_file, .{ | 632 | try parent_atom.addReloc(macho_file, .{ |
| ... | @@ -655,6 +666,7 @@ pub fn getUavVAddr( | ... | @@ -655,6 +666,7 @@ pub fn getUavVAddr( |
| 655 | const sym = self.symbols.items[sym_index]; | 666 | const sym = self.symbols.items[sym_index]; |
| 656 | const vaddr = sym.getAddress(.{}, macho_file); | 667 | const vaddr = sym.getAddress(.{}, macho_file); |
| 657 | switch (reloc_info.parent) { | 668 | switch (reloc_info.parent) { |
| 669 | .none => unreachable, | ||
| 658 | .atom_index => |atom_index| { | 670 | .atom_index => |atom_index| { |
| 659 | const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?; | 671 | const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?; |
| 660 | try parent_atom.addReloc(macho_file, .{ | 672 | try parent_atom.addReloc(macho_file, .{ |
| ... | @@ -766,7 +778,7 @@ pub fn updateFunc( | ... | @@ -766,7 +778,7 @@ pub fn updateFunc( |
| 766 | func_index: InternPool.Index, | 778 | func_index: InternPool.Index, |
| 767 | air: Air, | 779 | air: Air, |
| 768 | liveness: Liveness, | 780 | liveness: Liveness, |
| 769 | ) !void { | 781 | ) link.File.UpdateNavError!void { |
| 770 | const tracy = trace(@src()); | 782 | const tracy = trace(@src()); |
| 771 | defer tracy.end(); | 783 | defer tracy.end(); |
| 772 | 784 | ||
| ... | @@ -777,13 +789,13 @@ pub fn updateFunc( | ... | @@ -777,13 +789,13 @@ pub fn updateFunc( |
| 777 | const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav); | 789 | const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav); |
| 778 | self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); | 790 | self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); |
| 779 | 791 | ||
| 780 | var code_buffer = std.ArrayList(u8).init(gpa); | 792 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 781 | defer code_buffer.deinit(); | 793 | defer code_buffer.deinit(gpa); |
| 782 | 794 | ||
| 783 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; | 795 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; |
| 784 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); | 796 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 785 | 797 | ||
| 786 | const res = try codegen.generateFunction( | 798 | try codegen.generateFunction( |
| 787 | &macho_file.base, | 799 | &macho_file.base, |
| 788 | pt, | 800 | pt, |
| 789 | zcu.navSrcLoc(func.owner_nav), | 801 | zcu.navSrcLoc(func.owner_nav), |
| ... | @@ -793,14 +805,7 @@ pub fn updateFunc( | ... | @@ -793,14 +805,7 @@ pub fn updateFunc( |
| 793 | &code_buffer, | 805 | &code_buffer, |
| 794 | if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, | 806 | if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, |
| 795 | ); | 807 | ); |
| 796 | 808 | const code = code_buffer.items; | |
| 797 | const code = switch (res) { | ||
| 798 | .ok => code_buffer.items, | ||
| 799 | .fail => |em| { | ||
| 800 | try zcu.failed_codegen.put(gpa, func.owner_nav, em); | ||
| 801 | return; | ||
| 802 | }, | ||
| 803 | }; | ||
| 804 | 809 | ||
| 805 | const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code); | 810 | const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code); |
| 806 | const old_rva, const old_alignment = blk: { | 811 | const old_rva, const old_alignment = blk: { |
| ... | @@ -813,7 +818,8 @@ pub fn updateFunc( | ... | @@ -813,7 +818,8 @@ pub fn updateFunc( |
| 813 | break :blk .{ atom.value, atom.alignment }; | 818 | break :blk .{ atom.value, atom.alignment }; |
| 814 | }; | 819 | }; |
| 815 | 820 | ||
| 816 | if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav); | 821 | if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err| |
| 822 | return macho_file.base.cgFail(func.owner_nav, "falied to finish dwarf function: {s}", .{@errorName(err)}); | ||
| 817 | 823 | ||
| 818 | // Exports will be updated by `Zcu.processExports` after the update. | 824 | // Exports will be updated by `Zcu.processExports` after the update. |
| 819 | if (old_rva != new_rva and old_rva > 0) { | 825 | if (old_rva != new_rva and old_rva > 0) { |
| ... | @@ -850,7 +856,8 @@ pub fn updateFunc( | ... | @@ -850,7 +856,8 @@ pub fn updateFunc( |
| 850 | } | 856 | } |
| 851 | const target_sym = self.symbols.items[sym_index]; | 857 | const target_sym = self.symbols.items[sym_index]; |
| 852 | const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline]; | 858 | const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline]; |
| 853 | try writeTrampoline(source_sym, target_sym, macho_file); | 859 | writeTrampoline(source_sym, target_sym, macho_file) catch |err| |
| 860 | return macho_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)}); | ||
| 854 | } | 861 | } |
| 855 | } | 862 | } |
| 856 | 863 | ||
| ... | @@ -883,7 +890,11 @@ pub fn updateNav( | ... | @@ -883,7 +890,11 @@ pub fn updateNav( |
| 883 | if (self.dwarf) |*dwarf| dwarf: { | 890 | if (self.dwarf) |*dwarf| dwarf: { |
| 884 | var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; | 891 | var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; |
| 885 | defer debug_wip_nav.deinit(); | 892 | defer debug_wip_nav.deinit(); |
| 886 | try dwarf.finishWipNav(pt, nav_index, &debug_wip_nav); | 893 | dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) { |
| 894 | error.OutOfMemory => return error.OutOfMemory, | ||
| 895 | error.Overflow => return error.Overflow, | ||
| 896 | else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), | ||
| 897 | }; | ||
| 887 | } | 898 | } |
| 888 | return; | 899 | return; |
| 889 | }, | 900 | }, |
| ... | @@ -894,13 +905,13 @@ pub fn updateNav( | ... | @@ -894,13 +905,13 @@ pub fn updateNav( |
| 894 | const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index); | 905 | const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index); |
| 895 | self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); | 906 | self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); |
| 896 | 907 | ||
| 897 | var code_buffer = std.ArrayList(u8).init(zcu.gpa); | 908 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 898 | defer code_buffer.deinit(); | 909 | defer code_buffer.deinit(zcu.gpa); |
| 899 | 910 | ||
| 900 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null; | 911 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null; |
| 901 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); | 912 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 902 | 913 | ||
| 903 | const res = try codegen.generateSymbol( | 914 | try codegen.generateSymbol( |
| 904 | &macho_file.base, | 915 | &macho_file.base, |
| 905 | pt, | 916 | pt, |
| 906 | zcu.navSrcLoc(nav_index), | 917 | zcu.navSrcLoc(nav_index), |
| ... | @@ -908,21 +919,19 @@ pub fn updateNav( | ... | @@ -908,21 +919,19 @@ pub fn updateNav( |
| 908 | &code_buffer, | 919 | &code_buffer, |
| 909 | .{ .atom_index = sym_index }, | 920 | .{ .atom_index = sym_index }, |
| 910 | ); | 921 | ); |
| 922 | const code = code_buffer.items; | ||
| 911 | 923 | ||
| 912 | const code = switch (res) { | ||
| 913 | .ok => code_buffer.items, | ||
| 914 | .fail => |em| { | ||
| 915 | try zcu.failed_codegen.put(zcu.gpa, nav_index, em); | ||
| 916 | return; | ||
| 917 | }, | ||
| 918 | }; | ||
| 919 | const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code); | 924 | const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code); |
| 920 | if (isThreadlocal(macho_file, nav_index)) | 925 | if (isThreadlocal(macho_file, nav_index)) |
| 921 | try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code) | 926 | try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code) |
| 922 | else | 927 | else |
| 923 | try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code); | 928 | try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code); |
| 924 | 929 | ||
| 925 | if (debug_wip_nav) |*wip_nav| try self.dwarf.?.finishWipNav(pt, nav_index, wip_nav); | 930 | if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) { |
| 931 | error.OutOfMemory => return error.OutOfMemory, | ||
| 932 | error.Overflow => return error.Overflow, | ||
| 933 | else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), | ||
| 934 | }; | ||
| 926 | } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); | 935 | } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); |
| 927 | 936 | ||
| 928 | // Exports will be updated by `Zcu.processExports` after the update. | 937 | // Exports will be updated by `Zcu.processExports` after the update. |
| ... | @@ -936,7 +945,7 @@ fn updateNavCode( | ... | @@ -936,7 +945,7 @@ fn updateNavCode( |
| 936 | sym_index: Symbol.Index, | 945 | sym_index: Symbol.Index, |
| 937 | sect_index: u8, | 946 | sect_index: u8, |
| 938 | code: []const u8, | 947 | code: []const u8, |
| 939 | ) !void { | 948 | ) link.File.UpdateNavError!void { |
| 940 | const zcu = pt.zcu; | 949 | const zcu = pt.zcu; |
| 941 | const gpa = zcu.gpa; | 950 | const gpa = zcu.gpa; |
| 942 | const ip = &zcu.intern_pool; | 951 | const ip = &zcu.intern_pool; |
| ... | @@ -978,7 +987,8 @@ fn updateNavCode( | ... | @@ -978,7 +987,8 @@ fn updateNavCode( |
| 978 | const need_realloc = code.len > capacity or !required_alignment.check(atom.value); | 987 | const need_realloc = code.len > capacity or !required_alignment.check(atom.value); |
| 979 | 988 | ||
| 980 | if (need_realloc) { | 989 | if (need_realloc) { |
| 981 | try atom.grow(macho_file); | 990 | atom.grow(macho_file) catch |err| |
| 991 | return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)}); | ||
| 982 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value }); | 992 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value }); |
| 983 | if (old_vaddr != atom.value) { | 993 | if (old_vaddr != atom.value) { |
| 984 | sym.value = 0; | 994 | sym.value = 0; |
| ... | @@ -991,7 +1001,8 @@ fn updateNavCode( | ... | @@ -991,7 +1001,8 @@ fn updateNavCode( |
| 991 | sect.size = needed_size; | 1001 | sect.size = needed_size; |
| 992 | } | 1002 | } |
| 993 | } else { | 1003 | } else { |
| 994 | try atom.allocate(macho_file); | 1004 | atom.allocate(macho_file) catch |err| |
| 1005 | return macho_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)}); | ||
| 995 | errdefer self.freeNavMetadata(macho_file, sym_index); | 1006 | errdefer self.freeNavMetadata(macho_file, sym_index); |
| 996 | 1007 | ||
| 997 | sym.value = 0; | 1008 | sym.value = 0; |
| ... | @@ -1000,7 +1011,8 @@ fn updateNavCode( | ... | @@ -1000,7 +1011,8 @@ fn updateNavCode( |
| 1000 | 1011 | ||
| 1001 | if (!sect.isZerofill()) { | 1012 | if (!sect.isZerofill()) { |
| 1002 | const file_offset = sect.offset + atom.value; | 1013 | const file_offset = sect.offset + atom.value; |
| 1003 | try macho_file.base.file.?.pwriteAll(code, file_offset); | 1014 | macho_file.base.file.?.pwriteAll(code, file_offset) catch |err| |
| 1015 | return macho_file.base.cgFail(nav_index, "failed to write output file: {s}", .{@errorName(err)}); | ||
| 1004 | } | 1016 | } |
| 1005 | } | 1017 | } |
| 1006 | 1018 | ||
| ... | @@ -1198,13 +1210,13 @@ fn lowerConst( | ... | @@ -1198,13 +1210,13 @@ fn lowerConst( |
| 1198 | ) !LowerConstResult { | 1210 | ) !LowerConstResult { |
| 1199 | const gpa = macho_file.base.comp.gpa; | 1211 | const gpa = macho_file.base.comp.gpa; |
| 1200 | 1212 | ||
| 1201 | var code_buffer = std.ArrayList(u8).init(gpa); | 1213 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1202 | defer code_buffer.deinit(); | 1214 | defer code_buffer.deinit(gpa); |
| 1203 | 1215 | ||
| 1204 | const name_str = try self.addString(gpa, name); | 1216 | const name_str = try self.addString(gpa, name); |
| 1205 | const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file); | 1217 | const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file); |
| 1206 | 1218 | ||
| 1207 | const res = try codegen.generateSymbol( | 1219 | try codegen.generateSymbol( |
| 1208 | &macho_file.base, | 1220 | &macho_file.base, |
| 1209 | pt, | 1221 | pt, |
| 1210 | src_loc, | 1222 | src_loc, |
| ... | @@ -1212,10 +1224,7 @@ fn lowerConst( | ... | @@ -1212,10 +1224,7 @@ fn lowerConst( |
| 1212 | &code_buffer, | 1224 | &code_buffer, |
| 1213 | .{ .atom_index = sym_index }, | 1225 | .{ .atom_index = sym_index }, |
| 1214 | ); | 1226 | ); |
| 1215 | const code = switch (res) { | 1227 | const code = code_buffer.items; |
| 1216 | .ok => code_buffer.items, | ||
| 1217 | .fail => |em| return .{ .fail = em }, | ||
| 1218 | }; | ||
| 1219 | 1228 | ||
| 1220 | const sym = &self.symbols.items[sym_index]; | 1229 | const sym = &self.symbols.items[sym_index]; |
| 1221 | sym.out_n_sect = output_section_index; | 1230 | sym.out_n_sect = output_section_index; |
| ... | @@ -1236,7 +1245,7 @@ fn lowerConst( | ... | @@ -1236,7 +1245,7 @@ fn lowerConst( |
| 1236 | 1245 | ||
| 1237 | const sect = macho_file.sections.items(.header)[output_section_index]; | 1246 | const sect = macho_file.sections.items(.header)[output_section_index]; |
| 1238 | const file_offset = sect.offset + atom.value; | 1247 | const file_offset = sect.offset + atom.value; |
| 1239 | try macho_file.base.file.?.pwriteAll(code, file_offset); | 1248 | try macho_file.pwriteAll(code, file_offset); |
| 1240 | 1249 | ||
| 1241 | return .{ .ok = sym_index }; | 1250 | return .{ .ok = sym_index }; |
| 1242 | } | 1251 | } |
| ... | @@ -1246,7 +1255,7 @@ pub fn updateExports( | ... | @@ -1246,7 +1255,7 @@ pub fn updateExports( |
| 1246 | macho_file: *MachO, | 1255 | macho_file: *MachO, |
| 1247 | pt: Zcu.PerThread, | 1256 | pt: Zcu.PerThread, |
| 1248 | exported: Zcu.Exported, | 1257 | exported: Zcu.Exported, |
| 1249 | export_indices: []const u32, | 1258 | export_indices: []const Zcu.Export.Index, |
| 1250 | ) link.File.UpdateExportsError!void { | 1259 | ) link.File.UpdateExportsError!void { |
| 1251 | const tracy = trace(@src()); | 1260 | const tracy = trace(@src()); |
| 1252 | defer tracy.end(); | 1261 | defer tracy.end(); |
| ... | @@ -1259,7 +1268,7 @@ pub fn updateExports( | ... | @@ -1259,7 +1268,7 @@ pub fn updateExports( |
| 1259 | break :blk self.navs.getPtr(nav).?; | 1268 | break :blk self.navs.getPtr(nav).?; |
| 1260 | }, | 1269 | }, |
| 1261 | .uav => |uav| self.uavs.getPtr(uav) orelse blk: { | 1270 | .uav => |uav| self.uavs.getPtr(uav) orelse blk: { |
| 1262 | const first_exp = zcu.all_exports.items[export_indices[0]]; | 1271 | const first_exp = export_indices[0].ptr(zcu); |
| 1263 | const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src); | 1272 | const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src); |
| 1264 | switch (res) { | 1273 | switch (res) { |
| 1265 | .mcv => {}, | 1274 | .mcv => {}, |
| ... | @@ -1279,7 +1288,7 @@ pub fn updateExports( | ... | @@ -1279,7 +1288,7 @@ pub fn updateExports( |
| 1279 | const nlist = self.symtab.items(.nlist)[nlist_idx]; | 1288 | const nlist = self.symtab.items(.nlist)[nlist_idx]; |
| 1280 | 1289 | ||
| 1281 | for (export_indices) |export_idx| { | 1290 | for (export_indices) |export_idx| { |
| 1282 | const exp = zcu.all_exports.items[export_idx]; | 1291 | const exp = export_idx.ptr(zcu); |
| 1283 | if (exp.opts.section.unwrap()) |section_name| { | 1292 | if (exp.opts.section.unwrap()) |section_name| { |
| 1284 | if (!section_name.eqlSlice("__text", &zcu.intern_pool)) { | 1293 | if (!section_name.eqlSlice("__text", &zcu.intern_pool)) { |
| 1285 | try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); | 1294 | try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); |
| ... | @@ -1352,8 +1361,8 @@ fn updateLazySymbol( | ... | @@ -1352,8 +1361,8 @@ fn updateLazySymbol( |
| 1352 | const gpa = zcu.gpa; | 1361 | const gpa = zcu.gpa; |
| 1353 | 1362 | ||
| 1354 | var required_alignment: Atom.Alignment = .none; | 1363 | var required_alignment: Atom.Alignment = .none; |
| 1355 | var code_buffer = std.ArrayList(u8).init(gpa); | 1364 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1356 | defer code_buffer.deinit(); | 1365 | defer code_buffer.deinit(gpa); |
| 1357 | 1366 | ||
| 1358 | const name_str = blk: { | 1367 | const name_str = blk: { |
| 1359 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ | 1368 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ |
| ... | @@ -1365,7 +1374,7 @@ fn updateLazySymbol( | ... | @@ -1365,7 +1374,7 @@ fn updateLazySymbol( |
| 1365 | }; | 1374 | }; |
| 1366 | 1375 | ||
| 1367 | const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; | 1376 | const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1368 | const res = try codegen.generateLazySymbol( | 1377 | try codegen.generateLazySymbol( |
| 1369 | &macho_file.base, | 1378 | &macho_file.base, |
| 1370 | pt, | 1379 | pt, |
| 1371 | src, | 1380 | src, |
| ... | @@ -1375,13 +1384,7 @@ fn updateLazySymbol( | ... | @@ -1375,13 +1384,7 @@ fn updateLazySymbol( |
| 1375 | .none, | 1384 | .none, |
| 1376 | .{ .atom_index = symbol_index }, | 1385 | .{ .atom_index = symbol_index }, |
| 1377 | ); | 1386 | ); |
| 1378 | const code = switch (res) { | 1387 | const code = code_buffer.items; |
| 1379 | .ok => code_buffer.items, | ||
| 1380 | .fail => |em| { | ||
| 1381 | log.err("{s}", .{em.msg}); | ||
| 1382 | return error.CodegenFail; | ||
| 1383 | }, | ||
| 1384 | }; | ||
| 1385 | 1388 | ||
| 1386 | const output_section_index = switch (lazy_sym.kind) { | 1389 | const output_section_index = switch (lazy_sym.kind) { |
| 1387 | .code => macho_file.zig_text_sect_index.?, | 1390 | .code => macho_file.zig_text_sect_index.?, |
| ... | @@ -1412,12 +1415,18 @@ fn updateLazySymbol( | ... | @@ -1412,12 +1415,18 @@ fn updateLazySymbol( |
| 1412 | 1415 | ||
| 1413 | const sect = macho_file.sections.items(.header)[output_section_index]; | 1416 | const sect = macho_file.sections.items(.header)[output_section_index]; |
| 1414 | const file_offset = sect.offset + atom.value; | 1417 | const file_offset = sect.offset + atom.value; |
| 1415 | try macho_file.base.file.?.pwriteAll(code, file_offset); | 1418 | try macho_file.pwriteAll(code, file_offset); |
| 1416 | } | 1419 | } |
| 1417 | 1420 | ||
| 1418 | pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { | 1421 | pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { |
| 1419 | if (self.dwarf) |*dwarf| { | 1422 | if (self.dwarf) |*dwarf| { |
| 1420 | try dwarf.updateLineNumber(pt.zcu, ti_id); | 1423 | const comp = dwarf.bin_file.comp; |
| 1424 | const diags = &comp.link_diags; | ||
| 1425 | dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { | ||
| 1426 | error.Overflow => return error.Overflow, | ||
| 1427 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1428 | else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), | ||
| 1429 | }; | ||
| 1421 | } | 1430 | } |
| 1422 | } | 1431 | } |
| 1423 | 1432 |
src/link/MachO/relocatable.zig+68-42| ... | @@ -18,13 +18,15 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat | ... | @@ -18,13 +18,15 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat |
| 18 | // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all | 18 | // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all |
| 19 | // debug info segments/sections (this is apparently by design by Apple), we copy | 19 | // debug info segments/sections (this is apparently by design by Apple), we copy |
| 20 | // the *only* input file over. | 20 | // the *only* input file over. |
| 21 | // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig | ||
| 22 | // compiler, investigate if we can get rid of this `if` prong here. | ||
| 23 | const path = positionals.items[0].path().?; | 21 | const path = positionals.items[0].path().?; |
| 24 | const in_file = try path.root_dir.handle.openFile(path.sub_path, .{}); | 22 | const in_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| |
| 25 | const stat = try in_file.stat(); | 23 | return diags.fail("failed to open {}: {s}", .{ path, @errorName(err) }); |
| 26 | const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size); | 24 | const stat = in_file.stat() catch |err| |
| 27 | if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error | 25 | return diags.fail("failed to stat {}: {s}", .{ path, @errorName(err) }); |
| 26 | const amt = in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size) catch |err| | ||
| 27 | return diags.fail("failed to copy range of file {}: {s}", .{ path, @errorName(err) }); | ||
| 28 | if (amt != stat.size) | ||
| 29 | return diags.fail("unexpected short write in copy range of file {}", .{path}); | ||
| 28 | return; | 30 | return; |
| 29 | } | 31 | } |
| 30 | 32 | ||
| ... | @@ -33,14 +35,18 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat | ... | @@ -33,14 +35,18 @@ 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)}); | 35 | diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | if (diags.hasErrors()) return error.FlushFailure; | 38 | if (diags.hasErrors()) return error.LinkFailure; |
| 37 | 39 | ||
| 38 | try macho_file.parseInputFiles(); | 40 | try macho_file.parseInputFiles(); |
| 39 | 41 | ||
| 40 | if (diags.hasErrors()) return error.FlushFailure; | 42 | if (diags.hasErrors()) return error.LinkFailure; |
| 41 | 43 | ||
| 42 | try macho_file.resolveSymbols(); | 44 | try macho_file.resolveSymbols(); |
| 43 | try macho_file.dedupLiterals(); | 45 | macho_file.dedupLiterals() catch |err| switch (err) { |
| 46 | error.OutOfMemory => return error.OutOfMemory, | ||
| 47 | error.LinkFailure => return error.LinkFailure, | ||
| 48 | else => |e| return diags.fail("failed to update ar size: {s}", .{@errorName(e)}), | ||
| 49 | }; | ||
| 44 | markExports(macho_file); | 50 | markExports(macho_file); |
| 45 | claimUnresolved(macho_file); | 51 | claimUnresolved(macho_file); |
| 46 | try initOutputSections(macho_file); | 52 | try initOutputSections(macho_file); |
| ... | @@ -49,7 +55,10 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat | ... | @@ -49,7 +55,10 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat |
| 49 | try calcSectionSizes(macho_file); | 55 | try calcSectionSizes(macho_file); |
| 50 | 56 | ||
| 51 | try createSegment(macho_file); | 57 | try createSegment(macho_file); |
| 52 | try allocateSections(macho_file); | 58 | allocateSections(macho_file) catch |err| switch (err) { |
| 59 | error.LinkFailure => return error.LinkFailure, | ||
| 60 | else => |e| return diags.fail("failed to allocate sections: {s}", .{@errorName(e)}), | ||
| 61 | }; | ||
| 53 | allocateSegment(macho_file); | 62 | allocateSegment(macho_file); |
| 54 | 63 | ||
| 55 | if (build_options.enable_logging) { | 64 | if (build_options.enable_logging) { |
| ... | @@ -93,11 +102,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -93,11 +102,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)}); | 102 | diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); |
| 94 | } | 103 | } |
| 95 | 104 | ||
| 96 | if (diags.hasErrors()) return error.FlushFailure; | 105 | if (diags.hasErrors()) return error.LinkFailure; |
| 97 | 106 | ||
| 98 | try parseInputFilesAr(macho_file); | 107 | try parseInputFilesAr(macho_file); |
| 99 | 108 | ||
| 100 | if (diags.hasErrors()) return error.FlushFailure; | 109 | if (diags.hasErrors()) return error.LinkFailure; |
| 101 | 110 | ||
| 102 | // First, we flush relocatable object file generated with our backends. | 111 | // First, we flush relocatable object file generated with our backends. |
| 103 | if (macho_file.getZigObject()) |zo| { | 112 | if (macho_file.getZigObject()) |zo| { |
| ... | @@ -108,7 +117,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -108,7 +117,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 108 | try macho_file.addAtomsToSections(); | 117 | try macho_file.addAtomsToSections(); |
| 109 | try calcSectionSizes(macho_file); | 118 | try calcSectionSizes(macho_file); |
| 110 | try createSegment(macho_file); | 119 | try createSegment(macho_file); |
| 111 | try allocateSections(macho_file); | 120 | allocateSections(macho_file) catch |err| |
| 121 | return diags.fail("failed to allocate sections: {s}", .{@errorName(err)}); | ||
| 112 | allocateSegment(macho_file); | 122 | allocateSegment(macho_file); |
| 113 | 123 | ||
| 114 | if (build_options.enable_logging) { | 124 | if (build_options.enable_logging) { |
| ... | @@ -126,8 +136,6 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -126,8 +136,6 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 126 | const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); | 136 | const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); |
| 127 | try writeHeader(macho_file, ncmds, sizeofcmds); | 137 | try writeHeader(macho_file, ncmds, sizeofcmds); |
| 128 | 138 | ||
| 129 | // TODO we can avoid reading in the file contents we just wrote if we give the linker | ||
| 130 | // ability to write directly to a buffer. | ||
| 131 | try zo.readFileContents(macho_file); | 139 | try zo.readFileContents(macho_file); |
| 132 | } | 140 | } |
| 133 | 141 | ||
| ... | @@ -152,7 +160,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -152,7 +160,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 152 | 160 | ||
| 153 | // Update sizes of contributing objects | 161 | // Update sizes of contributing objects |
| 154 | for (files.items) |index| { | 162 | for (files.items) |index| { |
| 155 | try macho_file.getFile(index).?.updateArSize(macho_file); | 163 | macho_file.getFile(index).?.updateArSize(macho_file) catch |err| |
| 164 | return diags.fail("failed to update ar size: {s}", .{@errorName(err)}); | ||
| 156 | } | 165 | } |
| 157 | 166 | ||
| 158 | // Update file offsets of contributing objects | 167 | // Update file offsets of contributing objects |
| ... | @@ -171,7 +180,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -171,7 +180,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 171 | state.file_off = pos; | 180 | state.file_off = pos; |
| 172 | pos += @sizeOf(Archive.ar_hdr); | 181 | pos += @sizeOf(Archive.ar_hdr); |
| 173 | pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width); | 182 | pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width); |
| 174 | pos += math.cast(usize, state.size) orelse return error.Overflow; | 183 | pos += try macho_file.cast(usize, state.size); |
| 175 | }, | 184 | }, |
| 176 | .object => |o| { | 185 | .object => |o| { |
| 177 | const state = &o.output_ar_state; | 186 | const state = &o.output_ar_state; |
| ... | @@ -179,7 +188,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -179,7 +188,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 179 | state.file_off = pos; | 188 | state.file_off = pos; |
| 180 | pos += @sizeOf(Archive.ar_hdr); | 189 | pos += @sizeOf(Archive.ar_hdr); |
| 181 | pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width); | 190 | pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width); |
| 182 | pos += math.cast(usize, state.size) orelse return error.Overflow; | 191 | pos += try macho_file.cast(usize, state.size); |
| 183 | }, | 192 | }, |
| 184 | else => unreachable, | 193 | else => unreachable, |
| 185 | } | 194 | } |
| ... | @@ -201,7 +210,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -201,7 +210,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 201 | try writer.writeAll(Archive.ARMAG); | 210 | try writer.writeAll(Archive.ARMAG); |
| 202 | 211 | ||
| 203 | // Write symtab | 212 | // Write symtab |
| 204 | try ar_symtab.write(format, macho_file, writer); | 213 | ar_symtab.write(format, macho_file, writer) catch |err| switch (err) { |
| 214 | error.OutOfMemory => return error.OutOfMemory, | ||
| 215 | else => |e| return diags.fail("failed to write archive symbol table: {s}", .{@errorName(e)}), | ||
| 216 | }; | ||
| 205 | 217 | ||
| 206 | // Write object files | 218 | // Write object files |
| 207 | for (files.items) |index| { | 219 | for (files.items) |index| { |
| ... | @@ -210,15 +222,16 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? | ... | @@ -210,15 +222,16 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 210 | if (padding > 0) { | 222 | if (padding > 0) { |
| 211 | try writer.writeByteNTimes(0, padding); | 223 | try writer.writeByteNTimes(0, padding); |
| 212 | } | 224 | } |
| 213 | try macho_file.getFile(index).?.writeAr(format, macho_file, writer); | 225 | macho_file.getFile(index).?.writeAr(format, macho_file, writer) catch |err| |
| 226 | return diags.fail("failed to write archive: {s}", .{@errorName(err)}); | ||
| 214 | } | 227 | } |
| 215 | 228 | ||
| 216 | assert(buffer.items.len == total_size); | 229 | assert(buffer.items.len == total_size); |
| 217 | 230 | ||
| 218 | try macho_file.base.file.?.setEndPos(total_size); | 231 | try macho_file.setEndPos(total_size); |
| 219 | try macho_file.base.file.?.pwriteAll(buffer.items, 0); | 232 | try macho_file.pwriteAll(buffer.items, 0); |
| 220 | 233 | ||
| 221 | if (diags.hasErrors()) return error.FlushFailure; | 234 | if (diags.hasErrors()) return error.LinkFailure; |
| 222 | } | 235 | } |
| 223 | 236 | ||
| 224 | fn parseInputFilesAr(macho_file: *MachO) !void { | 237 | fn parseInputFilesAr(macho_file: *MachO) !void { |
| ... | @@ -452,11 +465,10 @@ fn allocateSections(macho_file: *MachO) !void { | ... | @@ -452,11 +465,10 @@ fn allocateSections(macho_file: *MachO) !void { |
| 452 | for (slice.items(.header)) |*header| { | 465 | for (slice.items(.header)) |*header| { |
| 453 | const needed_size = header.size; | 466 | const needed_size = header.size; |
| 454 | header.size = 0; | 467 | header.size = 0; |
| 455 | const alignment = try math.powi(u32, 2, header.@"align"); | 468 | const alignment = try macho_file.alignPow(header.@"align"); |
| 456 | if (!header.isZerofill()) { | 469 | if (!header.isZerofill()) { |
| 457 | if (needed_size > macho_file.allocatedSize(header.offset)) { | 470 | if (needed_size > macho_file.allocatedSize(header.offset)) { |
| 458 | header.offset = math.cast(u32, try macho_file.findFreeSpace(needed_size, alignment)) orelse | 471 | header.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(needed_size, alignment)); |
| 459 | return error.Overflow; | ||
| 460 | } | 472 | } |
| 461 | } | 473 | } |
| 462 | if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) { | 474 | if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) { |
| ... | @@ -572,7 +584,7 @@ fn sortRelocs(macho_file: *MachO) void { | ... | @@ -572,7 +584,7 @@ fn sortRelocs(macho_file: *MachO) void { |
| 572 | } | 584 | } |
| 573 | } | 585 | } |
| 574 | 586 | ||
| 575 | fn writeSections(macho_file: *MachO) !void { | 587 | fn writeSections(macho_file: *MachO) link.File.FlushError!void { |
| 576 | const tracy = trace(@src()); | 588 | const tracy = trace(@src()); |
| 577 | defer tracy.end(); | 589 | defer tracy.end(); |
| 578 | 590 | ||
| ... | @@ -583,7 +595,7 @@ fn writeSections(macho_file: *MachO) !void { | ... | @@ -583,7 +595,7 @@ fn writeSections(macho_file: *MachO) !void { |
| 583 | for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| { | 595 | for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| { |
| 584 | if (header.isZerofill()) continue; | 596 | if (header.isZerofill()) continue; |
| 585 | if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections? | 597 | if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections? |
| 586 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 598 | const size = try macho_file.cast(usize, header.size); |
| 587 | try out.resize(gpa, size); | 599 | try out.resize(gpa, size); |
| 588 | const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; | 600 | const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; |
| 589 | @memset(out.items, padding_byte); | 601 | @memset(out.items, padding_byte); |
| ... | @@ -662,16 +674,16 @@ fn writeSectionsToFile(macho_file: *MachO) !void { | ... | @@ -662,16 +674,16 @@ fn writeSectionsToFile(macho_file: *MachO) !void { |
| 662 | 674 | ||
| 663 | const slice = macho_file.sections.slice(); | 675 | const slice = macho_file.sections.slice(); |
| 664 | for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| { | 676 | for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| { |
| 665 | try macho_file.base.file.?.pwriteAll(out.items, header.offset); | 677 | try macho_file.pwriteAll(out.items, header.offset); |
| 666 | try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); | 678 | try macho_file.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); |
| 667 | } | 679 | } |
| 668 | 680 | ||
| 669 | try macho_file.writeDataInCode(); | 681 | try macho_file.writeDataInCode(); |
| 670 | try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff); | 682 | try macho_file.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff); |
| 671 | try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff); | 683 | try macho_file.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff); |
| 672 | } | 684 | } |
| 673 | 685 | ||
| 674 | fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } { | 686 | fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } { |
| 675 | const gpa = macho_file.base.comp.gpa; | 687 | const gpa = macho_file.base.comp.gpa; |
| 676 | const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); | 688 | const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); |
| 677 | const buffer = try gpa.alloc(u8, needed_size); | 689 | const buffer = try gpa.alloc(u8, needed_size); |
| ... | @@ -686,31 +698,45 @@ fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } { | ... | @@ -686,31 +698,45 @@ fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } { |
| 686 | { | 698 | { |
| 687 | assert(macho_file.segments.items.len == 1); | 699 | assert(macho_file.segments.items.len == 1); |
| 688 | const seg = macho_file.segments.items[0]; | 700 | const seg = macho_file.segments.items[0]; |
| 689 | try writer.writeStruct(seg); | 701 | writer.writeStruct(seg) catch |err| switch (err) { |
| 702 | error.NoSpaceLeft => unreachable, | ||
| 703 | }; | ||
| 690 | for (macho_file.sections.items(.header)) |header| { | 704 | for (macho_file.sections.items(.header)) |header| { |
| 691 | try writer.writeStruct(header); | 705 | writer.writeStruct(header) catch |err| switch (err) { |
| 706 | error.NoSpaceLeft => unreachable, | ||
| 707 | }; | ||
| 692 | } | 708 | } |
| 693 | ncmds += 1; | 709 | ncmds += 1; |
| 694 | } | 710 | } |
| 695 | 711 | ||
| 696 | try writer.writeStruct(macho_file.data_in_code_cmd); | 712 | writer.writeStruct(macho_file.data_in_code_cmd) catch |err| switch (err) { |
| 713 | error.NoSpaceLeft => unreachable, | ||
| 714 | }; | ||
| 697 | ncmds += 1; | 715 | ncmds += 1; |
| 698 | try writer.writeStruct(macho_file.symtab_cmd); | 716 | writer.writeStruct(macho_file.symtab_cmd) catch |err| switch (err) { |
| 717 | error.NoSpaceLeft => unreachable, | ||
| 718 | }; | ||
| 699 | ncmds += 1; | 719 | ncmds += 1; |
| 700 | try writer.writeStruct(macho_file.dysymtab_cmd); | 720 | writer.writeStruct(macho_file.dysymtab_cmd) catch |err| switch (err) { |
| 721 | error.NoSpaceLeft => unreachable, | ||
| 722 | }; | ||
| 701 | ncmds += 1; | 723 | ncmds += 1; |
| 702 | 724 | ||
| 703 | if (macho_file.platform.isBuildVersionCompatible()) { | 725 | if (macho_file.platform.isBuildVersionCompatible()) { |
| 704 | try load_commands.writeBuildVersionLC(macho_file.platform, macho_file.sdk_version, writer); | 726 | load_commands.writeBuildVersionLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) { |
| 727 | error.NoSpaceLeft => unreachable, | ||
| 728 | }; | ||
| 705 | ncmds += 1; | 729 | ncmds += 1; |
| 706 | } else { | 730 | } else { |
| 707 | try load_commands.writeVersionMinLC(macho_file.platform, macho_file.sdk_version, writer); | 731 | load_commands.writeVersionMinLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) { |
| 732 | error.NoSpaceLeft => unreachable, | ||
| 733 | }; | ||
| 708 | ncmds += 1; | 734 | ncmds += 1; |
| 709 | } | 735 | } |
| 710 | 736 | ||
| 711 | assert(stream.pos == needed_size); | 737 | assert(stream.pos == needed_size); |
| 712 | 738 | ||
| 713 | try macho_file.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); | 739 | try macho_file.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); |
| 714 | 740 | ||
| 715 | return .{ ncmds, buffer.len }; | 741 | return .{ ncmds, buffer.len }; |
| 716 | } | 742 | } |
| ... | @@ -742,7 +768,7 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { | ... | @@ -742,7 +768,7 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { |
| 742 | header.ncmds = @intCast(ncmds); | 768 | header.ncmds = @intCast(ncmds); |
| 743 | header.sizeofcmds = @intCast(sizeofcmds); | 769 | header.sizeofcmds = @intCast(sizeofcmds); |
| 744 | 770 | ||
| 745 | try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0); | 771 | try macho_file.pwriteAll(mem.asBytes(&header), 0); |
| 746 | } | 772 | } |
| 747 | 773 | ||
| 748 | const std = @import("std"); | 774 | const std = @import("std"); |
src/link/NvPtx.zig+9-7| ... | @@ -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 | } |
| 84 | 84 | ||
| 85 | pub fn updateFunc(self: *NvPtx, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 85 | pub 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 | } |
| 88 | 94 | ||
| 89 | pub fn updateNav(self: *NvPtx, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { | 95 | pub 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 | } |
| 92 | 98 | ||
| ... | @@ -94,7 +100,7 @@ pub fn updateExports( | ... | @@ -94,7 +100,7 @@ pub fn updateExports( |
| 94 | self: *NvPtx, | 100 | self: *NvPtx, |
| 95 | pt: Zcu.PerThread, | 101 | pt: Zcu.PerThread, |
| 96 | exported: Zcu.Exported, | 102 | exported: Zcu.Exported, |
| 97 | export_indices: []const u32, | 103 | export_indices: []const Zcu.Export.Index, |
| 98 | ) !void { | 104 | ) !void { |
| 99 | if (build_options.skip_non_native and builtin.object_format != .nvptx) | 105 | if (build_options.skip_non_native and builtin.object_format != .nvptx) |
| 100 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 106 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | @@ -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 | } |
| 104 | 110 | ||
| 105 | pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void { | ||
| 106 | return self.llvm_object.freeDecl(decl_index); | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 111 | pub 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+76-110| ... | @@ -60,7 +60,7 @@ fn_nav_table: std.AutoArrayHashMapUnmanaged( | ... | @@ -60,7 +60,7 @@ fn_nav_table: std.AutoArrayHashMapUnmanaged( |
| 60 | data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty, | 60 | data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty, |
| 61 | /// When `updateExports` is called, we store the export indices here, to be used | 61 | /// When `updateExports` is called, we store the export indices here, to be used |
| 62 | /// during flush. | 62 | /// during flush. |
| 63 | nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u32) = .empty, | 63 | nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []Zcu.Export.Index) = .empty, |
| 64 | 64 | ||
| 65 | lazy_syms: LazySymbolTable = .{}, | 65 | lazy_syms: LazySymbolTable = .{}, |
| 66 | 66 | ||
| ... | @@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void | ... | @@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void |
| 345 | try a.writer().writeInt(u16, 1, .big); | 345 | try a.writer().writeInt(u16, 1, .big); |
| 346 | 346 | ||
| 347 | // getting the full file path | 347 | // getting the full file path |
| 348 | // TODO don't call getcwd here, that is inappropriate | ||
| 348 | var buf: [std.fs.max_path_bytes]u8 = undefined; | 349 | var buf: [std.fs.max_path_bytes]u8 = undefined; |
| 349 | const full_path = try std.fs.path.join(arena, &.{ | 350 | const full_path = try std.fs.path.join(arena, &.{ |
| 350 | file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf), | 351 | file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf), |
| ... | @@ -385,7 +386,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi | ... | @@ -385,7 +386,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi |
| 385 | } | 386 | } |
| 386 | } | 387 | } |
| 387 | 388 | ||
| 388 | pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 389 | pub fn updateFunc( |
| 390 | self: *Plan9, | ||
| 391 | pt: Zcu.PerThread, | ||
| 392 | func_index: InternPool.Index, | ||
| 393 | air: Air, | ||
| 394 | liveness: Liveness, | ||
| 395 | ) link.File.UpdateNavError!void { | ||
| 389 | if (build_options.skip_non_native and builtin.object_format != .plan9) { | 396 | 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"); | 397 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 391 | } | 398 | } |
| ... | @@ -397,8 +404,8 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, | ... | @@ -397,8 +404,8 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 397 | 404 | ||
| 398 | const atom_idx = try self.seeNav(pt, func.owner_nav); | 405 | const atom_idx = try self.seeNav(pt, func.owner_nav); |
| 399 | 406 | ||
| 400 | var code_buffer = std.ArrayList(u8).init(gpa); | 407 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 401 | defer code_buffer.deinit(); | 408 | defer code_buffer.deinit(gpa); |
| 402 | var dbg_info_output: DebugInfoOutput = .{ | 409 | var dbg_info_output: DebugInfoOutput = .{ |
| 403 | .dbg_line = std.ArrayList(u8).init(gpa), | 410 | .dbg_line = std.ArrayList(u8).init(gpa), |
| 404 | .start_line = null, | 411 | .start_line = null, |
| ... | @@ -409,7 +416,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, | ... | @@ -409,7 +416,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 409 | }; | 416 | }; |
| 410 | defer dbg_info_output.dbg_line.deinit(); | 417 | defer dbg_info_output.dbg_line.deinit(); |
| 411 | 418 | ||
| 412 | const res = try codegen.generateFunction( | 419 | try codegen.generateFunction( |
| 413 | &self.base, | 420 | &self.base, |
| 414 | pt, | 421 | pt, |
| 415 | zcu.navSrcLoc(func.owner_nav), | 422 | zcu.navSrcLoc(func.owner_nav), |
| ... | @@ -419,10 +426,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, | ... | @@ -419,10 +426,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 419 | &code_buffer, | 426 | &code_buffer, |
| 420 | .{ .plan9 = &dbg_info_output }, | 427 | .{ .plan9 = &dbg_info_output }, |
| 421 | ); | 428 | ); |
| 422 | const code = switch (res) { | 429 | const code = try code_buffer.toOwnedSlice(gpa); |
| 423 | .ok => try code_buffer.toOwnedSlice(), | ||
| 424 | .fail => |em| return zcu.failed_codegen.put(gpa, func.owner_nav, em), | ||
| 425 | }; | ||
| 426 | self.getAtomPtr(atom_idx).code = .{ | 430 | self.getAtomPtr(atom_idx).code = .{ |
| 427 | .code_ptr = null, | 431 | .code_ptr = null, |
| 428 | .other = .{ .nav_index = func.owner_nav }, | 432 | .other = .{ .nav_index = func.owner_nav }, |
| ... | @@ -433,11 +437,13 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, | ... | @@ -433,11 +437,13 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 433 | .start_line = dbg_info_output.start_line.?, | 437 | .start_line = dbg_info_output.start_line.?, |
| 434 | .end_line = dbg_info_output.end_line, | 438 | .end_line = dbg_info_output.end_line, |
| 435 | }; | 439 | }; |
| 436 | try self.putFn(func.owner_nav, out); | 440 | // The awkward error handling here is due to putFn calling `std.posix.getcwd` which it should not do. |
| 441 | self.putFn(func.owner_nav, out) catch |err| | ||
| 442 | return zcu.codegenFail(func.owner_nav, "failed to put fn: {s}", .{@errorName(err)}); | ||
| 437 | return self.updateFinish(pt, func.owner_nav); | 443 | return self.updateFinish(pt, func.owner_nav); |
| 438 | } | 444 | } |
| 439 | 445 | ||
| 440 | pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 446 | pub 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; |
| ... | @@ -456,10 +462,10 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde | ... | @@ -456,10 +462,10 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde |
| 456 | if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) { | 462 | if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) { |
| 457 | const atom_idx = try self.seeNav(pt, nav_index); | 463 | const atom_idx = try self.seeNav(pt, nav_index); |
| 458 | 464 | ||
| 459 | var code_buffer = std.ArrayList(u8).init(gpa); | 465 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 460 | defer code_buffer.deinit(); | 466 | defer code_buffer.deinit(gpa); |
| 461 | // TODO we need the symbol index for symbol in the table of locals for the containing atom | 467 | // TODO we need the symbol index for symbol in the table of locals for the containing atom |
| 462 | const res = try codegen.generateSymbol( | 468 | try codegen.generateSymbol( |
| 463 | &self.base, | 469 | &self.base, |
| 464 | pt, | 470 | pt, |
| 465 | zcu.navSrcLoc(nav_index), | 471 | zcu.navSrcLoc(nav_index), |
| ... | @@ -467,10 +473,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde | ... | @@ -467,10 +473,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde |
| 467 | &code_buffer, | 473 | &code_buffer, |
| 468 | .{ .atom_index = @intCast(atom_idx) }, | 474 | .{ .atom_index = @intCast(atom_idx) }, |
| 469 | ); | 475 | ); |
| 470 | const code = switch (res) { | 476 | const code = code_buffer.items; |
| 471 | .ok => code_buffer.items, | ||
| 472 | .fail => |em| return zcu.failed_codegen.put(gpa, nav_index, em), | ||
| 473 | }; | ||
| 474 | try self.data_nav_table.ensureUnusedCapacity(gpa, 1); | 477 | try self.data_nav_table.ensureUnusedCapacity(gpa, 1); |
| 475 | const duped_code = try gpa.dupe(u8, code); | 478 | const duped_code = try gpa.dupe(u8, code); |
| 476 | self.getAtomPtr(self.navs.get(nav_index).?.index).code = .{ .code_ptr = null, .other = .{ .nav_index = nav_index } }; | 479 | self.getAtomPtr(self.navs.get(nav_index).?.index).code = .{ .code_ptr = null, .other = .{ .nav_index = nav_index } }; |
| ... | @@ -529,16 +532,21 @@ fn allocateGotIndex(self: *Plan9) usize { | ... | @@ -529,16 +532,21 @@ fn allocateGotIndex(self: *Plan9) usize { |
| 529 | } | 532 | } |
| 530 | } | 533 | } |
| 531 | 534 | ||
| 532 | pub fn flush(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 535 | pub fn flush( |
| 536 | self: *Plan9, | ||
| 537 | arena: Allocator, | ||
| 538 | tid: Zcu.PerThread.Id, | ||
| 539 | prog_node: std.Progress.Node, | ||
| 540 | ) link.File.FlushError!void { | ||
| 533 | const comp = self.base.comp; | 541 | const comp = self.base.comp; |
| 542 | const diags = &comp.link_diags; | ||
| 534 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 543 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 535 | assert(!use_lld); | 544 | assert(!use_lld); |
| 536 | 545 | ||
| 537 | switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) { | 546 | switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) { |
| 538 | .Exe => {}, | 547 | .Exe => {}, |
| 539 | // plan9 object files are totally different | 548 | .Obj => return diags.fail("writing plan9 object files unimplemented", .{}), |
| 540 | .Obj => return error.TODOImplementPlan9Objs, | 549 | .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}), |
| 541 | .Lib => return error.TODOImplementWritingLibFiles, | ||
| 542 | } | 550 | } |
| 543 | return self.flushModule(arena, tid, prog_node); | 551 | return self.flushModule(arena, tid, prog_node); |
| 544 | } | 552 | } |
| ... | @@ -583,7 +591,13 @@ fn atomCount(self: *Plan9) usize { | ... | @@ -583,7 +591,13 @@ fn atomCount(self: *Plan9) usize { |
| 583 | return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count; | 591 | return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count; |
| 584 | } | 592 | } |
| 585 | 593 | ||
| 586 | pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 594 | pub fn flushModule( |
| 595 | self: *Plan9, | ||
| 596 | arena: Allocator, | ||
| 597 | /// TODO: stop using this | ||
| 598 | tid: Zcu.PerThread.Id, | ||
| 599 | prog_node: std.Progress.Node, | ||
| 600 | ) link.File.FlushError!void { | ||
| 587 | if (build_options.skip_non_native and builtin.object_format != .plan9) { | 601 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 588 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 602 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 589 | } | 603 | } |
| ... | @@ -594,6 +608,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -594,6 +608,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 594 | _ = arena; // Has the same lifetime as the call to Compilation.update. | 608 | _ = arena; // Has the same lifetime as the call to Compilation.update. |
| 595 | 609 | ||
| 596 | const comp = self.base.comp; | 610 | const comp = self.base.comp; |
| 611 | const diags = &comp.link_diags; | ||
| 597 | const gpa = comp.gpa; | 612 | const gpa = comp.gpa; |
| 598 | const target = comp.root_mod.resolved_target.result; | 613 | const target = comp.root_mod.resolved_target.result; |
| 599 | 614 | ||
| ... | @@ -605,7 +620,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -605,7 +620,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 605 | defer assert(self.hdr.entry != 0x0); | 620 | defer assert(self.hdr.entry != 0x0); |
| 606 | 621 | ||
| 607 | const pt: Zcu.PerThread = .activate( | 622 | const pt: Zcu.PerThread = .activate( |
| 608 | self.base.comp.zcu orelse return error.LinkingWithoutZigSourceUnimplemented, | 623 | self.base.comp.zcu orelse return diags.fail("linking without zig source unimplemented", .{}), |
| 609 | tid, | 624 | tid, |
| 610 | ); | 625 | ); |
| 611 | defer pt.deactivate(); | 626 | defer pt.deactivate(); |
| ... | @@ -614,22 +629,16 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -614,22 +629,16 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 614 | if (self.lazy_syms.getPtr(.none)) |metadata| { | 629 | if (self.lazy_syms.getPtr(.none)) |metadata| { |
| 615 | // Most lazy symbols can be updated on first use, but | 630 | // Most lazy symbols can be updated on first use, but |
| 616 | // anyerror needs to wait for everything to be flushed. | 631 | // anyerror needs to wait for everything to be flushed. |
| 617 | if (metadata.text_state != .unused) self.updateLazySymbolAtom( | 632 | if (metadata.text_state != .unused) try self.updateLazySymbolAtom( |
| 618 | pt, | 633 | pt, |
| 619 | .{ .kind = .code, .ty = .anyerror_type }, | 634 | .{ .kind = .code, .ty = .anyerror_type }, |
| 620 | metadata.text_atom, | 635 | metadata.text_atom, |
| 621 | ) catch |err| return switch (err) { | 636 | ); |
| 622 | error.CodegenFail => error.FlushFailure, | 637 | if (metadata.rodata_state != .unused) try self.updateLazySymbolAtom( |
| 623 | else => |e| e, | ||
| 624 | }; | ||
| 625 | if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( | ||
| 626 | pt, | 638 | pt, |
| 627 | .{ .kind = .const_data, .ty = .anyerror_type }, | 639 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 628 | metadata.rodata_atom, | 640 | metadata.rodata_atom, |
| 629 | ) catch |err| return switch (err) { | 641 | ); |
| 630 | error.CodegenFail => error.FlushFailure, | ||
| 631 | else => |e| e, | ||
| 632 | }; | ||
| 633 | } | 642 | } |
| 634 | for (self.lazy_syms.values()) |*metadata| { | 643 | for (self.lazy_syms.values()) |*metadata| { |
| 635 | if (metadata.text_state != .unused) metadata.text_state = .flushed; | 644 | if (metadata.text_state != .unused) metadata.text_state = .flushed; |
| ... | @@ -902,30 +911,29 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -902,30 +911,29 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 902 | } | 911 | } |
| 903 | } | 912 | } |
| 904 | } | 913 | } |
| 905 | // write it all! | 914 | file.pwritevAll(iovecs, 0) catch |err| return diags.fail("failed to write file: {s}", .{@errorName(err)}); |
| 906 | try file.pwritevAll(iovecs, 0); | ||
| 907 | } | 915 | } |
| 908 | fn addNavExports( | 916 | fn addNavExports( |
| 909 | self: *Plan9, | 917 | self: *Plan9, |
| 910 | mod: *Zcu, | 918 | zcu: *Zcu, |
| 911 | nav_index: InternPool.Nav.Index, | 919 | nav_index: InternPool.Nav.Index, |
| 912 | export_indices: []const u32, | 920 | export_indices: []const Zcu.Export.Index, |
| 913 | ) !void { | 921 | ) !void { |
| 914 | const gpa = self.base.comp.gpa; | 922 | const gpa = self.base.comp.gpa; |
| 915 | const metadata = self.navs.getPtr(nav_index).?; | 923 | const metadata = self.navs.getPtr(nav_index).?; |
| 916 | const atom = self.getAtom(metadata.index); | 924 | const atom = self.getAtom(metadata.index); |
| 917 | 925 | ||
| 918 | for (export_indices) |export_idx| { | 926 | for (export_indices) |export_idx| { |
| 919 | const exp = mod.all_exports.items[export_idx]; | 927 | const exp = export_idx.ptr(zcu); |
| 920 | const exp_name = exp.opts.name.toSlice(&mod.intern_pool); | 928 | const exp_name = exp.opts.name.toSlice(&zcu.intern_pool); |
| 921 | // plan9 does not support custom sections | 929 | // plan9 does not support custom sections |
| 922 | if (exp.opts.section.unwrap()) |section_name| { | 930 | if (exp.opts.section.unwrap()) |section_name| { |
| 923 | if (!section_name.eqlSlice(".text", &mod.intern_pool) and | 931 | if (!section_name.eqlSlice(".text", &zcu.intern_pool) and |
| 924 | !section_name.eqlSlice(".data", &mod.intern_pool)) | 932 | !section_name.eqlSlice(".data", &zcu.intern_pool)) |
| 925 | { | 933 | { |
| 926 | try mod.failed_exports.put(mod.gpa, export_idx, try Zcu.ErrorMsg.create( | 934 | try zcu.failed_exports.put(zcu.gpa, export_idx, try Zcu.ErrorMsg.create( |
| 927 | gpa, | 935 | gpa, |
| 928 | mod.navSrcLoc(nav_index), | 936 | zcu.navSrcLoc(nav_index), |
| 929 | "plan9 does not support extra sections", | 937 | "plan9 does not support extra sections", |
| 930 | .{}, | 938 | .{}, |
| 931 | )); | 939 | )); |
| ... | @@ -947,50 +955,6 @@ fn addNavExports( | ... | @@ -947,50 +955,6 @@ fn addNavExports( |
| 947 | } | 955 | } |
| 948 | } | 956 | } |
| 949 | 957 | ||
| 950 | pub 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 | } | ||
| 994 | fn createAtom(self: *Plan9) !Atom.Index { | 958 | fn createAtom(self: *Plan9) !Atom.Index { |
| 995 | const gpa = self.base.comp.gpa; | 959 | const gpa = self.base.comp.gpa; |
| 996 | const index = @as(Atom.Index, @intCast(self.atoms.items.len)); | 960 | const index = @as(Atom.Index, @intCast(self.atoms.items.len)); |
| ... | @@ -1043,7 +1007,7 @@ pub fn updateExports( | ... | @@ -1043,7 +1007,7 @@ pub fn updateExports( |
| 1043 | self: *Plan9, | 1007 | self: *Plan9, |
| 1044 | pt: Zcu.PerThread, | 1008 | pt: Zcu.PerThread, |
| 1045 | exported: Zcu.Exported, | 1009 | exported: Zcu.Exported, |
| 1046 | export_indices: []const u32, | 1010 | export_indices: []const Zcu.Export.Index, |
| 1047 | ) !void { | 1011 | ) !void { |
| 1048 | const gpa = self.base.comp.gpa; | 1012 | const gpa = self.base.comp.gpa; |
| 1049 | switch (exported) { | 1013 | switch (exported) { |
| ... | @@ -1054,7 +1018,7 @@ pub fn updateExports( | ... | @@ -1054,7 +1018,7 @@ pub fn updateExports( |
| 1054 | gpa.free(kv.value); | 1018 | gpa.free(kv.value); |
| 1055 | } | 1019 | } |
| 1056 | try self.nav_exports.ensureUnusedCapacity(gpa, 1); | 1020 | try self.nav_exports.ensureUnusedCapacity(gpa, 1); |
| 1057 | const duped_indices = try gpa.dupe(u32, export_indices); | 1021 | const duped_indices = try gpa.dupe(Zcu.Export.Index, export_indices); |
| 1058 | self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices); | 1022 | self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices); |
| 1059 | }, | 1023 | }, |
| 1060 | } | 1024 | } |
| ... | @@ -1085,12 +1049,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F | ... | @@ -1085,12 +1049,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F |
| 1085 | return atom; | 1049 | return atom; |
| 1086 | } | 1050 | } |
| 1087 | 1051 | ||
| 1088 | fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, atom_index: Atom.Index) !void { | 1052 | fn updateLazySymbolAtom( |
| 1053 | self: *Plan9, | ||
| 1054 | pt: Zcu.PerThread, | ||
| 1055 | sym: File.LazySymbol, | ||
| 1056 | atom_index: Atom.Index, | ||
| 1057 | ) error{ LinkFailure, OutOfMemory }!void { | ||
| 1089 | const gpa = pt.zcu.gpa; | 1058 | const gpa = pt.zcu.gpa; |
| 1059 | const comp = self.base.comp; | ||
| 1060 | const diags = &comp.link_diags; | ||
| 1090 | 1061 | ||
| 1091 | var required_alignment: InternPool.Alignment = .none; | 1062 | var required_alignment: InternPool.Alignment = .none; |
| 1092 | var code_buffer = std.ArrayList(u8).init(gpa); | 1063 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1093 | defer code_buffer.deinit(); | 1064 | defer code_buffer.deinit(gpa); |
| 1094 | 1065 | ||
| 1095 | // create the symbol for the name | 1066 | // create the symbol for the name |
| 1096 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ | 1067 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ |
| ... | @@ -1107,7 +1078,7 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a | ... | @@ -1107,7 +1078,7 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a |
| 1107 | 1078 | ||
| 1108 | // generate the code | 1079 | // generate the code |
| 1109 | const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded; | 1080 | const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1110 | const res = try codegen.generateLazySymbol( | 1081 | codegen.generateLazySymbol( |
| 1111 | &self.base, | 1082 | &self.base, |
| 1112 | pt, | 1083 | pt, |
| 1113 | src, | 1084 | src, |
| ... | @@ -1116,14 +1087,12 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a | ... | @@ -1116,14 +1087,12 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a |
| 1116 | &code_buffer, | 1087 | &code_buffer, |
| 1117 | .none, | 1088 | .none, |
| 1118 | .{ .atom_index = @intCast(atom_index) }, | 1089 | .{ .atom_index = @intCast(atom_index) }, |
| 1119 | ); | 1090 | ) catch |err| switch (err) { |
| 1120 | const code = switch (res) { | 1091 | error.OutOfMemory => return error.OutOfMemory, |
| 1121 | .ok => code_buffer.items, | 1092 | error.CodegenFail => return error.LinkFailure, |
| 1122 | .fail => |em| { | 1093 | error.Overflow => return diags.fail("codegen failure: encountered number too big for compiler", .{}), |
| 1123 | log.err("{s}", .{em.msg}); | ||
| 1124 | return error.CodegenFail; | ||
| 1125 | }, | ||
| 1126 | }; | 1094 | }; |
| 1095 | const code = code_buffer.items; | ||
| 1127 | // duped_code is freed when the atom is freed | 1096 | // duped_code is freed when the atom is freed |
| 1128 | const duped_code = try gpa.dupe(u8, code); | 1097 | const duped_code = try gpa.dupe(u8, code); |
| 1129 | errdefer gpa.free(duped_code); | 1098 | errdefer gpa.free(duped_code); |
| ... | @@ -1283,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { | ... | @@ -1283,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 1283 | try self.writeSym(writer, sym); | 1252 | try self.writeSym(writer, sym); |
| 1284 | if (self.nav_exports.get(nav_index)) |export_indices| { | 1253 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 1285 | for (export_indices) |export_idx| { | 1254 | for (export_indices) |export_idx| { |
| 1286 | const exp = zcu.all_exports.items[export_idx]; | 1255 | const exp = export_idx.ptr(zcu); |
| 1287 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { | 1256 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { |
| 1288 | try self.writeSym(writer, self.syms.items[exp_i]); | 1257 | try self.writeSym(writer, self.syms.items[exp_i]); |
| 1289 | } | 1258 | } |
| ... | @@ -1322,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { | ... | @@ -1322,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 1322 | try self.writeSym(writer, sym); | 1291 | try self.writeSym(writer, sym); |
| 1323 | if (self.nav_exports.get(nav_index)) |export_indices| { | 1292 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 1324 | for (export_indices) |export_idx| { | 1293 | for (export_indices) |export_idx| { |
| 1325 | const exp = zcu.all_exports.items[export_idx]; | 1294 | const exp = export_idx.ptr(zcu); |
| 1326 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { | 1295 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { |
| 1327 | const s = self.syms.items[exp_i]; | 1296 | const s = self.syms.items[exp_i]; |
| 1328 | if (mem.eql(u8, s.name, "_start")) | 1297 | if (mem.eql(u8, s.name, "_start")) |
| ... | @@ -1432,19 +1401,16 @@ pub fn lowerUav( | ... | @@ -1432,19 +1401,16 @@ pub fn lowerUav( |
| 1432 | const got_index = self.allocateGotIndex(); | 1401 | const got_index = self.allocateGotIndex(); |
| 1433 | gop.value_ptr.* = index; | 1402 | gop.value_ptr.* = index; |
| 1434 | // we need to free name latex | 1403 | // we need to free name latex |
| 1435 | var code_buffer = std.ArrayList(u8).init(gpa); | 1404 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1436 | const res = try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index }); | 1405 | defer code_buffer.deinit(gpa); |
| 1437 | const code = switch (res) { | 1406 | try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index }); |
| 1438 | .ok => code_buffer.items, | ||
| 1439 | .fail => |em| return .{ .fail = em }, | ||
| 1440 | }; | ||
| 1441 | const atom_ptr = self.getAtomPtr(index); | 1407 | const atom_ptr = self.getAtomPtr(index); |
| 1442 | atom_ptr.* = .{ | 1408 | atom_ptr.* = .{ |
| 1443 | .type = .d, | 1409 | .type = .d, |
| 1444 | .offset = undefined, | 1410 | .offset = undefined, |
| 1445 | .sym_index = null, | 1411 | .sym_index = null, |
| 1446 | .got_index = got_index, | 1412 | .got_index = got_index, |
| 1447 | .code = Atom.CodePtr.fromSlice(code), | 1413 | .code = Atom.CodePtr.fromSlice(try code_buffer.toOwnedSlice(gpa)), |
| 1448 | }; | 1414 | }; |
| 1449 | _ = try atom_ptr.getOrCreateSymbolTableEntry(self); | 1415 | _ = try atom_ptr.getOrCreateSymbolTableEntry(self); |
| 1450 | self.syms.items[atom_ptr.sym_index.?] = .{ | 1416 | self.syms.items[atom_ptr.sym_index.?] = .{ |
src/link/SpirV.zig+26-18| ... | @@ -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 | } |
| 124 | 124 | ||
| 125 | pub fn updateFunc(self: *SpirV, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 125 | pub 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 | } |
| 136 | 142 | ||
| 137 | pub fn updateNav(self: *SpirV, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { | 143 | pub 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 | } |
| ... | @@ -149,7 +155,7 @@ pub fn updateExports( | ... | @@ -149,7 +155,7 @@ pub fn updateExports( |
| 149 | self: *SpirV, | 155 | self: *SpirV, |
| 150 | pt: Zcu.PerThread, | 156 | pt: Zcu.PerThread, |
| 151 | exported: Zcu.Exported, | 157 | exported: Zcu.Exported, |
| 152 | export_indices: []const u32, | 158 | export_indices: []const Zcu.Export.Index, |
| 153 | ) !void { | 159 | ) !void { |
| 154 | const zcu = pt.zcu; | 160 | const zcu = pt.zcu; |
| 155 | const ip = &zcu.intern_pool; | 161 | const ip = &zcu.intern_pool; |
| ... | @@ -184,7 +190,7 @@ pub fn updateExports( | ... | @@ -184,7 +190,7 @@ pub fn updateExports( |
| 184 | }; | 190 | }; |
| 185 | 191 | ||
| 186 | for (export_indices) |export_idx| { | 192 | for (export_indices) |export_idx| { |
| 187 | const exp = zcu.all_exports.items[export_idx]; | 193 | const exp = export_idx.ptr(zcu); |
| 188 | try self.object.spv.declareEntryPoint( | 194 | try self.object.spv.declareEntryPoint( |
| 189 | spv_decl_index, | 195 | spv_decl_index, |
| 190 | exp.opts.name.toSlice(ip), | 196 | exp.opts.name.toSlice(ip), |
| ... | @@ -196,16 +202,21 @@ pub fn updateExports( | ... | @@ -196,16 +202,21 @@ 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 | } |
| 198 | 204 | ||
| 199 | pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void { | ||
| 200 | _ = self; | ||
| 201 | _ = decl_index; | ||
| 202 | } | ||
| 203 | |||
| 204 | pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 205 | pub 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 | } |
| 207 | 208 | ||
| 208 | pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 209 | pub fn flushModule( |
| 210 | self: *SpirV, | ||
| 211 | arena: Allocator, | ||
| 212 | tid: Zcu.PerThread.Id, | ||
| 213 | prog_node: std.Progress.Node, | ||
| 214 | ) link.File.FlushError!void { | ||
| 215 | // The goal is to never use this because it's only needed if we need to | ||
| 216 | // write to InternPool, but flushModule is too late to be writing to the | ||
| 217 | // InternPool. | ||
| 218 | _ = tid; | ||
| 219 | |||
| 209 | if (build_options.skip_non_native) { | 220 | if (build_options.skip_non_native) { |
| 210 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 221 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 211 | } | 222 | } |
| ... | @@ -216,12 +227,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -216,12 +227,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 216 | const sub_prog_node = prog_node.start("Flush Module", 0); | 227 | const sub_prog_node = prog_node.start("Flush Module", 0); |
| 217 | defer sub_prog_node.end(); | 228 | defer sub_prog_node.end(); |
| 218 | 229 | ||
| 219 | const spv = &self.object.spv; | ||
| 220 | |||
| 221 | const comp = self.base.comp; | 230 | const comp = self.base.comp; |
| 231 | const spv = &self.object.spv; | ||
| 232 | const diags = &comp.link_diags; | ||
| 222 | const gpa = comp.gpa; | 233 | const gpa = comp.gpa; |
| 223 | const target = comp.getTarget(); | 234 | const target = comp.getTarget(); |
| 224 | _ = tid; | ||
| 225 | 235 | ||
| 226 | try writeCapabilities(spv, target); | 236 | try writeCapabilities(spv, target); |
| 227 | try writeMemoryModel(spv, target); | 237 | try writeMemoryModel(spv, target); |
| ... | @@ -264,13 +274,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n | ... | @@ -264,13 +274,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 264 | 274 | ||
| 265 | const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { | 275 | const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { |
| 266 | error.OutOfMemory => return error.OutOfMemory, | 276 | error.OutOfMemory => return error.OutOfMemory, |
| 267 | else => |other| { | 277 | else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}), |
| 268 | log.err("error while linking: {s}", .{@errorName(other)}); | ||
| 269 | return error.FlushFailure; | ||
| 270 | }, | ||
| 271 | }; | 278 | }; |
| 272 | 279 | ||
| 273 | try self.base.file.?.writeAll(std.mem.sliceAsBytes(linked_module)); | 280 | self.base.file.?.writeAll(std.mem.sliceAsBytes(linked_module)) catch |err| |
| 281 | return diags.fail("failed to write: {s}", .{@errorName(err)}); | ||
| 274 | } | 282 | } |
| 275 | 283 | ||
| 276 | fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress.Node) ![]Word { | 284 | fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress.Node) ![]Word { |
src/link/Wasm.zig+3698-3751| ... | @@ -1,45 +1,67 @@ | ... | @@ -1,45 +1,67 @@ |
| 1 | //! The overall strategy here is to load all the object file data into memory | ||
| 2 | //! as inputs are parsed. During `prelink`, as much linking as possible is | ||
| 3 | //! performed without any knowledge of functions and globals provided by the | ||
| 4 | //! Zcu. If there is no Zcu, effectively all linking is done in `prelink`. | ||
| 5 | //! | ||
| 6 | //! `updateFunc`, `updateNav`, `updateExports`, and `deleteExport` are handled | ||
| 7 | //! by merely tracking references to the relevant functions and globals. All | ||
| 8 | //! the linking logic between objects and Zcu happens in `flush`. Many | ||
| 9 | //! components of the final output are computed on-the-fly at this time rather | ||
| 10 | //! than being precomputed and stored separately. | ||
| 11 | |||
| 1 | const Wasm = @This(); | 12 | const Wasm = @This(); |
| 2 | const build_options = @import("build_options"); | 13 | const Archive = @import("Wasm/Archive.zig"); |
| 14 | const Object = @import("Wasm/Object.zig"); | ||
| 15 | pub const Flush = @import("Wasm/Flush.zig"); | ||
| 3 | 16 | ||
| 4 | const builtin = @import("builtin"); | 17 | const builtin = @import("builtin"); |
| 5 | const native_endian = builtin.cpu.arch.endian(); | 18 | const native_endian = builtin.cpu.arch.endian(); |
| 6 | 19 | ||
| 20 | const build_options = @import("build_options"); | ||
| 21 | |||
| 7 | const std = @import("std"); | 22 | const std = @import("std"); |
| 8 | const Allocator = std.mem.Allocator; | 23 | const Allocator = std.mem.Allocator; |
| 9 | const Cache = std.Build.Cache; | 24 | const Cache = std.Build.Cache; |
| 10 | const Path = Cache.Path; | 25 | const Path = Cache.Path; |
| 11 | const assert = std.debug.assert; | 26 | const assert = std.debug.assert; |
| 12 | const fs = std.fs; | 27 | const fs = std.fs; |
| 13 | const gc_log = std.log.scoped(.gc); | ||
| 14 | const leb = std.leb; | 28 | const leb = std.leb; |
| 15 | const log = std.log.scoped(.link); | 29 | const log = std.log.scoped(.link); |
| 16 | const mem = std.mem; | 30 | const mem = std.mem; |
| 17 | 31 | ||
| 18 | const Air = @import("../Air.zig"); | 32 | const Air = @import("../Air.zig"); |
| 19 | const Archive = @import("Wasm/Archive.zig"); | 33 | const Mir = @import("../arch/wasm/Mir.zig"); |
| 20 | const CodeGen = @import("../arch/wasm/CodeGen.zig"); | 34 | const CodeGen = @import("../arch/wasm/CodeGen.zig"); |
| 35 | const abi = @import("../arch/wasm/abi.zig"); | ||
| 21 | const Compilation = @import("../Compilation.zig"); | 36 | const Compilation = @import("../Compilation.zig"); |
| 22 | const Dwarf = @import("Dwarf.zig"); | 37 | const Dwarf = @import("Dwarf.zig"); |
| 23 | const InternPool = @import("../InternPool.zig"); | 38 | const InternPool = @import("../InternPool.zig"); |
| 24 | const Liveness = @import("../Liveness.zig"); | 39 | const Liveness = @import("../Liveness.zig"); |
| 25 | const LlvmObject = @import("../codegen/llvm.zig").Object; | 40 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 26 | const Object = @import("Wasm/Object.zig"); | ||
| 27 | const Symbol = @import("Wasm/Symbol.zig"); | ||
| 28 | const Type = @import("../Type.zig"); | ||
| 29 | const Value = @import("../Value.zig"); | ||
| 30 | const Zcu = @import("../Zcu.zig"); | 41 | const Zcu = @import("../Zcu.zig"); |
| 31 | const ZigObject = @import("Wasm/ZigObject.zig"); | ||
| 32 | const codegen = @import("../codegen.zig"); | 42 | const codegen = @import("../codegen.zig"); |
| 33 | const dev = @import("../dev.zig"); | 43 | const dev = @import("../dev.zig"); |
| 34 | const link = @import("../link.zig"); | 44 | const link = @import("../link.zig"); |
| 35 | const lldMain = @import("../main.zig").lldMain; | 45 | const lldMain = @import("../main.zig").lldMain; |
| 36 | const trace = @import("../tracy.zig").trace; | 46 | const trace = @import("../tracy.zig").trace; |
| 37 | const wasi_libc = @import("../wasi_libc.zig"); | 47 | const wasi_libc = @import("../wasi_libc.zig"); |
| 48 | const Value = @import("../Value.zig"); | ||
| 38 | 49 | ||
| 39 | base: link.File, | 50 | base: link.File, |
| 40 | /// Null-terminated strings, indexes have type String and string_table provides | 51 | /// Null-terminated strings, indexes have type String and string_table provides |
| 41 | /// lookup. | 52 | /// lookup. |
| 53 | /// | ||
| 54 | /// There are a couple of sites that add things here without adding | ||
| 55 | /// corresponding string_table entries. For such cases, when implementing | ||
| 56 | /// serialization/deserialization, they should be adjusted to prefix that data | ||
| 57 | /// with a null byte so that deserialization does not attempt to create | ||
| 58 | /// string_table entries for them. Alternately those sites could be moved to | ||
| 59 | /// use a different byte array for this purpose. | ||
| 42 | string_bytes: std.ArrayListUnmanaged(u8), | 60 | string_bytes: std.ArrayListUnmanaged(u8), |
| 61 | /// Sometimes we have logic that wants to borrow string bytes to store | ||
| 62 | /// arbitrary things in there. In this case it is not allowed to intern new | ||
| 63 | /// strings during this time. This safety lock is used to detect misuses. | ||
| 64 | string_bytes_lock: std.debug.SafetyLock = .{}, | ||
| 43 | /// Omitted when serializing linker state. | 65 | /// Omitted when serializing linker state. |
| 44 | string_table: String.Table, | 66 | string_table: String.Table, |
| 45 | /// Symbol name of the entry function to export | 67 | /// Symbol name of the entry function to export |
| ... | @@ -62,2525 +84,3230 @@ export_table: bool, | ... | @@ -62,2525 +84,3230 @@ export_table: bool, |
| 62 | name: []const u8, | 84 | name: []const u8, |
| 63 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 85 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 64 | llvm_object: ?LlvmObject.Ptr = null, | 86 | llvm_object: ?LlvmObject.Ptr = null, |
| 65 | zig_object: ?*ZigObject, | ||
| 66 | /// List of relocatable files to be linked into the final binary. | 87 | /// List of relocatable files to be linked into the final binary. |
| 67 | objects: std.ArrayListUnmanaged(Object) = .{}, | 88 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 89 | |||
| 90 | func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty, | ||
| 91 | /// Provides a mapping of both imports and provided functions to symbol name. | ||
| 92 | /// Local functions may be unnamed. | ||
| 93 | /// Key is symbol name, however the `FunctionImport` may have an name override for the import name. | ||
| 94 | object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty, | ||
| 95 | /// All functions for all objects. | ||
| 96 | object_functions: std.ArrayListUnmanaged(ObjectFunction) = .empty, | ||
| 97 | |||
| 98 | /// Provides a mapping of both imports and provided globals to symbol name. | ||
| 99 | /// Local globals may be unnamed. | ||
| 100 | object_global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImport) = .empty, | ||
| 101 | /// All globals for all objects. | ||
| 102 | object_globals: std.ArrayListUnmanaged(ObjectGlobal) = .empty, | ||
| 103 | |||
| 104 | /// All table imports for all objects. | ||
| 105 | object_table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport) = .empty, | ||
| 106 | /// All parsed table sections for all objects. | ||
| 107 | object_tables: std.ArrayListUnmanaged(Table) = .empty, | ||
| 108 | |||
| 109 | /// All memory imports for all objects. | ||
| 110 | object_memory_imports: std.AutoArrayHashMapUnmanaged(String, MemoryImport) = .empty, | ||
| 111 | /// All parsed memory sections for all objects. | ||
| 112 | object_memories: std.ArrayListUnmanaged(ObjectMemory) = .empty, | ||
| 113 | |||
| 114 | /// All relocations from all objects concatenated. `relocs_start` marks the end | ||
| 115 | /// point of object relocations and start point of Zcu relocations. | ||
| 116 | object_relocations: std.MultiArrayList(ObjectRelocation) = .empty, | ||
| 117 | |||
| 118 | /// List of initialization functions. These must be called in order of priority | ||
| 119 | /// by the (synthetic) `__wasm_call_ctors` function. | ||
| 120 | object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty, | ||
| 121 | |||
| 122 | /// The data section of an object has many segments. Each segment corresponds | ||
| 123 | /// logically to an object file's .data section, or .rodata section. In | ||
| 124 | /// the case of `-fdata-sections` there will be one segment per data symbol. | ||
| 125 | object_data_segments: std.ArrayListUnmanaged(ObjectDataSegment) = .empty, | ||
| 126 | /// Each segment has many data symbols, which correspond logically to global | ||
| 127 | /// constants. | ||
| 128 | object_datas: std.ArrayListUnmanaged(ObjectData) = .empty, | ||
| 129 | object_data_imports: std.AutoArrayHashMapUnmanaged(String, ObjectDataImport) = .empty, | ||
| 130 | /// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations. | ||
| 131 | object_custom_segments: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, CustomSegment) = .empty, | ||
| 132 | |||
| 133 | /// All comdat information for all objects. | ||
| 134 | object_comdats: std.ArrayListUnmanaged(Comdat) = .empty, | ||
| 135 | /// A table that maps the relocations to be performed where the key represents | ||
| 136 | /// the section (across all objects) that the slice of relocations applies to. | ||
| 137 | object_relocations_table: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, ObjectRelocation.Slice) = .empty, | ||
| 138 | /// Incremented across all objects in order to enable calculation of `ObjectSectionIndex` values. | ||
| 139 | object_total_sections: u32 = 0, | ||
| 140 | /// All comdat symbols from all objects concatenated. | ||
| 141 | object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty, | ||
| 142 | |||
| 143 | /// Relocations to be emitted into an object file. Remains empty when not | ||
| 144 | /// emitting an object file. | ||
| 145 | out_relocs: std.MultiArrayList(OutReloc) = .empty, | ||
| 146 | /// List of locations within `string_bytes` that must be patched with the virtual | ||
| 147 | /// memory address of a Uav during `flush`. | ||
| 148 | /// When emitting an object file, `out_relocs` is used instead. | ||
| 149 | uav_fixups: std.ArrayListUnmanaged(UavFixup) = .empty, | ||
| 150 | /// List of locations within `string_bytes` that must be patched with the virtual | ||
| 151 | /// memory address of a Nav during `flush`. | ||
| 152 | /// When emitting an object file, `out_relocs` is used instead. | ||
| 153 | /// No functions here only global variables. | ||
| 154 | nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty, | ||
| 155 | /// When a nav reference is a function pointer, this tracks the required function | ||
| 156 | /// table entry index that needs to overwrite the code in the final output. | ||
| 157 | func_table_fixups: std.ArrayListUnmanaged(FuncTableFixup) = .empty, | ||
| 158 | /// Symbols to be emitted into an object file. Remains empty when not emitting | ||
| 159 | /// an object file. | ||
| 160 | symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty, | ||
| 161 | |||
| 68 | /// When importing objects from the host environment, a name must be supplied. | 162 | /// 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 Zig | 163 | /// LLVM uses "env" by default when none is given. |
| 70 | /// to support existing code. | 164 | /// This value is passed to object files since wasm tooling conventions provides |
| 71 | /// TODO: Allow setting this through a flag? | 165 | /// no way to specify the module name in the symbol table. |
| 72 | host_name: String, | 166 | object_host_name: OptionalString, |
| 73 | /// List of symbols generated by the linker. | 167 | |
| 74 | synthetic_symbols: std.ArrayListUnmanaged(Symbol) = .empty, | ||
| 75 | /// Maps atoms to their segment index | ||
| 76 | atoms: std.AutoHashMapUnmanaged(Segment.Index, Atom.Index) = .empty, | ||
| 77 | /// List of all atoms. | ||
| 78 | managed_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. | ||
| 82 | imported_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. | ||
| 85 | imported_globals_count: u32 = 0, | ||
| 86 | /// The count of imported tables. This number will be appended | ||
| 87 | /// to the table indexes when sections are merged. | ||
| 88 | imported_tables_count: u32 = 0, | ||
| 89 | /// Map of symbol locations, represented by its `Import` | ||
| 90 | imports: std.AutoHashMapUnmanaged(SymbolLoc, Import) = .empty, | ||
| 91 | /// Represents non-synthetic section entries. | ||
| 92 | /// Used for code, data and custom sections. | ||
| 93 | segments: std.ArrayListUnmanaged(Segment) = .empty, | ||
| 94 | /// Maps a data segment key (such as .rodata) to the index into `segments`. | ||
| 95 | data_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` | ||
| 99 | segment_info: std.AutoArrayHashMapUnmanaged(Segment.Index, NamedSegment) = .empty, | ||
| 100 | |||
| 101 | // Output sections | ||
| 102 | /// Output type section | ||
| 103 | func_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. | ||
| 107 | functions: 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 | ||
| 119 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty, | ||
| 120 | /// Memory section | 168 | /// Memory section |
| 121 | memories: std.wasm.Memory = .{ .limits = .{ | 169 | memories: std.wasm.Memory = .{ .limits = .{ |
| 122 | .min = 0, | 170 | .min = 0, |
| 123 | .max = undefined, | 171 | .max = 0, |
| 124 | .flags = 0, | 172 | .flags = .{ .has_max = false, .is_shared = false }, |
| 125 | } }, | 173 | } }, |
| 126 | /// Output table section | ||
| 127 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .empty, | ||
| 128 | /// Output export section | ||
| 129 | exports: 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. | ||
| 132 | init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .empty, | ||
| 133 | /// Index to a function defining the entry of the wasm file | ||
| 134 | entry: ?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 | ||
| 141 | function_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. | ||
| 148 | lazy_archives: std.ArrayListUnmanaged(LazyArchive) = .empty, | ||
| 149 | |||
| 150 | /// A map of global names to their symbol location | ||
| 151 | globals: std.AutoArrayHashMapUnmanaged(String, SymbolLoc) = .empty, | ||
| 152 | /// The list of GOT symbols and their location | ||
| 153 | got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .empty, | ||
| 154 | /// Maps discarded symbols and their positions to the location of the symbol | ||
| 155 | /// it was resolved to | ||
| 156 | discarded: 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. | ||
| 159 | resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .empty, | ||
| 160 | /// Symbols that remain undefined after symbol resolution. | ||
| 161 | undefs: 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. | ||
| 165 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .empty, | ||
| 166 | 174 | ||
| 167 | /// `--verbose-link` output. | 175 | /// `--verbose-link` output. |
| 168 | /// Initialized on creation, appended to as inputs are added, printed during `flush`. | 176 | /// Initialized on creation, appended to as inputs are added, printed during `flush`. |
| 169 | /// String data is allocated into Compilation arena. | 177 | /// String data is allocated into Compilation arena. |
| 170 | dump_argv_list: std.ArrayListUnmanaged([]const u8), | 178 | dump_argv_list: std.ArrayListUnmanaged([]const u8), |
| 171 | 179 | ||
| 172 | /// Represents the index into `segments` where the 'code' section lives. | ||
| 173 | code_section_index: Segment.OptionalIndex = .none, | ||
| 174 | custom_sections: CustomSections, | ||
| 175 | preloaded_strings: PreloadedStrings, | 180 | preloaded_strings: PreloadedStrings, |
| 176 | 181 | ||
| 177 | /// Type reflection is used on the field names to autopopulate each field | 182 | /// This field is used when emitting an object; `navs_exe` used otherwise. |
| 178 | /// during initialization. | 183 | /// Does not include externs since that data lives elsewhere. |
| 179 | const PreloadedStrings = struct { | 184 | navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty, |
| 180 | __heap_base: String, | 185 | /// This field is unused when emitting an object; `navs_obj` used otherwise. |
| 181 | __heap_end: String, | 186 | /// Does not include externs since that data lives elsewhere. |
| 182 | __indirect_function_table: String, | 187 | navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty, |
| 183 | __linear_memory: String, | 188 | /// Tracks all InternPool values referenced by codegen. Needed for outputting |
| 184 | __stack_pointer: String, | 189 | /// the data segment. This one does not track ref count because object files |
| 185 | __tls_align: String, | 190 | /// require using max LEB encoding for these references anyway. |
| 186 | __tls_base: String, | 191 | uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty, |
| 187 | __tls_size: String, | 192 | /// Tracks ref count to optimize LEB encodings for UAV references. |
| 188 | __wasm_apply_global_tls_relocs: String, | 193 | uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty, |
| 189 | __wasm_call_ctors: String, | 194 | /// Sparse table of uavs that need to be emitted with greater alignment than |
| 190 | __wasm_init_memory: String, | 195 | /// the default for the type. |
| 191 | __wasm_init_memory_flag: String, | 196 | overaligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty, |
| 192 | __wasm_init_tls: String, | 197 | /// When the key is an enum type, this represents a `@tagName` function. |
| 193 | __zig_err_name_table: String, | 198 | zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty, |
| 194 | __zig_err_names: String, | 199 | nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty, |
| 195 | __zig_errors_len: String, | 200 | uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty, |
| 196 | _initialize: String, | 201 | imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, |
| 197 | _start: String, | 202 | |
| 198 | memory: String, | 203 | dwarf: ?Dwarf = null, |
| 204 | |||
| 205 | flush_buffer: Flush = .{}, | ||
| 206 | |||
| 207 | /// Empty until `prelink`. There it is populated based on object files. | ||
| 208 | /// Next, it is copied into `Flush.missing_exports` just before `flush` | ||
| 209 | /// and that data is used during `flush`. | ||
| 210 | missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty, | ||
| 211 | entry_resolution: FunctionImport.Resolution = .unresolved, | ||
| 212 | |||
| 213 | /// Empty when outputting an object. | ||
| 214 | function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty, | ||
| 215 | hidden_function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty, | ||
| 216 | global_exports: std.ArrayListUnmanaged(GlobalExport) = .empty, | ||
| 217 | /// Tracks the value at the end of prelink. | ||
| 218 | global_exports_len: u32 = 0, | ||
| 219 | |||
| 220 | /// Ordered list of non-import functions that will appear in the final binary. | ||
| 221 | /// Empty until prelink. | ||
| 222 | functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .empty, | ||
| 223 | /// Tracks the value at the end of prelink, at which point `functions` | ||
| 224 | /// contains only object file functions, and nothing from the Zcu yet. | ||
| 225 | functions_end_prelink: u32 = 0, | ||
| 226 | |||
| 227 | function_imports_len_prelink: u32 = 0, | ||
| 228 | data_imports_len_prelink: u32 = 0, | ||
| 229 | /// At the end of prelink, this is populated with needed functions from | ||
| 230 | /// objects. | ||
| 231 | /// | ||
| 232 | /// During the Zcu phase, entries are not deleted from this table | ||
| 233 | /// because doing so would be irreversible when a `deleteExport` call is | ||
| 234 | /// handled. However, entries are added during the Zcu phase when extern | ||
| 235 | /// functions are passed to `updateNav`. | ||
| 236 | /// | ||
| 237 | /// `flush` gets a copy of this table, and then Zcu exports are applied to | ||
| 238 | /// remove elements from the table, and the remainder are either undefined | ||
| 239 | /// symbol errors, or import section entries depending on the output mode. | ||
| 240 | function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty, | ||
| 241 | |||
| 242 | /// At the end of prelink, this is populated with data symbols needed by | ||
| 243 | /// objects. | ||
| 244 | /// | ||
| 245 | /// During the Zcu phase, entries are not deleted from this table | ||
| 246 | /// because doing so would be irreversible when a `deleteExport` call is | ||
| 247 | /// handled. However, entries are added during the Zcu phase when extern | ||
| 248 | /// functions are passed to `updateNav`. | ||
| 249 | /// | ||
| 250 | /// `flush` gets a copy of this table, and then Zcu exports are applied to | ||
| 251 | /// remove elements from the table, and the remainder are either undefined | ||
| 252 | /// symbol errors, or symbol table entries depending on the output mode. | ||
| 253 | data_imports: std.AutoArrayHashMapUnmanaged(String, DataImportId) = .empty, | ||
| 254 | /// Set of data symbols that will appear in the final binary. Used to populate | ||
| 255 | /// `Flush.data_segments` before sorting. | ||
| 256 | data_segments: std.AutoArrayHashMapUnmanaged(DataSegmentId, void) = .empty, | ||
| 257 | |||
| 258 | /// Ordered list of non-import globals that will appear in the final binary. | ||
| 259 | /// Empty until prelink. | ||
| 260 | globals: std.AutoArrayHashMapUnmanaged(GlobalImport.Resolution, void) = .empty, | ||
| 261 | /// Tracks the value at the end of prelink, at which point `globals` | ||
| 262 | /// contains only object file globals, and nothing from the Zcu yet. | ||
| 263 | globals_end_prelink: u32 = 0, | ||
| 264 | global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImportId) = .empty, | ||
| 265 | |||
| 266 | /// Ordered list of non-import tables that will appear in the final binary. | ||
| 267 | /// Empty until prelink. | ||
| 268 | tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty, | ||
| 269 | table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty, | ||
| 270 | |||
| 271 | /// All functions that have had their address taken and therefore might be | ||
| 272 | /// called via a `call_indirect` function. | ||
| 273 | zcu_indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, | ||
| 274 | object_indirect_function_import_set: std.AutoArrayHashMapUnmanaged(String, void) = .empty, | ||
| 275 | object_indirect_function_set: std.AutoArrayHashMapUnmanaged(ObjectFunctionIndex, void) = .empty, | ||
| 276 | |||
| 277 | error_name_table_ref_count: u32 = 0, | ||
| 278 | tag_name_table_ref_count: u32 = 0, | ||
| 279 | |||
| 280 | /// Set to true if any `GLOBAL_INDEX` relocation is encountered with | ||
| 281 | /// `SymbolFlags.tls` set to true. This is for objects only; final | ||
| 282 | /// value must be this OR'd with the same logic for zig functions | ||
| 283 | /// (set to true if any threadlocal global is used). | ||
| 284 | any_tls_relocs: bool = false, | ||
| 285 | any_passive_inits: bool = false, | ||
| 286 | |||
| 287 | /// All MIR instructions for all Zcu functions. | ||
| 288 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | ||
| 289 | /// Corresponds to `mir_instructions`. | ||
| 290 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | ||
| 291 | /// All local types for all Zcu functions. | ||
| 292 | all_zcu_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | ||
| 293 | |||
| 294 | params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | ||
| 295 | returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | ||
| 296 | |||
| 297 | /// All Zcu error names in order, null-terminated, concatenated. No need to | ||
| 298 | /// serialize; trivially reconstructed. | ||
| 299 | error_name_bytes: std.ArrayListUnmanaged(u8) = .empty, | ||
| 300 | /// For each Zcu error, in order, offset into `error_name_bytes` where the name | ||
| 301 | /// is stored. No need to serialize; trivially reconstructed. | ||
| 302 | error_name_offs: std.ArrayListUnmanaged(u32) = .empty, | ||
| 303 | |||
| 304 | tag_name_bytes: std.ArrayListUnmanaged(u8) = .empty, | ||
| 305 | tag_name_offs: std.ArrayListUnmanaged(u32) = .empty, | ||
| 306 | |||
| 307 | pub const TagNameOff = extern struct { | ||
| 308 | off: u32, | ||
| 309 | len: u32, | ||
| 199 | }; | 310 | }; |
| 200 | 311 | ||
| 201 | /// Type reflection is used on the field names to autopopulate each inner `name` field. | 312 | /// Index into `Wasm.zcu_indirect_function_set`. |
| 202 | const CustomSections = struct { | 313 | pub const ZcuIndirectFunctionSetIndex = enum(u32) { |
| 203 | @".debug_info": CustomSection, | 314 | _, |
| 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 | }; | 315 | }; |
| 212 | 316 | ||
| 213 | const CustomSection = struct { | 317 | pub const UavFixup = extern struct { |
| 214 | name: String, | 318 | uavs_exe_index: UavsExeIndex, |
| 215 | index: Segment.OptionalIndex, | 319 | /// Index into `string_bytes`. |
| 320 | offset: u32, | ||
| 321 | addend: u32, | ||
| 216 | }; | 322 | }; |
| 217 | 323 | ||
| 218 | /// Index into string_bytes | 324 | pub const NavFixup = extern struct { |
| 219 | pub const String = enum(u32) { | 325 | navs_exe_index: NavsExeIndex, |
| 220 | _, | 326 | /// Index into `string_bytes`. |
| 327 | offset: u32, | ||
| 328 | addend: u32, | ||
| 329 | }; | ||
| 221 | 330 | ||
| 222 | const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage); | 331 | pub const FuncTableFixup = extern struct { |
| 332 | table_index: ZcuIndirectFunctionSetIndex, | ||
| 333 | /// Index into `string_bytes`. | ||
| 334 | offset: u32, | ||
| 335 | }; | ||
| 223 | 336 | ||
| 224 | const TableContext = struct { | 337 | /// Index into `objects`. |
| 225 | bytes: []const u8, | 338 | pub const ObjectIndex = enum(u32) { |
| 339 | _, | ||
| 226 | 340 | ||
| 227 | pub fn eql(_: @This(), a: String, b: String) bool { | 341 | pub fn ptr(index: ObjectIndex, wasm: *const Wasm) *Object { |
| 228 | return a == b; | 342 | return &wasm.objects.items[@intFromEnum(index)]; |
| 229 | } | 343 | } |
| 344 | }; | ||
| 230 | 345 | ||
| 231 | pub fn hash(ctx: @This(), key: String) u64 { | 346 | /// Index into `Wasm.functions`. |
| 232 | return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0)); | 347 | pub const FunctionIndex = enum(u32) { |
| 233 | } | 348 | _, |
| 234 | }; | ||
| 235 | 349 | ||
| 236 | const TableIndexAdapter = struct { | 350 | pub fn ptr(index: FunctionIndex, wasm: *const Wasm) *FunctionImport.Resolution { |
| 237 | bytes: []const u8, | 351 | return &wasm.functions.keys()[@intFromEnum(index)]; |
| 352 | } | ||
| 238 | 353 | ||
| 239 | pub fn eql(ctx: @This(), a: []const u8, b: String) bool { | 354 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex { |
| 240 | return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0)); | 355 | return fromResolution(wasm, .fromIpNav(wasm, nav_index)); |
| 241 | } | 356 | } |
| 242 | 357 | ||
| 243 | pub fn hash(_: @This(), adapted_key: []const u8) u64 { | 358 | pub fn fromTagNameType(wasm: *const Wasm, tag_type: InternPool.Index) ?FunctionIndex { |
| 244 | assert(mem.indexOfScalar(u8, adapted_key, 0) == null); | 359 | const zcu_func: ZcuFunc.Index = @enumFromInt(wasm.zcu_funcs.getIndex(tag_type) orelse return null); |
| 245 | return std.hash_map.hashString(adapted_key); | 360 | return fromResolution(wasm, .pack(wasm, .{ .zcu_func = zcu_func })); |
| 361 | } | ||
| 362 | |||
| 363 | pub fn fromSymbolName(wasm: *const Wasm, name: String) ?FunctionIndex { | ||
| 364 | if (wasm.object_function_imports.getPtr(name)) |import| { | ||
| 365 | return fromResolution(wasm, import.resolution); | ||
| 246 | } | 366 | } |
| 247 | }; | 367 | if (wasm.function_exports.get(name)) |index| return index; |
| 368 | if (wasm.hidden_function_exports.get(name)) |index| return index; | ||
| 369 | return null; | ||
| 370 | } | ||
| 248 | 371 | ||
| 249 | pub fn toOptional(i: String) OptionalString { | 372 | pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?FunctionIndex { |
| 250 | const result: OptionalString = @enumFromInt(@intFromEnum(i)); | 373 | const i = wasm.functions.getIndex(resolution) orelse return null; |
| 251 | assert(result != .none); | 374 | return @enumFromInt(i); |
| 252 | return result; | ||
| 253 | } | 375 | } |
| 254 | }; | 376 | }; |
| 255 | 377 | ||
| 256 | pub const OptionalString = enum(u32) { | 378 | pub const GlobalExport = extern struct { |
| 257 | none = std.math.maxInt(u32), | 379 | name: String, |
| 380 | global_index: GlobalIndex, | ||
| 381 | }; | ||
| 382 | |||
| 383 | /// 0. Index into `Flush.function_imports` | ||
| 384 | /// 1. Index into `functions`. | ||
| 385 | /// | ||
| 386 | /// Note that function_imports indexes are subject to swap removals during | ||
| 387 | /// `flush`. | ||
| 388 | pub const OutputFunctionIndex = enum(u32) { | ||
| 258 | _, | 389 | _, |
| 259 | 390 | ||
| 260 | pub fn unwrap(i: OptionalString) ?String { | 391 | pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?OutputFunctionIndex { |
| 261 | if (i == .none) return null; | 392 | return fromFunctionIndex(wasm, FunctionIndex.fromResolution(wasm, resolution) orelse return null); |
| 262 | return @enumFromInt(@intFromEnum(i)); | 393 | } |
| 394 | |||
| 395 | pub fn fromFunctionIndex(wasm: *const Wasm, index: FunctionIndex) OutputFunctionIndex { | ||
| 396 | return @enumFromInt(wasm.flush_buffer.function_imports.entries.len + @intFromEnum(index)); | ||
| 397 | } | ||
| 398 | |||
| 399 | pub fn fromObjectFunction(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex { | ||
| 400 | return fromResolution(wasm, .fromObjectFunction(wasm, index)).?; | ||
| 401 | } | ||
| 402 | |||
| 403 | pub fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex { | ||
| 404 | const ptr = index.ptr(wasm); | ||
| 405 | if (ptr.flags.binding == .weak) { | ||
| 406 | const name = ptr.name.unwrap().?; | ||
| 407 | const import = wasm.object_function_imports.getPtr(name).?; | ||
| 408 | assert(import.resolution != .unresolved); | ||
| 409 | return fromResolution(wasm, import.resolution).?; | ||
| 410 | } | ||
| 411 | return fromResolution(wasm, .fromObjectFunction(wasm, index)).?; | ||
| 412 | } | ||
| 413 | |||
| 414 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) OutputFunctionIndex { | ||
| 415 | const zcu = wasm.base.comp.zcu.?; | ||
| 416 | const ip = &zcu.intern_pool; | ||
| 417 | return switch (ip.indexToKey(ip_index)) { | ||
| 418 | .@"extern" => |ext| { | ||
| 419 | const name = wasm.getExistingString(ext.name.toSlice(ip)).?; | ||
| 420 | return fromSymbolName(wasm, name); | ||
| 421 | }, | ||
| 422 | else => fromResolution(wasm, .fromIpIndex(wasm, ip_index)).?, | ||
| 423 | }; | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) OutputFunctionIndex { | ||
| 427 | const zcu = wasm.base.comp.zcu.?; | ||
| 428 | const ip = &zcu.intern_pool; | ||
| 429 | const nav = ip.getNav(nav_index); | ||
| 430 | return fromIpIndex(wasm, nav.status.fully_resolved.val); | ||
| 431 | } | ||
| 432 | |||
| 433 | pub fn fromTagNameType(wasm: *const Wasm, tag_type: InternPool.Index) OutputFunctionIndex { | ||
| 434 | return fromFunctionIndex(wasm, FunctionIndex.fromTagNameType(wasm, tag_type).?); | ||
| 435 | } | ||
| 436 | |||
| 437 | pub fn fromSymbolName(wasm: *const Wasm, name: String) OutputFunctionIndex { | ||
| 438 | if (wasm.flush_buffer.function_imports.getIndex(name)) |i| return @enumFromInt(i); | ||
| 439 | return fromFunctionIndex(wasm, FunctionIndex.fromSymbolName(wasm, name).?); | ||
| 263 | } | 440 | } |
| 264 | }; | 441 | }; |
| 265 | 442 | ||
| 266 | /// Index into objects array or the zig object. | 443 | /// Index into `Wasm.globals`. |
| 267 | pub const ObjectId = enum(u16) { | 444 | pub const GlobalIndex = enum(u32) { |
| 268 | zig_object = std.math.maxInt(u16) - 1, | ||
| 269 | _, | 445 | _, |
| 270 | 446 | ||
| 271 | pub fn toOptional(i: ObjectId) OptionalObjectId { | 447 | /// This is only accurate when not emitting an object and there is a Zcu. |
| 272 | const result: OptionalObjectId = @enumFromInt(@intFromEnum(i)); | 448 | pub const stack_pointer: GlobalIndex = @enumFromInt(0); |
| 273 | assert(result != .none); | 449 | |
| 274 | return result; | 450 | /// Same as `stack_pointer` but with a safety assertion. |
| 451 | pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index { | ||
| 452 | const comp = wasm.base.comp; | ||
| 453 | assert(comp.config.output_mode != .Obj); | ||
| 454 | assert(comp.zcu != null); | ||
| 455 | return .stack_pointer; | ||
| 456 | } | ||
| 457 | |||
| 458 | pub fn ptr(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution { | ||
| 459 | return &f.globals.items[@intFromEnum(index)]; | ||
| 460 | } | ||
| 461 | |||
| 462 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?GlobalIndex { | ||
| 463 | const i = wasm.globals.getIndex(.fromIpNav(wasm, nav_index)) orelse return null; | ||
| 464 | return @enumFromInt(i); | ||
| 465 | } | ||
| 466 | |||
| 467 | pub fn fromObjectGlobal(wasm: *const Wasm, i: ObjectGlobalIndex) GlobalIndex { | ||
| 468 | return @enumFromInt(wasm.globals.getIndex(.fromObjectGlobal(wasm, i)).?); | ||
| 469 | } | ||
| 470 | |||
| 471 | pub fn fromObjectGlobalHandlingWeak(wasm: *const Wasm, index: ObjectGlobalIndex) GlobalIndex { | ||
| 472 | const global = index.ptr(wasm); | ||
| 473 | return if (global.flags.binding == .weak) | ||
| 474 | fromSymbolName(wasm, global.name.unwrap().?) | ||
| 475 | else | ||
| 476 | fromObjectGlobal(wasm, index); | ||
| 477 | } | ||
| 478 | |||
| 479 | pub fn fromSymbolName(wasm: *const Wasm, name: String) GlobalIndex { | ||
| 480 | const import = wasm.object_global_imports.getPtr(name).?; | ||
| 481 | return @enumFromInt(wasm.globals.getIndex(import.resolution).?); | ||
| 275 | } | 482 | } |
| 276 | }; | 483 | }; |
| 277 | 484 | ||
| 278 | /// Optional index into objects array or the zig object. | 485 | /// Index into `tables`. |
| 279 | pub const OptionalObjectId = enum(u16) { | 486 | pub const TableIndex = enum(u32) { |
| 280 | zig_object = std.math.maxInt(u16) - 1, | ||
| 281 | none = std.math.maxInt(u16), | ||
| 282 | _, | 487 | _, |
| 283 | 488 | ||
| 284 | pub fn unwrap(i: OptionalObjectId) ?ObjectId { | 489 | pub fn ptr(index: TableIndex, f: *const Flush) *Wasm.TableImport.Resolution { |
| 285 | if (i == .none) return null; | 490 | return &f.tables.items[@intFromEnum(index)]; |
| 286 | return @enumFromInt(@intFromEnum(i)); | ||
| 287 | } | 491 | } |
| 288 | }; | ||
| 289 | 492 | ||
| 290 | /// None of this data is serialized since it can be re-loaded from disk, or if | 493 | pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex { |
| 291 | /// it has been changed, the data must be discarded. | 494 | return @enumFromInt(wasm.tables.getIndex(.fromObjectTable(i)).?); |
| 292 | const LazyArchive = struct { | 495 | } |
| 293 | path: Path, | ||
| 294 | file_contents: []const u8, | ||
| 295 | archive: Archive, | ||
| 296 | 496 | ||
| 297 | fn deinit(la: *LazyArchive, gpa: Allocator) void { | 497 | pub fn fromSymbolName(wasm: *const Wasm, name: String) TableIndex { |
| 298 | la.archive.deinit(gpa); | 498 | const import = wasm.object_table_imports.getPtr(name).?; |
| 299 | gpa.free(la.path.sub_path); | 499 | return @enumFromInt(wasm.tables.getIndex(import.resolution).?); |
| 300 | gpa.free(la.file_contents); | ||
| 301 | la.* = undefined; | ||
| 302 | } | 500 | } |
| 303 | }; | 501 | }; |
| 304 | 502 | ||
| 305 | pub const Segment = struct { | 503 | /// The first N indexes correspond to input objects (`objects`) array. |
| 306 | alignment: Alignment, | 504 | /// After that, the indexes correspond to the `source_locations` array, |
| 307 | size: u32, | 505 | /// representing a location in a Zig source file that can be pinpointed |
| 308 | offset: u32, | 506 | /// precisely via AST node and token. |
| 309 | flags: u32, | 507 | pub const SourceLocation = enum(u32) { |
| 508 | /// From the Zig compilation unit but no precise source location. | ||
| 509 | zig_object_nofile = std.math.maxInt(u32) - 1, | ||
| 510 | none = std.math.maxInt(u32), | ||
| 511 | _, | ||
| 310 | 512 | ||
| 311 | const Index = enum(u32) { | 513 | /// Index into `source_locations`. |
| 514 | pub const Index = enum(u32) { | ||
| 312 | _, | 515 | _, |
| 313 | |||
| 314 | pub fn toOptional(i: Index) OptionalIndex { | ||
| 315 | const result: OptionalIndex = @enumFromInt(@intFromEnum(i)); | ||
| 316 | assert(result != .none); | ||
| 317 | return result; | ||
| 318 | } | ||
| 319 | }; | 516 | }; |
| 320 | 517 | ||
| 321 | const OptionalIndex = enum(u32) { | 518 | pub const Unpacked = union(enum) { |
| 322 | none = std.math.maxInt(u32), | 519 | none, |
| 323 | _, | 520 | zig_object_nofile, |
| 324 | 521 | object_index: ObjectIndex, | |
| 325 | pub fn unwrap(i: OptionalIndex) ?Index { | 522 | source_location_index: Index, |
| 326 | if (i == .none) return null; | ||
| 327 | return @enumFromInt(@intFromEnum(i)); | ||
| 328 | } | ||
| 329 | }; | 523 | }; |
| 330 | 524 | ||
| 331 | pub const Flag = enum(u32) { | 525 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) SourceLocation { |
| 332 | WASM_DATA_SEGMENT_IS_PASSIVE = 0x01, | 526 | _ = wasm; |
| 333 | WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02, | 527 | return switch (unpacked) { |
| 334 | }; | 528 | .zig_object_nofile => .zig_object_nofile, |
| 529 | .none => .none, | ||
| 530 | .object_index => |object_index| @enumFromInt(@intFromEnum(object_index)), | ||
| 531 | .source_location_index => @panic("TODO"), | ||
| 532 | }; | ||
| 533 | } | ||
| 534 | |||
| 535 | pub fn unpack(sl: SourceLocation, wasm: *const Wasm) Unpacked { | ||
| 536 | return switch (sl) { | ||
| 537 | .zig_object_nofile => .zig_object_nofile, | ||
| 538 | .none => .none, | ||
| 539 | _ => { | ||
| 540 | const i = @intFromEnum(sl); | ||
| 541 | if (i < wasm.objects.items.len) return .{ .object_index = @enumFromInt(i) }; | ||
| 542 | const sl_index = i - wasm.objects.items.len; | ||
| 543 | _ = sl_index; | ||
| 544 | @panic("TODO"); | ||
| 545 | }, | ||
| 546 | }; | ||
| 547 | } | ||
| 335 | 548 | ||
| 336 | pub fn isPassive(segment: Segment) bool { | 549 | pub fn fromObject(object_index: ObjectIndex, wasm: *const Wasm) SourceLocation { |
| 337 | return segment.flags & @intFromEnum(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0; | 550 | return pack(.{ .object_index = object_index }, wasm); |
| 338 | } | 551 | } |
| 339 | 552 | ||
| 340 | /// For a given segment, determines if it needs passive initialization | 553 | pub fn addError(sl: SourceLocation, wasm: *Wasm, comptime f: []const u8, args: anytype) void { |
| 341 | fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool { | 554 | const diags = &wasm.base.comp.link_diags; |
| 342 | if (import_mem and !std.mem.eql(u8, name, ".bss")) { | 555 | switch (sl.unpack(wasm)) { |
| 343 | return true; | 556 | .none => unreachable, |
| 557 | .zig_object_nofile => diags.addError("zig compilation unit: " ++ f, args), | ||
| 558 | .object_index => |i| diags.addError("{}: " ++ f, .{i.ptr(wasm).path} ++ args), | ||
| 559 | .source_location_index => @panic("TODO"), | ||
| 344 | } | 560 | } |
| 345 | return segment.isPassive(); | ||
| 346 | } | 561 | } |
| 347 | }; | ||
| 348 | 562 | ||
| 349 | pub const SymbolLoc = struct { | 563 | pub fn addNote( |
| 350 | /// The index of the symbol within the specified file | 564 | sl: SourceLocation, |
| 351 | index: Symbol.Index, | 565 | err: *link.Diags.ErrorWithNotes, |
| 352 | /// The index of the object file where the symbol resides. | 566 | comptime f: []const u8, |
| 353 | file: OptionalObjectId, | 567 | args: anytype, |
| 354 | }; | 568 | ) void { |
| 569 | err.addNote(f, args); | ||
| 570 | const err_msg = &err.diags.msgs.items[err.index]; | ||
| 571 | err_msg.notes[err.note_slot - 1].source_location = .{ .wasm = sl }; | ||
| 572 | } | ||
| 355 | 573 | ||
| 356 | /// From a given location, returns the corresponding symbol in the wasm binary | 574 | pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{LinkFailure} { |
| 357 | pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol { | 575 | return diags.failSourceLocation(.{ .wasm = sl }, format, args); |
| 358 | if (wasm.discarded.get(loc)) |new_loc| { | ||
| 359 | return symbolLocSymbol(wasm, new_loc); | ||
| 360 | } | 576 | } |
| 361 | return switch (loc.file) { | 577 | |
| 362 | .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)], | 578 | pub fn string( |
| 363 | .zig_object => wasm.zig_object.?.symbol(loc.index), | 579 | sl: SourceLocation, |
| 364 | _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)], | 580 | msg: []const u8, |
| 581 | bundle: *std.zig.ErrorBundle.Wip, | ||
| 582 | wasm: *const Wasm, | ||
| 583 | ) Allocator.Error!std.zig.ErrorBundle.String { | ||
| 584 | return switch (sl.unpack(wasm)) { | ||
| 585 | .none => try bundle.addString(msg), | ||
| 586 | .zig_object_nofile => try bundle.printString("zig compilation unit: {s}", .{msg}), | ||
| 587 | .object_index => |i| { | ||
| 588 | const obj = i.ptr(wasm); | ||
| 589 | return if (obj.archive_member_name.slice(wasm)) |obj_name| | ||
| 590 | try bundle.printString("{} ({s}): {s}", .{ obj.path, std.fs.path.basename(obj_name), msg }) | ||
| 591 | else | ||
| 592 | try bundle.printString("{}: {s}", .{ obj.path, msg }); | ||
| 593 | }, | ||
| 594 | .source_location_index => @panic("TODO"), | ||
| 595 | }; | ||
| 596 | } | ||
| 597 | }; | ||
| 598 | |||
| 599 | /// The lower bits of this ABI-match the flags here: | ||
| 600 | /// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection | ||
| 601 | /// The upper bits are used for nefarious purposes. | ||
| 602 | pub const SymbolFlags = packed struct(u32) { | ||
| 603 | binding: Binding = .strong, | ||
| 604 | /// Indicating that this is a hidden symbol. Hidden symbols are not to be | ||
| 605 | /// exported when performing the final link, but may be linked to other | ||
| 606 | /// modules. | ||
| 607 | visibility_hidden: bool = false, | ||
| 608 | padding0: u1 = 0, | ||
| 609 | /// For non-data symbols, this must match whether the symbol is an import | ||
| 610 | /// or is defined; for data symbols, determines whether a segment is | ||
| 611 | /// specified. | ||
| 612 | undefined: bool = false, | ||
| 613 | /// The symbol is intended to be exported from the wasm module to the host | ||
| 614 | /// environment. This differs from the visibility flags in that it affects | ||
| 615 | /// static linking. | ||
| 616 | exported: bool = false, | ||
| 617 | /// The symbol uses an explicit symbol name, rather than reusing the name | ||
| 618 | /// from a wasm import. This allows it to remap imports from foreign | ||
| 619 | /// WebAssembly modules into local symbols with different names. | ||
| 620 | explicit_name: bool = false, | ||
| 621 | /// The symbol is intended to be included in the linker output, regardless | ||
| 622 | /// of whether it is used by the program. Same meaning as `retain`. | ||
| 623 | no_strip: bool = false, | ||
| 624 | /// The symbol resides in thread local storage. | ||
| 625 | tls: bool = false, | ||
| 626 | /// The symbol represents an absolute address. This means its offset is | ||
| 627 | /// relative to the start of the wasm memory as opposed to being relative | ||
| 628 | /// to a data segment. | ||
| 629 | absolute: bool = false, | ||
| 630 | |||
| 631 | // Above here matches the tooling conventions ABI. | ||
| 632 | |||
| 633 | padding1: u13 = 0, | ||
| 634 | /// Zig-specific. Dead things are allowed to be garbage collected. | ||
| 635 | alive: bool = false, | ||
| 636 | /// Zig-specific. This symbol comes from an object that must be included in | ||
| 637 | /// the final link. | ||
| 638 | must_link: bool = false, | ||
| 639 | /// Zig-specific. | ||
| 640 | global_type: GlobalType4 = .zero, | ||
| 641 | /// Zig-specific. | ||
| 642 | limits_has_max: bool = false, | ||
| 643 | /// Zig-specific. | ||
| 644 | limits_is_shared: bool = false, | ||
| 645 | /// Zig-specific. | ||
| 646 | ref_type: RefType1 = .funcref, | ||
| 647 | |||
| 648 | pub const Binding = enum(u2) { | ||
| 649 | strong = 0, | ||
| 650 | /// Indicating that this is a weak symbol. When linking multiple modules | ||
| 651 | /// defining the same symbol, all weak definitions are discarded if any | ||
| 652 | /// strong definitions exist; then if multiple weak definitions exist all | ||
| 653 | /// but one (unspecified) are discarded; and finally it is an error if more | ||
| 654 | /// than one definition remains. | ||
| 655 | weak = 1, | ||
| 656 | /// Indicating that this is a local symbol. Local symbols are not to be | ||
| 657 | /// exported, or linked to other modules/sections. The names of all | ||
| 658 | /// non-local symbols must be unique, but the names of local symbols | ||
| 659 | /// are not considered for uniqueness. A local function or global | ||
| 660 | /// symbol cannot reference an import. | ||
| 661 | local = 2, | ||
| 365 | }; | 662 | }; |
| 366 | } | ||
| 367 | 663 | ||
| 368 | /// From a given location, returns the name of the symbol. | 664 | pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void { |
| 369 | pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) [:0]const u8 { | 665 | flags.no_strip = no_strip; |
| 370 | return wasm.stringSlice(wasm.symbolLocSymbol(loc).name); | 666 | flags.alive = false; |
| 371 | } | 667 | flags.must_link = must_link; |
| 668 | flags.global_type = .zero; | ||
| 669 | flags.limits_has_max = false; | ||
| 670 | flags.limits_is_shared = false; | ||
| 671 | flags.ref_type = .funcref; | ||
| 672 | } | ||
| 372 | 673 | ||
| 373 | /// From a given symbol location, returns the final location. | 674 | pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool) bool { |
| 374 | /// e.g. when a symbol was resolved and replaced by the symbol | 675 | return flags.exported or |
| 375 | /// in a different file, this will return said location. | 676 | (is_dynamic and !flags.visibility_hidden) or |
| 376 | /// If the symbol wasn't replaced by another, this will return | 677 | (flags.no_strip and flags.must_link); |
| 377 | /// the given location itwasm. | ||
| 378 | pub fn symbolLocFinalLoc(wasm: *const Wasm, loc: SymbolLoc) SymbolLoc { | ||
| 379 | if (wasm.discarded.get(loc)) |new_loc| { | ||
| 380 | return symbolLocFinalLoc(wasm, new_loc); | ||
| 381 | } | 678 | } |
| 382 | return loc; | ||
| 383 | } | ||
| 384 | 679 | ||
| 385 | // Contains the location of the function symbol, as well as | 680 | pub fn isExported(flags: SymbolFlags, is_dynamic: bool) bool { |
| 386 | /// the priority itself of the initialization function. | 681 | if (flags.undefined or flags.binding == .local) return false; |
| 387 | pub const InitFuncLoc = struct { | 682 | if (is_dynamic and !flags.visibility_hidden) return true; |
| 388 | /// object file index in the list of objects. | 683 | return flags.exported; |
| 389 | /// Unlike `SymbolLoc` this cannot be `null` as we never define | 684 | } |
| 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, | ||
| 396 | 685 | ||
| 397 | /// From a given `InitFuncLoc` returns the corresponding function symbol | 686 | /// Returns the name as how it will be output into the final object |
| 398 | fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol { | 687 | /// file or binary. When `merge` is true, this will return the |
| 399 | return wasm.symbolLocSymbol(getSymbolLoc(loc)); | 688 | /// short name. i.e. ".rodata". When false, it returns the entire name instead. |
| 689 | pub fn outputName(flags: SymbolFlags, name: []const u8, merge: bool) []const u8 { | ||
| 690 | if (flags.tls) return ".tdata"; | ||
| 691 | if (!merge) return name; | ||
| 692 | if (mem.startsWith(u8, name, ".rodata.")) return ".rodata"; | ||
| 693 | if (mem.startsWith(u8, name, ".text.")) return ".text"; | ||
| 694 | if (mem.startsWith(u8, name, ".data.")) return ".data"; | ||
| 695 | if (mem.startsWith(u8, name, ".bss.")) return ".bss"; | ||
| 696 | return name; | ||
| 697 | } | ||
| 698 | |||
| 699 | /// Masks off the Zig-specific stuff. | ||
| 700 | pub fn toAbiInteger(flags: SymbolFlags) u32 { | ||
| 701 | var copy = flags; | ||
| 702 | copy.initZigSpecific(false, false); | ||
| 703 | return @bitCast(copy); | ||
| 400 | } | 704 | } |
| 705 | }; | ||
| 401 | 706 | ||
| 402 | /// Turns the given `InitFuncLoc` into a `SymbolLoc` | 707 | pub const GlobalType4 = packed struct(u4) { |
| 403 | fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc { | 708 | valtype: Valtype3, |
| 709 | mutable: bool, | ||
| 710 | |||
| 711 | pub const zero: GlobalType4 = @bitCast(@as(u4, 0)); | ||
| 712 | |||
| 713 | pub fn to(gt: GlobalType4) ObjectGlobal.Type { | ||
| 404 | return .{ | 714 | return .{ |
| 405 | .file = loc.file.toOptional(), | 715 | .valtype = gt.valtype.to(), |
| 406 | .index = loc.index, | 716 | .mutable = gt.mutable, |
| 407 | }; | 717 | }; |
| 408 | } | 718 | } |
| 719 | }; | ||
| 409 | 720 | ||
| 410 | /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`. | 721 | pub const Valtype3 = enum(u3) { |
| 411 | fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool { | 722 | i32, |
| 412 | _ = ctx; | 723 | i64, |
| 413 | return lhs.priority < rhs.priority; | 724 | f32, |
| 725 | f64, | ||
| 726 | v128, | ||
| 727 | |||
| 728 | pub fn from(v: std.wasm.Valtype) Valtype3 { | ||
| 729 | return switch (v) { | ||
| 730 | .i32 => .i32, | ||
| 731 | .i64 => .i64, | ||
| 732 | .f32 => .f32, | ||
| 733 | .f64 => .f64, | ||
| 734 | .v128 => .v128, | ||
| 735 | }; | ||
| 736 | } | ||
| 737 | |||
| 738 | pub fn to(v: Valtype3) std.wasm.Valtype { | ||
| 739 | return switch (v) { | ||
| 740 | .i32 => .i32, | ||
| 741 | .i64 => .i64, | ||
| 742 | .f32 => .f32, | ||
| 743 | .f64 => .f64, | ||
| 744 | .v128 => .v128, | ||
| 745 | }; | ||
| 414 | } | 746 | } |
| 415 | }; | 747 | }; |
| 416 | 748 | ||
| 417 | pub fn open( | 749 | /// Index into `Wasm.navs_obj`. |
| 418 | arena: Allocator, | 750 | pub const NavsObjIndex = enum(u32) { |
| 419 | comp: *Compilation, | 751 | _, |
| 420 | emit: Path, | ||
| 421 | options: link.File.OpenOptions, | ||
| 422 | ) !*Wasm { | ||
| 423 | // TODO: restore saved linker state, don't truncate the file, and | ||
| 424 | // participate in incremental compilation. | ||
| 425 | return createEmpty(arena, comp, emit, options); | ||
| 426 | } | ||
| 427 | 752 | ||
| 428 | pub fn createEmpty( | 753 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index { |
| 429 | arena: Allocator, | 754 | return &wasm.navs_obj.keys()[@intFromEnum(i)]; |
| 430 | comp: *Compilation, | 755 | } |
| 431 | emit: Path, | ||
| 432 | options: link.File.OpenOptions, | ||
| 433 | ) !*Wasm { | ||
| 434 | const gpa = comp.gpa; | ||
| 435 | const target = comp.root_mod.resolved_target.result; | ||
| 436 | assert(target.ofmt == .wasm); | ||
| 437 | 756 | ||
| 438 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 757 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj { |
| 439 | const use_llvm = comp.config.use_llvm; | 758 | return &wasm.navs_obj.values()[@intFromEnum(i)]; |
| 440 | const output_mode = comp.config.output_mode; | 759 | } |
| 441 | const shared_memory = comp.config.shared_memory; | ||
| 442 | const wasi_exec_model = comp.config.wasi_exec_model; | ||
| 443 | 760 | ||
| 444 | // If using LLD to link, this code should produce an object file so that it | 761 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { |
| 445 | // can be passed to LLD. | 762 | const zcu = wasm.base.comp.zcu.?; |
| 446 | // If using LLVM to generate the object file for the zig compilation unit, | 763 | const ip = &zcu.intern_pool; |
| 447 | // we need a place to put the object file so that it can be subsequently | 764 | const nav = ip.getNav(i.key(wasm).*); |
| 448 | // handled. | 765 | return nav.fqn.toSlice(ip); |
| 449 | const zcu_object_sub_path = if (!use_lld and !use_llvm) | 766 | } |
| 450 | null | 767 | }; |
| 451 | else | ||
| 452 | try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); | ||
| 453 | 768 | ||
| 454 | const wasm = try arena.create(Wasm); | 769 | /// Index into `Wasm.navs_exe`. |
| 455 | wasm.* = .{ | 770 | pub const NavsExeIndex = enum(u32) { |
| 456 | .base = .{ | 771 | _, |
| 457 | .tag = .wasm, | ||
| 458 | .comp = comp, | ||
| 459 | .emit = emit, | ||
| 460 | .zcu_object_sub_path = zcu_object_sub_path, | ||
| 461 | .gc_sections = options.gc_sections orelse (output_mode != .Obj), | ||
| 462 | .print_gc_sections = options.print_gc_sections, | ||
| 463 | .stack_size = options.stack_size orelse switch (target.os.tag) { | ||
| 464 | .freestanding => 1 * 1024 * 1024, // 1 MiB | ||
| 465 | else => 16 * 1024 * 1024, // 16 MiB | ||
| 466 | }, | ||
| 467 | .allow_shlib_undefined = options.allow_shlib_undefined orelse false, | ||
| 468 | .file = null, | ||
| 469 | .disable_lld_caching = options.disable_lld_caching, | ||
| 470 | .build_id = options.build_id, | ||
| 471 | }, | ||
| 472 | .name = undefined, | ||
| 473 | .string_table = .empty, | ||
| 474 | .string_bytes = .empty, | ||
| 475 | .import_table = options.import_table, | ||
| 476 | .export_table = options.export_table, | ||
| 477 | .import_symbols = options.import_symbols, | ||
| 478 | .export_symbol_names = options.export_symbol_names, | ||
| 479 | .global_base = options.global_base, | ||
| 480 | .initial_memory = options.initial_memory, | ||
| 481 | .max_memory = options.max_memory, | ||
| 482 | 772 | ||
| 483 | .entry_name = undefined, | 773 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index { |
| 484 | .zig_object = null, | 774 | return &wasm.navs_exe.keys()[@intFromEnum(i)]; |
| 485 | .dump_argv_list = .empty, | ||
| 486 | .host_name = undefined, | ||
| 487 | .custom_sections = undefined, | ||
| 488 | .preloaded_strings = undefined, | ||
| 489 | }; | ||
| 490 | if (use_llvm and comp.config.have_zcu) { | ||
| 491 | wasm.llvm_object = try LlvmObject.create(arena, comp); | ||
| 492 | } | 775 | } |
| 493 | errdefer wasm.base.destroy(); | ||
| 494 | 776 | ||
| 495 | wasm.host_name = try wasm.internString("env"); | 777 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe { |
| 496 | 778 | return &wasm.navs_exe.values()[@intFromEnum(i)]; | |
| 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 | } | 779 | } |
| 503 | 780 | ||
| 504 | inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| { | 781 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { |
| 505 | @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name); | 782 | const zcu = wasm.base.comp.zcu.?; |
| 783 | const ip = &zcu.intern_pool; | ||
| 784 | const nav = ip.getNav(i.key(wasm).*); | ||
| 785 | return nav.fqn.toSlice(ip); | ||
| 506 | } | 786 | } |
| 787 | }; | ||
| 507 | 788 | ||
| 508 | wasm.entry_name = switch (options.entry) { | 789 | /// Index into `Wasm.uavs_obj`. |
| 509 | .disabled => .none, | 790 | pub const UavsObjIndex = enum(u32) { |
| 510 | .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), | 791 | _, |
| 511 | .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), | ||
| 512 | .named => |name| (try wasm.internString(name)).toOptional(), | ||
| 513 | }; | ||
| 514 | 792 | ||
| 515 | if (use_lld and (use_llvm or !comp.config.have_zcu)) { | 793 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { |
| 516 | // LLVM emits the object file (if any); LLD links it into the final product. | 794 | return &wasm.uavs_obj.keys()[@intFromEnum(i)]; |
| 517 | return wasm; | ||
| 518 | } | 795 | } |
| 519 | 796 | ||
| 520 | // What path should this Wasm linker code output to? | 797 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj { |
| 521 | // If using LLD to link, this code should produce an object file so that it | 798 | return &wasm.uavs_obj.values()[@intFromEnum(i)]; |
| 522 | // can be passed to LLD. | 799 | } |
| 523 | const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; | 800 | }; |
| 524 | 801 | ||
| 525 | wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{ | 802 | /// Index into `Wasm.uavs_exe`. |
| 526 | .truncate = true, | 803 | pub const UavsExeIndex = enum(u32) { |
| 527 | .read = true, | 804 | _, |
| 528 | .mode = if (fs.has_executable_bit) | ||
| 529 | if (target.os.tag == .wasi and output_mode == .Exe) | ||
| 530 | fs.File.default_mode | 0b001_000_000 | ||
| 531 | else | ||
| 532 | fs.File.default_mode | ||
| 533 | else | ||
| 534 | 0, | ||
| 535 | }); | ||
| 536 | wasm.name = sub_path; | ||
| 537 | 805 | ||
| 538 | // create stack pointer symbol | 806 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { |
| 539 | { | 807 | return &wasm.uavs_exe.keys()[@intFromEnum(i)]; |
| 540 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__stack_pointer, .global); | 808 | } |
| 541 | const symbol = wasm.symbolLocSymbol(loc); | 809 | |
| 542 | // For object files we will import the stack pointer symbol | 810 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe { |
| 543 | if (output_mode == .Obj) { | 811 | return &wasm.uavs_exe.values()[@intFromEnum(i)]; |
| 544 | symbol.setUndefined(true); | 812 | } |
| 545 | symbol.index = @intCast(wasm.imported_globals_count); | 813 | }; |
| 546 | wasm.imported_globals_count += 1; | 814 | |
| 547 | try wasm.imports.putNoClobber(gpa, loc, .{ | 815 | /// Used when emitting a relocatable object. |
| 548 | .module_name = wasm.host_name, | 816 | pub const ZcuDataObj = extern struct { |
| 549 | .name = symbol.name, | 817 | code: DataPayload, |
| 550 | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, | 818 | relocs: OutReloc.Slice, |
| 551 | }); | 819 | }; |
| 552 | } else { | 820 | |
| 553 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | 821 | /// Used when not emitting a relocatable object. |
| 554 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 822 | pub const ZcuDataExe = extern struct { |
| 555 | const global = try wasm.wasm_globals.addOne(gpa); | 823 | code: DataPayload, |
| 556 | global.* = .{ | 824 | /// Tracks how many references there are for the purposes of sorting data segments. |
| 557 | .global_type = .{ | 825 | count: u32, |
| 558 | .valtype = .i32, | 826 | }; |
| 559 | .mutable = true, | 827 | |
| 560 | }, | 828 | /// An abstraction for calling `lowerZcuData` repeatedly until all data entries |
| 561 | .init = .{ .i32_const = 0 }, | 829 | /// are populated. |
| 562 | }; | 830 | const ZcuDataStarts = struct { |
| 563 | } | 831 | uavs_i: u32, |
| 832 | |||
| 833 | fn init(wasm: *const Wasm) ZcuDataStarts { | ||
| 834 | const comp = wasm.base.comp; | ||
| 835 | const is_obj = comp.config.output_mode == .Obj; | ||
| 836 | return if (is_obj) initObj(wasm) else initExe(wasm); | ||
| 564 | } | 837 | } |
| 565 | 838 | ||
| 566 | // create indirect function pointer symbol | 839 | fn initObj(wasm: *const Wasm) ZcuDataStarts { |
| 567 | { | 840 | return .{ |
| 568 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__indirect_function_table, .table); | 841 | .uavs_i = @intCast(wasm.uavs_obj.entries.len), |
| 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 | }; | 842 | }; |
| 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 | } | 843 | } |
| 593 | 844 | ||
| 594 | // create __wasm_call_ctors | 845 | fn initExe(wasm: *const Wasm) ZcuDataStarts { |
| 595 | { | 846 | return .{ |
| 596 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_call_ctors, .function); | 847 | .uavs_i = @intCast(wasm.uavs_exe.entries.len), |
| 597 | const symbol = wasm.symbolLocSymbol(loc); | 848 | }; |
| 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 | } | 849 | } |
| 603 | 850 | ||
| 604 | // shared-memory symbols for TLS support | 851 | fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { |
| 605 | if (shared_memory) { | 852 | const comp = wasm.base.comp; |
| 606 | { | 853 | const is_obj = comp.config.output_mode == .Obj; |
| 607 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_base, .global); | 854 | return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt); |
| 608 | const symbol = wasm.symbolLocSymbol(loc); | 855 | } |
| 609 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 856 | |
| 610 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | 857 | fn finishObj(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { |
| 611 | symbol.mark(); | 858 | var uavs_i = zds.uavs_i; |
| 612 | try wasm.wasm_globals.append(gpa, .{ | 859 | while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) { |
| 613 | .global_type = .{ .valtype = .i32, .mutable = true }, | 860 | // Call to `lowerZcuData` here possibly creates more entries in these tables. |
| 614 | .init = .{ .i32_const = undefined }, | 861 | wasm.uavs_obj.values()[uavs_i] = try lowerZcuData(wasm, pt, wasm.uavs_obj.keys()[uavs_i]); |
| 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 | } | 862 | } |
| 644 | } | 863 | } |
| 645 | 864 | ||
| 646 | if (comp.zcu) |zcu| { | 865 | fn finishExe(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { |
| 647 | if (!use_llvm) { | 866 | var uavs_i = zds.uavs_i; |
| 648 | const zig_object = try arena.create(ZigObject); | 867 | while (uavs_i < wasm.uavs_exe.entries.len) : (uavs_i += 1) { |
| 649 | wasm.zig_object = zig_object; | 868 | // Call to `lowerZcuData` here possibly creates more entries in these tables. |
| 650 | zig_object.* = .{ | 869 | const zcu_data = try lowerZcuData(wasm, pt, wasm.uavs_exe.keys()[uavs_i]); |
| 651 | .path = .{ | 870 | wasm.uavs_exe.values()[uavs_i].code = zcu_data.code; |
| 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 | } | 871 | } |
| 659 | } | 872 | } |
| 873 | }; | ||
| 660 | 874 | ||
| 661 | return wasm; | 875 | pub const ZcuFunc = union { |
| 662 | } | 876 | function: CodeGen.Function, |
| 877 | tag_name: TagName, | ||
| 663 | 878 | ||
| 664 | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { | 879 | pub const TagName = extern struct { |
| 665 | var index: u32 = 0; | 880 | symbol_name: String, |
| 666 | while (index < wasm.func_types.items.len) : (index += 1) { | 881 | type_index: FunctionType.Index, |
| 667 | if (wasm.func_types.items[index].eql(func_type)) return index; | 882 | /// Index into `Wasm.tag_name_offs`. |
| 668 | } | 883 | table_index: u32, |
| 669 | return null; | 884 | }; |
| 670 | } | ||
| 671 | 885 | ||
| 672 | /// Either creates a new import, or updates one if existing. | 886 | /// Index into `Wasm.zcu_funcs`. |
| 673 | /// When `type_index` is non-null, we assume an external function. | 887 | /// Note that swapRemove is sometimes performed on `zcu_funcs`. |
| 674 | /// In all other cases, a data-symbol will be created instead. | 888 | pub const Index = enum(u32) { |
| 675 | pub fn addOrUpdateImport( | 889 | _, |
| 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 | 890 | ||
| 691 | /// For a given name, creates a new global synthetic symbol. | 891 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { |
| 692 | /// Leaves index undefined and the default flags (0). | 892 | return &wasm.zcu_funcs.keys()[@intFromEnum(i)]; |
| 693 | fn createSyntheticSymbol(wasm: *Wasm, name: String, tag: Symbol.Tag) !SymbolLoc { | 893 | } |
| 694 | return wasm.createSyntheticSymbolOffset(name, tag); | ||
| 695 | } | ||
| 696 | 894 | ||
| 697 | fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: String, tag: Symbol.Tag) !SymbolLoc { | 895 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc { |
| 698 | const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len); | 896 | return &wasm.zcu_funcs.values()[@intFromEnum(i)]; |
| 699 | const loc: SymbolLoc = .{ .index = sym_index, .file = .none }; | 897 | } |
| 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 | 898 | ||
| 713 | fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void { | 899 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { |
| 714 | const diags = &wasm.base.comp.link_diags; | 900 | const zcu = wasm.base.comp.zcu.?; |
| 715 | const obj = link.openObject(path, false, false) catch |err| { | 901 | const ip = &zcu.intern_pool; |
| 716 | switch (diags.failParse(path, "failed to open object: {s}", .{@errorName(err)})) { | 902 | const ip_index = i.key(wasm).*; |
| 717 | error.LinkFailure => return, | 903 | switch (ip.indexToKey(ip_index)) { |
| 904 | .func => |func| { | ||
| 905 | const nav = ip.getNav(func.owner_nav); | ||
| 906 | return nav.fqn.toSlice(ip); | ||
| 907 | }, | ||
| 908 | .enum_type => { | ||
| 909 | return i.value(wasm).tag_name.symbol_name.slice(wasm); | ||
| 910 | }, | ||
| 911 | else => unreachable, | ||
| 912 | } | ||
| 718 | } | 913 | } |
| 719 | }; | 914 | |
| 720 | wasm.parseObject(obj) catch |err| { | 915 | pub fn typeIndex(i: @This(), wasm: *Wasm) FunctionType.Index { |
| 721 | switch (diags.failParse(path, "failed to parse object: {s}", .{@errorName(err)})) { | 916 | const comp = wasm.base.comp; |
| 722 | error.LinkFailure => return, | 917 | const zcu = comp.zcu.?; |
| 918 | const target = &comp.root_mod.resolved_target.result; | ||
| 919 | const ip = &zcu.intern_pool; | ||
| 920 | switch (ip.indexToKey(i.key(wasm).*)) { | ||
| 921 | .func => |func| { | ||
| 922 | const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu); | ||
| 923 | const fn_info = zcu.typeToFunc(fn_ty).?; | ||
| 924 | return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?; | ||
| 925 | }, | ||
| 926 | .enum_type => { | ||
| 927 | return i.value(wasm).tag_name.type_index; | ||
| 928 | }, | ||
| 929 | else => unreachable, | ||
| 930 | } | ||
| 723 | } | 931 | } |
| 724 | }; | 932 | }; |
| 725 | } | 933 | }; |
| 726 | |||
| 727 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { | ||
| 728 | defer obj.file.close(); | ||
| 729 | const gpa = wasm.base.comp.gpa; | ||
| 730 | try wasm.objects.ensureUnusedCapacity(gpa, 1); | ||
| 731 | const stat = try obj.file.stat(); | ||
| 732 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; | ||
| 733 | 934 | ||
| 734 | const file_contents = try gpa.alloc(u8, size); | 935 | pub const NavExport = extern struct { |
| 735 | defer gpa.free(file_contents); | 936 | name: String, |
| 937 | nav_index: InternPool.Nav.Index, | ||
| 938 | }; | ||
| 736 | 939 | ||
| 737 | const n = try obj.file.preadAll(file_contents, 0); | 940 | pub const UavExport = extern struct { |
| 738 | if (n != file_contents.len) return error.UnexpectedEndOfFile; | 941 | name: String, |
| 942 | uav_index: InternPool.Index, | ||
| 943 | }; | ||
| 739 | 944 | ||
| 740 | wasm.objects.appendAssumeCapacity(try Object.create(wasm, file_contents, obj.path, null)); | 945 | pub const FunctionImport = extern struct { |
| 741 | } | 946 | flags: SymbolFlags, |
| 947 | module_name: OptionalString, | ||
| 948 | /// May be different than the key which is a symbol name. | ||
| 949 | name: String, | ||
| 950 | source_location: SourceLocation, | ||
| 951 | resolution: Resolution, | ||
| 952 | type: FunctionType.Index, | ||
| 953 | |||
| 954 | /// Represents a synthetic function, a function from an object, or a | ||
| 955 | /// function from the Zcu. | ||
| 956 | pub const Resolution = enum(u32) { | ||
| 957 | unresolved, | ||
| 958 | __wasm_apply_global_tls_relocs, | ||
| 959 | __wasm_call_ctors, | ||
| 960 | __wasm_init_memory, | ||
| 961 | __wasm_init_tls, | ||
| 962 | // Next, index into `object_functions`. | ||
| 963 | // Next, index into `zcu_funcs`. | ||
| 964 | _, | ||
| 742 | 965 | ||
| 743 | /// Creates a new empty `Atom` and returns its `Atom.Index` | 966 | const first_object_function = @intFromEnum(Resolution.__wasm_init_tls) + 1; |
| 744 | pub 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 | 967 | ||
| 754 | return index; | 968 | pub const Unpacked = union(enum) { |
| 755 | } | 969 | unresolved, |
| 970 | __wasm_apply_global_tls_relocs, | ||
| 971 | __wasm_call_ctors, | ||
| 972 | __wasm_init_memory, | ||
| 973 | __wasm_init_tls, | ||
| 974 | object_function: ObjectFunctionIndex, | ||
| 975 | zcu_func: ZcuFunc.Index, | ||
| 976 | }; | ||
| 756 | 977 | ||
| 757 | pub fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom { | 978 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { |
| 758 | return wasm.managed_atoms.items[@intFromEnum(index)]; | 979 | return switch (r) { |
| 759 | } | 980 | .unresolved => .unresolved, |
| 981 | .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs, | ||
| 982 | .__wasm_call_ctors => .__wasm_call_ctors, | ||
| 983 | .__wasm_init_memory => .__wasm_init_memory, | ||
| 984 | .__wasm_init_tls => .__wasm_init_tls, | ||
| 985 | _ => { | ||
| 986 | const object_function_index = @intFromEnum(r) - first_object_function; | ||
| 760 | 987 | ||
| 761 | pub fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { | 988 | const zcu_func_index = if (object_function_index < wasm.object_functions.items.len) |
| 762 | return &wasm.managed_atoms.items[@intFromEnum(index)]; | 989 | return .{ .object_function = @enumFromInt(object_function_index) } |
| 763 | } | 990 | else |
| 991 | object_function_index - wasm.object_functions.items.len; | ||
| 764 | 992 | ||
| 765 | fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void { | 993 | return .{ .zcu_func = @enumFromInt(zcu_func_index) }; |
| 766 | const gpa = wasm.base.comp.gpa; | 994 | }, |
| 995 | }; | ||
| 996 | } | ||
| 767 | 997 | ||
| 768 | defer obj.file.close(); | 998 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { |
| 999 | return switch (unpacked) { | ||
| 1000 | .unresolved => .unresolved, | ||
| 1001 | .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs, | ||
| 1002 | .__wasm_call_ctors => .__wasm_call_ctors, | ||
| 1003 | .__wasm_init_memory => .__wasm_init_memory, | ||
| 1004 | .__wasm_init_tls => .__wasm_init_tls, | ||
| 1005 | .object_function => |i| @enumFromInt(first_object_function + @intFromEnum(i)), | ||
| 1006 | .zcu_func => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)), | ||
| 1007 | }; | ||
| 1008 | } | ||
| 769 | 1009 | ||
| 770 | const stat = try obj.file.stat(); | 1010 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution { |
| 771 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; | 1011 | const zcu = wasm.base.comp.zcu.?; |
| 1012 | const ip = &zcu.intern_pool; | ||
| 1013 | return fromIpIndex(wasm, ip.getNav(nav_index).status.fully_resolved.val); | ||
| 1014 | } | ||
| 772 | 1015 | ||
| 773 | const file_contents = try gpa.alloc(u8, size); | 1016 | pub fn fromZcuFunc(wasm: *const Wasm, i: ZcuFunc.Index) Resolution { |
| 774 | var keep_file_contents = false; | 1017 | return pack(wasm, .{ .zcu_func = i }); |
| 775 | defer if (!keep_file_contents) gpa.free(file_contents); | 1018 | } |
| 776 | 1019 | ||
| 777 | const n = try obj.file.preadAll(file_contents, 0); | 1020 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { |
| 778 | if (n != file_contents.len) return error.UnexpectedEndOfFile; | 1021 | return fromZcuFunc(wasm, @enumFromInt(wasm.zcu_funcs.getIndex(ip_index).?)); |
| 1022 | } | ||
| 779 | 1023 | ||
| 780 | var archive = try Archive.parse(gpa, file_contents); | 1024 | pub fn fromObjectFunction(wasm: *const Wasm, object_function: ObjectFunctionIndex) Resolution { |
| 1025 | return pack(wasm, .{ .object_function = object_function }); | ||
| 1026 | } | ||
| 781 | 1027 | ||
| 782 | if (!obj.must_link) { | 1028 | pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool { |
| 783 | errdefer archive.deinit(gpa); | 1029 | return switch (r.unpack(wasm)) { |
| 784 | try wasm.lazy_archives.append(gpa, .{ | 1030 | .unresolved, .zcu_func => true, |
| 785 | .path = .{ | 1031 | else => false, |
| 786 | .root_dir = obj.path.root_dir, | 1032 | }; |
| 787 | .sub_path = try gpa.dupe(u8, obj.path.sub_path), | 1033 | } |
| 788 | }, | ||
| 789 | .file_contents = file_contents, | ||
| 790 | .archive = archive, | ||
| 791 | }); | ||
| 792 | keep_file_contents = true; | ||
| 793 | return; | ||
| 794 | } | ||
| 795 | 1034 | ||
| 796 | defer archive.deinit(gpa); | 1035 | pub fn typeIndex(r: Resolution, wasm: *Wasm) FunctionType.Index { |
| 1036 | return switch (unpack(r, wasm)) { | ||
| 1037 | .unresolved => unreachable, | ||
| 1038 | .__wasm_apply_global_tls_relocs, | ||
| 1039 | .__wasm_call_ctors, | ||
| 1040 | .__wasm_init_memory, | ||
| 1041 | => getExistingFuncType2(wasm, &.{}, &.{}), | ||
| 1042 | .__wasm_init_tls => getExistingFuncType2(wasm, &.{.i32}, &.{}), | ||
| 1043 | .object_function => |i| i.ptr(wasm).type_index, | ||
| 1044 | .zcu_func => |i| i.typeIndex(wasm), | ||
| 1045 | }; | ||
| 1046 | } | ||
| 797 | 1047 | ||
| 798 | // In this case we must force link all embedded object files within the archive | 1048 | pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { |
| 799 | // We loop over all symbols, and then group them by offset as the offset | 1049 | return switch (unpack(r, wasm)) { |
| 800 | // notates where the object file starts. | 1050 | .unresolved => unreachable, |
| 801 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); | 1051 | .__wasm_apply_global_tls_relocs => @tagName(Unpacked.__wasm_apply_global_tls_relocs), |
| 802 | defer offsets.deinit(); | 1052 | .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors), |
| 803 | for (archive.toc.values()) |symbol_offsets| { | 1053 | .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory), |
| 804 | for (symbol_offsets.items) |sym_offset| { | 1054 | .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls), |
| 805 | try offsets.put(sym_offset, {}); | 1055 | .object_function => |i| i.ptr(wasm).name.slice(wasm), |
| 1056 | .zcu_func => |i| i.name(wasm), | ||
| 1057 | }; | ||
| 806 | } | 1058 | } |
| 807 | } | 1059 | }; |
| 808 | 1060 | ||
| 809 | for (offsets.keys()) |file_offset| { | 1061 | /// Index into `object_function_imports`. |
| 810 | const object = try archive.parseObject(wasm, file_contents[file_offset..], obj.path); | 1062 | pub const Index = enum(u32) { |
| 811 | try wasm.objects.append(gpa, object); | 1063 | _, |
| 812 | } | ||
| 813 | } | ||
| 814 | 1064 | ||
| 815 | fn requiresTLSReloc(wasm: *const Wasm) bool { | 1065 | pub fn key(index: Index, wasm: *const Wasm) *String { |
| 816 | for (wasm.got_symbols.items) |loc| { | 1066 | return &wasm.object_function_imports.keys()[@intFromEnum(index)]; |
| 817 | if (wasm.symbolLocSymbol(loc).isTLS()) { | ||
| 818 | return true; | ||
| 819 | } | 1067 | } |
| 820 | } | ||
| 821 | return false; | ||
| 822 | } | ||
| 823 | |||
| 824 | fn objectPath(wasm: *const Wasm, object_id: ObjectId) Path { | ||
| 825 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.path; | ||
| 826 | return obj.path; | ||
| 827 | } | ||
| 828 | 1068 | ||
| 829 | fn objectSymbols(wasm: *const Wasm, object_id: ObjectId) []const Symbol { | 1069 | pub fn value(index: Index, wasm: *const Wasm) *FunctionImport { |
| 830 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items; | 1070 | return &wasm.object_function_imports.values()[@intFromEnum(index)]; |
| 831 | return obj.symtable; | 1071 | } |
| 832 | } | ||
| 833 | 1072 | ||
| 834 | fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol { | 1073 | pub fn symbolName(index: Index, wasm: *const Wasm) String { |
| 835 | const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)]; | 1074 | return index.key(wasm).*; |
| 836 | return &obj.symtable[@intFromEnum(index)]; | 1075 | } |
| 837 | } | ||
| 838 | 1076 | ||
| 839 | fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func { | 1077 | pub fn importName(index: Index, wasm: *const Wasm) String { |
| 840 | const obj = wasm.objectById(object_id) orelse { | 1078 | return index.value(wasm).name; |
| 841 | const zo = wasm.zig_object.?; | 1079 | } |
| 842 | const sym = zo.symbols.items[@intFromEnum(sym_index)]; | ||
| 843 | return zo.functions.items[sym.index]; | ||
| 844 | }; | ||
| 845 | const sym = obj.symtable[@intFromEnum(sym_index)]; | ||
| 846 | return obj.functions[sym.index - obj.imported_functions_count]; | ||
| 847 | } | ||
| 848 | 1080 | ||
| 849 | fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 { | 1081 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { |
| 850 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count; | 1082 | return index.value(wasm).module_name; |
| 851 | return obj.imported_functions_count; | 1083 | } |
| 852 | } | ||
| 853 | 1084 | ||
| 854 | fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global { | 1085 | pub fn functionType(index: Index, wasm: *const Wasm) FunctionType.Index { |
| 855 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items; | 1086 | return value(index, wasm).type; |
| 856 | return obj.globals; | 1087 | } |
| 857 | } | 1088 | }; |
| 1089 | }; | ||
| 858 | 1090 | ||
| 859 | fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type { | 1091 | pub const ObjectFunction = extern struct { |
| 860 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items; | 1092 | flags: SymbolFlags, |
| 861 | return obj.func_types; | 1093 | /// `none` if this function has no symbol describing it. |
| 862 | } | 1094 | name: OptionalString, |
| 1095 | type_index: FunctionType.Index, | ||
| 1096 | code: Code, | ||
| 1097 | /// The offset within the code section where the data starts. | ||
| 1098 | offset: u32, | ||
| 1099 | /// The object file whose code section contains this function. | ||
| 1100 | object_index: ObjectIndex, | ||
| 863 | 1101 | ||
| 864 | fn objectSegmentInfo(wasm: *const Wasm, object_id: ObjectId) []const NamedSegment { | 1102 | pub const Code = DataPayload; |
| 865 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items; | ||
| 866 | return obj.segment_info; | ||
| 867 | } | ||
| 868 | 1103 | ||
| 869 | /// For a given symbol index, find its corresponding import. | 1104 | pub fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice { |
| 870 | /// Asserts import exists. | 1105 | const code_section_index = of.object_index.ptr(wasm).code_section_index.?; |
| 871 | fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import { | 1106 | const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty; |
| 872 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?; | 1107 | return .init(relocs, of.offset, of.code.len, wasm); |
| 873 | return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]); | 1108 | } |
| 874 | } | 1109 | }; |
| 875 | 1110 | ||
| 876 | /// Returns the object element pointer, or null if it is the ZigObject. | 1111 | pub const GlobalImport = extern struct { |
| 877 | fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object { | 1112 | flags: SymbolFlags, |
| 878 | if (object_id == .zig_object) return null; | 1113 | module_name: OptionalString, |
| 879 | return &wasm.objects.items[@intFromEnum(object_id)]; | 1114 | /// May be different than the key which is a symbol name. |
| 880 | } | 1115 | name: String, |
| 1116 | source_location: SourceLocation, | ||
| 1117 | resolution: Resolution, | ||
| 1118 | |||
| 1119 | /// Represents a synthetic global, a global from an object, or a global | ||
| 1120 | /// from the Zcu. | ||
| 1121 | pub const Resolution = enum(u32) { | ||
| 1122 | unresolved, | ||
| 1123 | __heap_base, | ||
| 1124 | __heap_end, | ||
| 1125 | __stack_pointer, | ||
| 1126 | __tls_align, | ||
| 1127 | __tls_base, | ||
| 1128 | __tls_size, | ||
| 1129 | // Next, index into `object_globals`. | ||
| 1130 | // Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. | ||
| 1131 | _, | ||
| 881 | 1132 | ||
| 882 | fn resolveSymbolsInObject(wasm: *Wasm, object_id: ObjectId) !void { | 1133 | const first_object_global = @intFromEnum(Resolution.__tls_size) + 1; |
| 883 | const gpa = wasm.base.comp.gpa; | 1134 | |
| 884 | const diags = &wasm.base.comp.link_diags; | 1135 | pub const Unpacked = union(enum) { |
| 885 | const obj_path = objectPath(wasm, object_id); | 1136 | unresolved, |
| 886 | log.debug("Resolving symbols in object: '{'}'", .{obj_path}); | 1137 | __heap_base, |
| 887 | const symbols = objectSymbols(wasm, object_id); | 1138 | __heap_end, |
| 888 | 1139 | __stack_pointer, | |
| 889 | for (symbols, 0..) |symbol, i| { | 1140 | __tls_align, |
| 890 | const sym_index: Symbol.Index = @enumFromInt(i); | 1141 | __tls_base, |
| 891 | const location: SymbolLoc = .{ | 1142 | __tls_size, |
| 892 | .file = object_id.toOptional(), | 1143 | object_global: ObjectGlobalIndex, |
| 893 | .index = sym_index, | 1144 | nav_exe: NavsExeIndex, |
| 1145 | nav_obj: NavsObjIndex, | ||
| 894 | }; | 1146 | }; |
| 895 | if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue; | ||
| 896 | 1147 | ||
| 897 | if (symbol.isLocal()) { | 1148 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { |
| 898 | if (symbol.isUndefined()) { | 1149 | return switch (r) { |
| 899 | diags.addParseError(obj_path, "local symbol '{s}' references import", .{ | 1150 | .unresolved => .unresolved, |
| 900 | wasm.stringSlice(symbol.name), | 1151 | .__heap_base => .__heap_base, |
| 901 | }); | 1152 | .__heap_end => .__heap_end, |
| 902 | } | 1153 | .__stack_pointer => .__stack_pointer, |
| 903 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); | 1154 | .__tls_align => .__tls_align, |
| 904 | continue; | 1155 | .__tls_base => .__tls_base, |
| 1156 | .__tls_size => .__tls_size, | ||
| 1157 | _ => { | ||
| 1158 | const i: u32 = @intFromEnum(r); | ||
| 1159 | const object_global_index = i - first_object_global; | ||
| 1160 | if (object_global_index < wasm.object_globals.items.len) | ||
| 1161 | return .{ .object_global = @enumFromInt(object_global_index) }; | ||
| 1162 | const comp = wasm.base.comp; | ||
| 1163 | const is_obj = comp.config.output_mode == .Obj; | ||
| 1164 | const nav_index = object_global_index - wasm.object_globals.items.len; | ||
| 1165 | return if (is_obj) .{ | ||
| 1166 | .nav_obj = @enumFromInt(nav_index), | ||
| 1167 | } else .{ | ||
| 1168 | .nav_exe = @enumFromInt(nav_index), | ||
| 1169 | }; | ||
| 1170 | }, | ||
| 1171 | }; | ||
| 905 | } | 1172 | } |
| 906 | 1173 | ||
| 907 | const maybe_existing = try wasm.globals.getOrPut(gpa, symbol.name); | 1174 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { |
| 908 | if (!maybe_existing.found_existing) { | 1175 | return switch (unpacked) { |
| 909 | maybe_existing.value_ptr.* = location; | 1176 | .unresolved => .unresolved, |
| 910 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); | 1177 | .__heap_base => .__heap_base, |
| 911 | 1178 | .__heap_end => .__heap_end, | |
| 912 | if (symbol.isUndefined()) { | 1179 | .__stack_pointer => .__stack_pointer, |
| 913 | try wasm.undefs.putNoClobber(gpa, symbol.name, location); | 1180 | .__tls_align => .__tls_align, |
| 914 | } | 1181 | .__tls_base => .__tls_base, |
| 915 | continue; | 1182 | .__tls_size => .__tls_size, |
| 1183 | .object_global => |i| @enumFromInt(first_object_global + @intFromEnum(i)), | ||
| 1184 | .nav_obj => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)), | ||
| 1185 | .nav_exe => |i| @enumFromInt(first_object_global + wasm.object_globals.items.len + @intFromEnum(i)), | ||
| 1186 | }; | ||
| 916 | } | 1187 | } |
| 917 | 1188 | ||
| 918 | const existing_loc = maybe_existing.value_ptr.*; | 1189 | pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution { |
| 919 | const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc); | 1190 | const comp = wasm.base.comp; |
| 920 | const existing_file_path: Path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else .{ | 1191 | const is_obj = comp.config.output_mode == .Obj; |
| 921 | .root_dir = std.Build.Cache.Directory.cwd(), | 1192 | return pack(wasm, if (is_obj) .{ |
| 922 | .sub_path = wasm.name, | 1193 | .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(ip_nav).?), |
| 923 | }; | 1194 | } else .{ |
| 924 | 1195 | .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?), | |
| 925 | if (!existing_sym.isUndefined()) outer: { | 1196 | }); |
| 926 | if (!symbol.isUndefined()) inner: { | 1197 | } |
| 927 | if (symbol.isWeak()) { | ||
| 928 | break :inner; // ignore the new symbol (discard it) | ||
| 929 | } | ||
| 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 | } | ||
| 939 | 1198 | ||
| 940 | try wasm.discarded.put(gpa, location, existing_loc); | 1199 | pub fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution { |
| 941 | continue; // Do not overwrite defined symbols with undefined symbols | 1200 | return pack(wasm, .{ .object_global = object_global }); |
| 942 | } | 1201 | } |
| 943 | 1202 | ||
| 944 | if (symbol.tag != existing_sym.tag) { | 1203 | pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { |
| 945 | var err = try diags.addErrorWithNotes(2); | 1204 | return switch (unpack(r, wasm)) { |
| 946 | try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ | 1205 | .unresolved => unreachable, |
| 947 | wasm.stringSlice(symbol.name), @tagName(symbol.tag), @tagName(existing_sym.tag), | 1206 | .__heap_base => @tagName(Unpacked.__heap_base), |
| 948 | }); | 1207 | .__heap_end => @tagName(Unpacked.__heap_end), |
| 949 | try err.addNote("first definition in '{'}'", .{existing_file_path}); | 1208 | .__stack_pointer => @tagName(Unpacked.__stack_pointer), |
| 950 | try err.addNote("next definition in '{'}'", .{obj_path}); | 1209 | .__tls_align => @tagName(Unpacked.__tls_align), |
| 1210 | .__tls_base => @tagName(Unpacked.__tls_base), | ||
| 1211 | .__tls_size => @tagName(Unpacked.__tls_size), | ||
| 1212 | .object_global => |i| i.name(wasm).slice(wasm), | ||
| 1213 | .nav_obj => |i| i.name(wasm), | ||
| 1214 | .nav_exe => |i| i.name(wasm), | ||
| 1215 | }; | ||
| 951 | } | 1216 | } |
| 1217 | }; | ||
| 952 | 1218 | ||
| 953 | if (existing_sym.isUndefined() and symbol.isUndefined()) { | 1219 | /// Index into `Wasm.object_global_imports`. |
| 954 | // only verify module/import name for function symbols | 1220 | pub const Index = enum(u32) { |
| 955 | if (symbol.tag == .function) { | 1221 | _, |
| 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 | } | ||
| 973 | 1222 | ||
| 974 | // both undefined so skip overwriting existing symbol and discard the new symbol | 1223 | pub fn key(index: Index, wasm: *const Wasm) *String { |
| 975 | try wasm.discarded.put(gpa, location, existing_loc); | 1224 | return &wasm.object_global_imports.keys()[@intFromEnum(index)]; |
| 976 | continue; | ||
| 977 | } | 1225 | } |
| 978 | 1226 | ||
| 979 | if (existing_sym.tag == .global) { | 1227 | pub fn value(index: Index, wasm: *const Wasm) *GlobalImport { |
| 980 | const existing_ty = wasm.getGlobalType(existing_loc); | 1228 | return &wasm.object_global_imports.values()[@intFromEnum(index)]; |
| 981 | const new_ty = wasm.getGlobalType(location); | ||
| 982 | if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) { | ||
| 983 | var err = try diags.addErrorWithNotes(2); | ||
| 984 | try err.addMsg("symbol '{s}' mismatching global types", .{wasm.stringSlice(symbol.name)}); | ||
| 985 | try err.addNote("first definition in '{'}'", .{existing_file_path}); | ||
| 986 | try err.addNote("next definition in '{'}'", .{obj_path}); | ||
| 987 | } | ||
| 988 | } | 1229 | } |
| 989 | 1230 | ||
| 990 | if (existing_sym.tag == .function) { | 1231 | pub fn symbolName(index: Index, wasm: *const Wasm) String { |
| 991 | const existing_ty = wasm.getFunctionSignature(existing_loc); | 1232 | return index.key(wasm).*; |
| 992 | const new_ty = wasm.getFunctionSignature(location); | ||
| 993 | if (!existing_ty.eql(new_ty)) { | ||
| 994 | var err = try diags.addErrorWithNotes(3); | ||
| 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 | } | 1233 | } |
| 1001 | 1234 | ||
| 1002 | // when both symbols are weak, we skip overwriting unless the existing | 1235 | pub fn importName(index: Index, wasm: *const Wasm) String { |
| 1003 | // symbol is weak and the new one isn't, in which case we *do* overwrite it. | 1236 | return index.value(wasm).name; |
| 1004 | if (existing_sym.isWeak() and symbol.isWeak()) blk: { | ||
| 1005 | if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk; | ||
| 1006 | try wasm.discarded.put(gpa, location, existing_loc); | ||
| 1007 | continue; | ||
| 1008 | } | 1237 | } |
| 1009 | 1238 | ||
| 1010 | // simply overwrite with the new symbol | 1239 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { |
| 1011 | log.debug("Overwriting symbol '{s}'", .{wasm.stringSlice(symbol.name)}); | 1240 | return index.value(wasm).module_name; |
| 1012 | log.debug(" old definition in '{'}'", .{existing_file_path}); | ||
| 1013 | log.debug(" new definition in '{'}'", .{obj_path}); | ||
| 1014 | try wasm.discarded.putNoClobber(gpa, existing_loc, location); | ||
| 1015 | maybe_existing.value_ptr.* = location; | ||
| 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 | } | 1241 | } |
| 1022 | } | ||
| 1023 | } | ||
| 1024 | 1242 | ||
| 1025 | fn resolveSymbolsInArchives(wasm: *Wasm) !void { | 1243 | pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type { |
| 1026 | if (wasm.lazy_archives.items.len == 0) return; | 1244 | return value(index, wasm).type(); |
| 1027 | const gpa = wasm.base.comp.gpa; | 1245 | } |
| 1028 | const diags = &wasm.base.comp.link_diags; | 1246 | }; |
| 1029 | 1247 | ||
| 1030 | log.debug("Resolving symbols in lazy_archives", .{}); | 1248 | pub fn @"type"(gi: *const GlobalImport) ObjectGlobal.Type { |
| 1031 | var index: u32 = 0; | 1249 | return gi.flags.global_type.to(); |
| 1032 | undef_loop: while (index < wasm.undefs.count()) { | 1250 | } |
| 1033 | const sym_name_index = wasm.undefs.keys()[index]; | 1251 | }; |
| 1034 | 1252 | ||
| 1035 | for (wasm.lazy_archives.items) |lazy_archive| { | 1253 | pub const ObjectGlobal = extern struct { |
| 1036 | const sym_name = wasm.stringSlice(sym_name_index); | 1254 | /// `none` if this function has no symbol describing it. |
| 1037 | log.debug("Detected symbol '{s}' in archive '{'}', parsing objects..", .{ | 1255 | name: OptionalString, |
| 1038 | sym_name, lazy_archive.path, | 1256 | flags: SymbolFlags, |
| 1039 | }); | 1257 | expr: Expr, |
| 1040 | const offset = lazy_archive.archive.toc.get(sym_name) orelse continue; // symbol does not exist in this archive | 1258 | /// The object file whose global section contains this global. |
| 1041 | 1259 | object_index: ObjectIndex, | |
| 1042 | // Symbol is found in unparsed object file within current archive. | 1260 | offset: u32, |
| 1043 | // Parse object and and resolve symbols again before we check remaining | 1261 | size: u32, |
| 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)); | ||
| 1052 | 1262 | ||
| 1053 | // continue loop for any remaining undefined symbols that still exist | 1263 | pub fn @"type"(og: *const ObjectGlobal) Type { |
| 1054 | // after resolving last object file | 1264 | return og.flags.global_type.to(); |
| 1055 | continue :undef_loop; | ||
| 1056 | } | ||
| 1057 | index += 1; | ||
| 1058 | } | 1265 | } |
| 1059 | } | ||
| 1060 | 1266 | ||
| 1061 | /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. | 1267 | pub const Type = struct { |
| 1062 | fn writeI32Const(writer: anytype, val: u32) !void { | 1268 | valtype: std.wasm.Valtype, |
| 1063 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 1269 | mutable: bool, |
| 1064 | try leb.writeIleb128(writer, @as(i32, @bitCast(val))); | 1270 | }; |
| 1065 | } | ||
| 1066 | |||
| 1067 | fn setupInitMemoryFunction(wasm: *Wasm) !void { | ||
| 1068 | const comp = wasm.base.comp; | ||
| 1069 | const gpa = comp.gpa; | ||
| 1070 | const shared_memory = comp.config.shared_memory; | ||
| 1071 | const import_memory = comp.config.import_memory; | ||
| 1072 | 1271 | ||
| 1073 | // Passive segments are used to avoid memory being reinitialized on each | 1272 | pub fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice { |
| 1074 | // thread's instantiation. These passive segments are initialized and | 1273 | const global_section_index = og.object_index.ptr(wasm).global_section_index.?; |
| 1075 | // dropped in __wasm_init_memory, which is registered as the start function | 1274 | const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty; |
| 1076 | // We also initialize bss segments (using memory.fill) as part of this | 1275 | return .init(relocs, og.offset, og.size, wasm); |
| 1077 | // function. | ||
| 1078 | if (!wasm.hasPassiveInitializationSegments()) { | ||
| 1079 | return; | ||
| 1080 | } | 1276 | } |
| 1081 | const sym_loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory, .function); | 1277 | }; |
| 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 | } | ||
| 1127 | |||
| 1128 | for (wasm.data_segments.keys(), wasm.data_segments.values(), 0..) |key, value, segment_index_usize| { | ||
| 1129 | const segment_index: u32 = @intCast(segment_index_usize); | ||
| 1130 | const segment = wasm.segmentPtr(value); | ||
| 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 | } | ||
| 1147 | 1278 | ||
| 1148 | try writeI32Const(writer, 0); | 1279 | pub const RefType1 = enum(u1) { |
| 1149 | try writeI32Const(writer, segment.size); | 1280 | funcref, |
| 1150 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | 1281 | externref, |
| 1151 | if (std.mem.eql(u8, key, ".bss")) { | ||
| 1152 | // fill bss segment with zeroes | ||
| 1153 | try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_fill)); | ||
| 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 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | if (shared_memory) { | ||
| 1164 | // we set the init memory flag to value '2' | ||
| 1165 | try writeI32Const(writer, flag_address); | ||
| 1166 | try writeI32Const(writer, 2); | ||
| 1167 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); | ||
| 1168 | try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.i32_atomic_store)); | ||
| 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 | } | ||
| 1200 | |||
| 1201 | for (wasm.data_segments.keys(), wasm.data_segments.values(), 0..) |name, value, segment_index_usize| { | ||
| 1202 | const segment_index: u32 = @intCast(segment_index_usize); | ||
| 1203 | const segment = wasm.segmentPtr(value); | ||
| 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 | } | ||
| 1212 | 1282 | ||
| 1213 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | 1283 | pub fn from(rt: std.wasm.RefType) RefType1 { |
| 1214 | try leb.writeUleb128(writer, std.wasm.miscOpcode(.data_drop)); | 1284 | return switch (rt) { |
| 1215 | try leb.writeUleb128(writer, segment_index); | 1285 | .funcref => .funcref, |
| 1216 | } | 1286 | .externref => .externref, |
| 1287 | }; | ||
| 1217 | } | 1288 | } |
| 1218 | 1289 | ||
| 1219 | // End of the function body | 1290 | pub fn to(rt: RefType1) std.wasm.RefType { |
| 1220 | try writer.writeByte(std.wasm.opcode(.end)); | 1291 | return switch (rt) { |
| 1292 | .funcref => .funcref, | ||
| 1293 | .externref => .externref, | ||
| 1294 | }; | ||
| 1295 | } | ||
| 1296 | }; | ||
| 1221 | 1297 | ||
| 1222 | try wasm.createSyntheticFunction( | 1298 | pub const TableImport = extern struct { |
| 1223 | wasm.preloaded_strings.__wasm_init_memory, | 1299 | flags: SymbolFlags, |
| 1224 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, | 1300 | module_name: String, |
| 1225 | &function_body, | 1301 | /// May be different than the key which is a symbol name. |
| 1226 | ); | 1302 | name: String, |
| 1227 | } | 1303 | source_location: SourceLocation, |
| 1304 | resolution: Resolution, | ||
| 1305 | limits_min: u32, | ||
| 1306 | limits_max: u32, | ||
| 1307 | |||
| 1308 | /// Represents a synthetic table, or a table from an object. | ||
| 1309 | pub const Resolution = enum(u32) { | ||
| 1310 | unresolved, | ||
| 1311 | __indirect_function_table, | ||
| 1312 | // Next, index into `object_tables`. | ||
| 1313 | _, | ||
| 1228 | 1314 | ||
| 1229 | /// Constructs a synthetic function that performs runtime relocations for | 1315 | const first_object_table = @intFromEnum(Resolution.__indirect_function_table) + 1; |
| 1230 | /// TLS symbols. This function is called by `__wasm_init_tls`. | ||
| 1231 | fn setupTLSRelocationsFunction(wasm: *Wasm) !void { | ||
| 1232 | const comp = wasm.base.comp; | ||
| 1233 | const gpa = comp.gpa; | ||
| 1234 | const shared_memory = comp.config.shared_memory; | ||
| 1235 | 1316 | ||
| 1236 | // When we have TLS GOT entries and shared memory is enabled, | 1317 | pub const Unpacked = union(enum) { |
| 1237 | // we must perform runtime relocations or else we don't create the function. | 1318 | unresolved, |
| 1238 | if (!shared_memory or !wasm.requiresTLSReloc()) { | 1319 | __indirect_function_table, |
| 1239 | return; | 1320 | object_table: ObjectTableIndex, |
| 1240 | } | 1321 | }; |
| 1241 | 1322 | ||
| 1242 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_apply_global_tls_relocs, .function); | 1323 | pub fn unpack(r: Resolution) Unpacked { |
| 1243 | wasm.symbolLocSymbol(loc).mark(); | 1324 | return switch (r) { |
| 1244 | var function_body = std.ArrayList(u8).init(gpa); | 1325 | .unresolved => .unresolved, |
| 1245 | defer function_body.deinit(); | 1326 | .__indirect_function_table => .__indirect_function_table, |
| 1246 | const writer = function_body.writer(); | 1327 | _ => .{ .object_table = @enumFromInt(@intFromEnum(r) - first_object_table) }, |
| 1247 | 1328 | }; | |
| 1248 | // locals (we have none) | 1329 | } |
| 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)); | ||
| 1270 | |||
| 1271 | try wasm.createSyntheticFunction( | ||
| 1272 | wasm.preloaded_strings.__wasm_apply_global_tls_relocs, | ||
| 1273 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, | ||
| 1274 | &function_body, | ||
| 1275 | ); | ||
| 1276 | } | ||
| 1277 | 1330 | ||
| 1278 | fn validateFeatures( | 1331 | fn pack(unpacked: Unpacked) Resolution { |
| 1279 | wasm: *const Wasm, | 1332 | return switch (unpacked) { |
| 1280 | to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool, | 1333 | .unresolved => .unresolved, |
| 1281 | emit_features_count: *u32, | 1334 | .__indirect_function_table => .__indirect_function_table, |
| 1282 | ) !void { | 1335 | .object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)), |
| 1283 | const comp = wasm.base.comp; | 1336 | }; |
| 1284 | const diags = &wasm.base.comp.link_diags; | ||
| 1285 | const target = comp.root_mod.resolved_target.result; | ||
| 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 | } | 1337 | } |
| 1310 | } | ||
| 1311 | 1338 | ||
| 1312 | // extract all the used, disallowed and required features from each | 1339 | fn fromObjectTable(object_table: ObjectTableIndex) Resolution { |
| 1313 | // linked object file so we can test them. | 1340 | return pack(.{ .object_table = object_table }); |
| 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 | } | 1341 | } |
| 1330 | 1342 | ||
| 1331 | for (object.segment_info) |segment| { | 1343 | pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType { |
| 1332 | if (segment.isTLS()) { | 1344 | return switch (unpack(r)) { |
| 1333 | has_tls = true; | 1345 | .unresolved => unreachable, |
| 1334 | } | 1346 | .__indirect_function_table => .funcref, |
| 1347 | .object_table => |i| i.ptr(wasm).flags.ref_type.to(), | ||
| 1348 | }; | ||
| 1335 | } | 1349 | } |
| 1336 | } | ||
| 1337 | 1350 | ||
| 1338 | // when we infer the features, we allow each feature found in the 'used' set | 1351 | pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits { |
| 1339 | // and insert it into the 'allowed' set. When features are not inferred, | 1352 | return switch (unpack(r)) { |
| 1340 | // we validate that a used feature is allowed. | 1353 | .unresolved => unreachable, |
| 1341 | for (used, 0..) |used_set, used_index| { | 1354 | .__indirect_function_table => .{ |
| 1342 | const is_enabled = @as(u1, @truncate(used_set)) != 0; | 1355 | .flags = .{ .has_max = true, .is_shared = false }, |
| 1343 | if (infer) { | 1356 | .min = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1), |
| 1344 | allowed[used_index] = is_enabled; | 1357 | .max = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1), |
| 1345 | emit_features_count.* += @intFromBool(is_enabled); | 1358 | }, |
| 1346 | } else if (is_enabled and !allowed[used_index]) { | 1359 | .object_table => |i| i.ptr(wasm).limits(), |
| 1347 | diags.addParseError( | 1360 | }; |
| 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 | } | 1361 | } |
| 1354 | } | 1362 | }; |
| 1355 | 1363 | ||
| 1356 | if (!valid_feature_set) { | 1364 | /// Index into `object_table_imports`. |
| 1357 | return error.FlushFailure; | 1365 | pub const Index = enum(u32) { |
| 1358 | } | 1366 | _, |
| 1359 | 1367 | ||
| 1360 | if (shared_memory) { | 1368 | pub fn key(index: Index, wasm: *const Wasm) *String { |
| 1361 | const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)]; | 1369 | return &wasm.object_table_imports.keys()[@intFromEnum(index)]; |
| 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 | } | 1370 | } |
| 1370 | 1371 | ||
| 1371 | for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| { | 1372 | pub fn value(index: Index, wasm: *const Wasm) *TableImport { |
| 1372 | if (!allowed[@intFromEnum(feature)]) { | 1373 | return &wasm.object_table_imports.values()[@intFromEnum(index)]; |
| 1373 | var err = try diags.addErrorWithNotes(0); | ||
| 1374 | try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature}); | ||
| 1375 | } | ||
| 1376 | } | 1374 | } |
| 1377 | } | ||
| 1378 | 1375 | ||
| 1379 | if (has_tls) { | 1376 | pub fn name(index: Index, wasm: *const Wasm) String { |
| 1380 | for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| { | 1377 | return index.key(wasm).*; |
| 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 | } | ||
| 1385 | } | 1378 | } |
| 1386 | } | ||
| 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 | 1379 | ||
| 1402 | object_used_features[@intFromEnum(feature.tag)] = true; | 1380 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { |
| 1381 | return index.value(wasm).module_name; | ||
| 1403 | } | 1382 | } |
| 1383 | }; | ||
| 1404 | 1384 | ||
| 1405 | // validate the linked object file has each required feature | 1385 | pub fn limits(ti: *const TableImport) std.wasm.Limits { |
| 1406 | for (required, 0..) |required_feature, feature_index| { | 1386 | return .{ |
| 1407 | const is_required = @as(u1, @truncate(required_feature)) != 0; | 1387 | .flags = .{ |
| 1408 | if (is_required and !object_used_features[feature_index]) { | 1388 | .has_max = ti.flags.limits_has_max, |
| 1409 | var err = try diags.addErrorWithNotes(2); | 1389 | .is_shared = ti.flags.limits_is_shared, |
| 1410 | try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))}); | 1390 | }, |
| 1411 | try err.addNote("required by '{'}'", .{wasm.objects.items[required_feature >> 1].path}); | 1391 | .min = ti.limits_min, |
| 1412 | try err.addNote("missing in '{'}'", .{object.path}); | 1392 | .max = ti.limits_max, |
| 1413 | valid_feature_set = false; | 1393 | }; |
| 1414 | } | ||
| 1415 | } | ||
| 1416 | } | 1394 | } |
| 1395 | }; | ||
| 1417 | 1396 | ||
| 1418 | if (!valid_feature_set) { | 1397 | pub const Table = extern struct { |
| 1419 | return error.FlushFailure; | 1398 | module_name: OptionalString, |
| 1420 | } | 1399 | name: OptionalString, |
| 1400 | flags: SymbolFlags, | ||
| 1401 | limits_min: u32, | ||
| 1402 | limits_max: u32, | ||
| 1421 | 1403 | ||
| 1422 | to_emit.* = allowed; | 1404 | pub fn limits(t: *const Table) std.wasm.Limits { |
| 1423 | } | 1405 | return .{ |
| 1406 | .flags = .{ | ||
| 1407 | .has_max = t.flags.limits_has_max, | ||
| 1408 | .is_shared = t.flags.limits_is_shared, | ||
| 1409 | }, | ||
| 1410 | .min = t.limits_min, | ||
| 1411 | .max = t.limits_max, | ||
| 1412 | }; | ||
| 1413 | } | ||
| 1414 | }; | ||
| 1424 | 1415 | ||
| 1425 | /// Creates synthetic linker-symbols, but only if they are being referenced from | 1416 | /// Uniquely identifies a section across all objects. By subtracting |
| 1426 | /// any object file. For instance, the `__heap_base` symbol will only be created, | 1417 | /// `Object.local_section_index_base` from this one, the Object section index |
| 1427 | /// if one or multiple undefined references exist. When none exist, the symbol will | 1418 | /// is obtained. |
| 1428 | /// not be created, ensuring we don't unnecessarily emit unreferenced symbols. | 1419 | pub const ObjectSectionIndex = enum(u32) { |
| 1429 | fn resolveLazySymbols(wasm: *Wasm) !void { | 1420 | _, |
| 1430 | const comp = wasm.base.comp; | 1421 | }; |
| 1431 | const gpa = comp.gpa; | ||
| 1432 | const shared_memory = comp.config.shared_memory; | ||
| 1433 | 1422 | ||
| 1434 | if (wasm.getExistingString("__heap_base")) |name_offset| { | 1423 | /// Index into `object_tables`. |
| 1435 | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { | 1424 | pub const ObjectTableIndex = enum(u32) { |
| 1436 | const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); | 1425 | _, |
| 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 | } | ||
| 1440 | } | ||
| 1441 | 1426 | ||
| 1442 | if (wasm.getExistingString("__heap_end")) |name_offset| { | 1427 | pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table { |
| 1443 | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { | 1428 | return &wasm.object_tables.items[@intFromEnum(index)]; |
| 1444 | const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); | ||
| 1445 | try wasm.discarded.putNoClobber(gpa, kv.value, loc); | ||
| 1446 | _ = wasm.resolved_symbols.swapRemove(loc); | ||
| 1447 | } | ||
| 1448 | } | 1429 | } |
| 1449 | 1430 | ||
| 1450 | if (!shared_memory) { | 1431 | pub fn chaseWeak(i: ObjectTableIndex, wasm: *const Wasm) ObjectTableIndex { |
| 1451 | if (wasm.getExistingString("__tls_base")) |name_offset| { | 1432 | const table = ptr(i, wasm); |
| 1452 | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { | 1433 | if (table.flags.binding != .weak) return i; |
| 1453 | const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global); | 1434 | const name = table.name.unwrap().?; |
| 1454 | try wasm.discarded.putNoClobber(gpa, kv.value, loc); | 1435 | const import = wasm.object_table_imports.getPtr(name).?; |
| 1455 | _ = wasm.resolved_symbols.swapRemove(kv.value); | 1436 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. |
| 1456 | const symbol = wasm.symbolLocSymbol(loc); | 1437 | return import.resolution.unpack().object_table; |
| 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 | } | ||
| 1465 | } | 1438 | } |
| 1466 | } | 1439 | }; |
| 1467 | 1440 | ||
| 1468 | pub fn findGlobalSymbol(wasm: *const Wasm, name: []const u8) ?SymbolLoc { | 1441 | /// Index into `Wasm.object_globals`. |
| 1469 | const name_index = wasm.getExistingString(name) orelse return null; | 1442 | pub const ObjectGlobalIndex = enum(u32) { |
| 1470 | return wasm.globals.get(name_index); | 1443 | _, |
| 1471 | } | ||
| 1472 | 1444 | ||
| 1473 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { | 1445 | pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal { |
| 1474 | const comp = wasm.base.comp; | 1446 | return &wasm.object_globals.items[@intFromEnum(index)]; |
| 1475 | const diags = &wasm.base.comp.link_diags; | ||
| 1476 | if (comp.config.output_mode == .Obj) return; | ||
| 1477 | if (wasm.import_symbols) return; | ||
| 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 | } | 1447 | } |
| 1504 | } | ||
| 1505 | |||
| 1506 | pub fn deinit(wasm: *Wasm) void { | ||
| 1507 | const gpa = wasm.base.comp.gpa; | ||
| 1508 | if (wasm.llvm_object) |llvm_object| llvm_object.deinit(); | ||
| 1509 | 1448 | ||
| 1510 | for (wasm.func_types.items) |*func_type| { | 1449 | pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString { |
| 1511 | func_type.deinit(gpa); | 1450 | return index.ptr(wasm).name; |
| 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 | } | 1451 | } |
| 1522 | 1452 | ||
| 1523 | for (wasm.lazy_archives.items) |*lazy_archive| lazy_archive.deinit(gpa); | 1453 | pub fn chaseWeak(i: ObjectGlobalIndex, wasm: *const Wasm) ObjectGlobalIndex { |
| 1524 | wasm.lazy_archives.deinit(gpa); | 1454 | const global = ptr(i, wasm); |
| 1525 | 1455 | if (global.flags.binding != .weak) return i; | |
| 1526 | if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls)) |loc| { | 1456 | const import_name = global.name.unwrap().?; |
| 1527 | const atom = wasm.symbol_atom.get(loc).?; | 1457 | const import = wasm.object_global_imports.getPtr(import_name).?; |
| 1528 | wasm.getAtomPtr(atom).deinit(gpa); | 1458 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. |
| 1459 | return import.resolution.unpack(wasm).object_global; | ||
| 1529 | } | 1460 | } |
| 1461 | }; | ||
| 1530 | 1462 | ||
| 1531 | wasm.synthetic_symbols.deinit(gpa); | 1463 | pub const ObjectMemory = extern struct { |
| 1532 | wasm.globals.deinit(gpa); | 1464 | flags: SymbolFlags, |
| 1533 | wasm.resolved_symbols.deinit(gpa); | 1465 | name: OptionalString, |
| 1534 | wasm.undefs.deinit(gpa); | 1466 | limits_min: u32, |
| 1535 | wasm.discarded.deinit(gpa); | 1467 | limits_max: u32, |
| 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 | 1468 | ||
| 1544 | // free output sections | 1469 | /// Index into `Wasm.object_memories`. |
| 1545 | wasm.imports.deinit(gpa); | 1470 | pub const Index = enum(u32) { |
| 1546 | wasm.func_types.deinit(gpa); | 1471 | _, |
| 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 | 1472 | ||
| 1554 | wasm.string_bytes.deinit(gpa); | 1473 | pub fn ptr(index: Index, wasm: *const Wasm) *ObjectMemory { |
| 1555 | wasm.string_table.deinit(gpa); | 1474 | return &wasm.object_memories.items[@intFromEnum(index)]; |
| 1556 | wasm.dump_argv_list.deinit(gpa); | 1475 | } |
| 1557 | } | 1476 | }; |
| 1558 | 1477 | ||
| 1559 | pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 1478 | pub fn limits(om: *const ObjectMemory) std.wasm.Limits { |
| 1560 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | 1479 | return .{ |
| 1561 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 1480 | .flags = .{ |
| 1481 | .has_max = om.limits_has_max, | ||
| 1482 | .is_shared = om.limits_is_shared, | ||
| 1483 | }, | ||
| 1484 | .min = om.limits_min, | ||
| 1485 | .max = om.limits_max, | ||
| 1486 | }; | ||
| 1562 | } | 1487 | } |
| 1563 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); | 1488 | }; |
| 1564 | try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness); | ||
| 1565 | } | ||
| 1566 | 1489 | ||
| 1567 | // Generate code for the "Nav", storing it in memory to be later written to | 1490 | /// Index into `Wasm.object_functions`. |
| 1568 | // the file on flush(). | 1491 | pub const ObjectFunctionIndex = enum(u32) { |
| 1569 | pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { | 1492 | _, |
| 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 | } | ||
| 1576 | 1493 | ||
| 1577 | pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { | 1494 | pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction { |
| 1578 | if (wasm.llvm_object) |_| return; | 1495 | return &wasm.object_functions.items[@intFromEnum(index)]; |
| 1579 | try wasm.zig_object.?.updateLineNumber(pt, ti_id); | 1496 | } |
| 1580 | } | ||
| 1581 | 1497 | ||
| 1582 | /// From a given symbol location, returns its `wasm.GlobalType`. | 1498 | pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex { |
| 1583 | /// Asserts the Symbol represents a global. | 1499 | const result: OptionalObjectFunctionIndex = @enumFromInt(@intFromEnum(i)); |
| 1584 | fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType { | 1500 | assert(result != .none); |
| 1585 | const symbol = wasm.symbolLocSymbol(loc); | 1501 | return result; |
| 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 | } | 1502 | } |
| 1610 | } | ||
| 1611 | 1503 | ||
| 1612 | /// From a given symbol location, returns its `wasm.Type`. | 1504 | pub fn chaseWeak(i: ObjectFunctionIndex, wasm: *const Wasm) ObjectFunctionIndex { |
| 1613 | /// Asserts the Symbol represents a function. | 1505 | const func = ptr(i, wasm); |
| 1614 | fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { | 1506 | if (func.flags.binding != .weak) return i; |
| 1615 | const symbol = wasm.symbolLocSymbol(loc); | 1507 | const name = func.name.unwrap().?; |
| 1616 | assert(symbol.tag == .function); | 1508 | const import = wasm.object_function_imports.getPtr(name).?; |
| 1617 | const is_undefined = symbol.isUndefined(); | 1509 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. |
| 1618 | switch (loc.file) { | 1510 | return import.resolution.unpack(wasm).object_function; |
| 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 | } | 1511 | } |
| 1652 | } | 1512 | }; |
| 1653 | 1513 | ||
| 1654 | /// Returns the symbol index from a symbol of which its flag is set global, | 1514 | /// Index into `object_functions`, or null. |
| 1655 | /// such as an exported or imported symbol. | 1515 | pub const OptionalObjectFunctionIndex = enum(u32) { |
| 1656 | /// If the symbol does not yet exist, creates a new one symbol instead | 1516 | none = std.math.maxInt(u32), |
| 1657 | /// and then returns the index to it. | 1517 | _, |
| 1658 | pub 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 | 1518 | ||
| 1664 | /// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type. | 1519 | pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex { |
| 1665 | /// Returns the given pointer address | 1520 | if (i == .none) return null; |
| 1666 | pub fn getNavVAddr( | 1521 | return @enumFromInt(@intFromEnum(i)); |
| 1667 | wasm: *Wasm, | 1522 | } |
| 1668 | pt: Zcu.PerThread, | 1523 | }; |
| 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 | 1524 | ||
| 1675 | pub fn lowerUav( | 1525 | pub const ObjectDataSegment = extern struct { |
| 1676 | wasm: *Wasm, | 1526 | /// `none` if segment info custom subsection is missing. |
| 1677 | pt: Zcu.PerThread, | 1527 | name: OptionalString, |
| 1678 | uav: InternPool.Index, | 1528 | flags: Flags, |
| 1679 | explicit_alignment: Alignment, | 1529 | payload: DataPayload, |
| 1680 | src_loc: Zcu.LazySrcLoc, | 1530 | offset: u32, |
| 1681 | ) !codegen.GenResult { | 1531 | object_index: ObjectIndex, |
| 1682 | return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc); | 1532 | |
| 1683 | } | 1533 | pub const Flags = packed struct(u32) { |
| 1534 | alive: bool = false, | ||
| 1535 | is_passive: bool = false, | ||
| 1536 | alignment: Alignment = .none, | ||
| 1537 | /// Signals that the segment contains only null terminated strings allowing | ||
| 1538 | /// the linker to perform merging. | ||
| 1539 | strings: bool = false, | ||
| 1540 | /// The segment contains thread-local data. This means that a unique copy | ||
| 1541 | /// of this segment will be created for each thread. | ||
| 1542 | tls: bool = false, | ||
| 1543 | /// If the object file is included in the final link, the segment should be | ||
| 1544 | /// retained in the final output regardless of whether it is used by the | ||
| 1545 | /// program. | ||
| 1546 | retain: bool = false, | ||
| 1547 | |||
| 1548 | _: u21 = 0, | ||
| 1549 | }; | ||
| 1684 | 1550 | ||
| 1685 | pub fn getUavVAddr(wasm: *Wasm, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { | 1551 | /// Index into `Wasm.object_data_segments`. |
| 1686 | return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info); | 1552 | pub const Index = enum(u32) { |
| 1687 | } | 1553 | _, |
| 1688 | 1554 | ||
| 1689 | pub fn deleteExport( | 1555 | pub fn ptr(i: Index, wasm: *const Wasm) *ObjectDataSegment { |
| 1690 | wasm: *Wasm, | 1556 | return &wasm.object_data_segments.items[@intFromEnum(i)]; |
| 1691 | exported: Zcu.Exported, | 1557 | } |
| 1692 | name: InternPool.NullTerminatedString, | 1558 | }; |
| 1693 | ) void { | ||
| 1694 | if (wasm.llvm_object) |_| return; | ||
| 1695 | return wasm.zig_object.?.deleteExport(wasm, exported, name); | ||
| 1696 | } | ||
| 1697 | 1559 | ||
| 1698 | pub fn updateExports( | 1560 | pub fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice { |
| 1699 | wasm: *Wasm, | 1561 | const data_section_index = ods.object_index.ptr(wasm).data_section_index.?; |
| 1700 | pt: Zcu.PerThread, | 1562 | const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty; |
| 1701 | exported: Zcu.Exported, | 1563 | return .init(relocs, ods.offset, ods.payload.len, wasm); |
| 1702 | export_indices: []const u32, | ||
| 1703 | ) !void { | ||
| 1704 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | ||
| 1705 | @panic("Attempted to compile for object format that was disabled by build configuration"); | ||
| 1706 | } | 1564 | } |
| 1707 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); | 1565 | }; |
| 1708 | return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices); | ||
| 1709 | } | ||
| 1710 | 1566 | ||
| 1711 | pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { | 1567 | /// A local or exported global const from an object file. |
| 1712 | if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); | 1568 | pub const ObjectData = extern struct { |
| 1713 | return wasm.zig_object.?.freeDecl(wasm, decl_index); | 1569 | segment: ObjectDataSegment.Index, |
| 1714 | } | 1570 | /// Index into the object segment payload. Must be <= the segment's size. |
| 1571 | offset: u32, | ||
| 1572 | /// May be zero. `offset + size` must be <= the segment's size. | ||
| 1573 | size: u32, | ||
| 1574 | name: String, | ||
| 1575 | flags: SymbolFlags, | ||
| 1715 | 1576 | ||
| 1716 | /// Assigns indexes to all indirect functions. | 1577 | /// Index into `Wasm.object_datas`. |
| 1717 | /// Starts at offset 1, where the value `0` represents an unresolved function pointer | 1578 | pub const Index = enum(u32) { |
| 1718 | /// or null-pointer | 1579 | _, |
| 1719 | fn mapFunctionTable(wasm: *Wasm) void { | 1580 | |
| 1720 | var it = wasm.function_table.iterator(); | 1581 | pub fn ptr(i: Index, wasm: *const Wasm) *ObjectData { |
| 1721 | var index: u32 = 1; | 1582 | return &wasm.object_datas.items[@intFromEnum(i)]; |
| 1722 | while (it.next()) |entry| { | ||
| 1723 | const symbol = wasm.symbolLocSymbol(entry.key_ptr.*); | ||
| 1724 | if (symbol.isAlive()) { | ||
| 1725 | entry.value_ptr.* = index; | ||
| 1726 | index += 1; | ||
| 1727 | } else { | ||
| 1728 | wasm.function_table.removeByPtr(entry.key_ptr); | ||
| 1729 | } | 1583 | } |
| 1730 | } | 1584 | }; |
| 1585 | }; | ||
| 1731 | 1586 | ||
| 1732 | if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) { | 1587 | pub const ObjectDataImport = extern struct { |
| 1733 | const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?; | 1588 | resolution: Resolution, |
| 1734 | const import = wasm.imports.getPtr(sym_loc).?; | 1589 | flags: SymbolFlags, |
| 1735 | import.kind.table.limits.min = index - 1; // we start at index 1. | 1590 | source_location: SourceLocation, |
| 1736 | } else if (index > 1) { | 1591 | |
| 1737 | log.debug("Appending indirect function table", .{}); | 1592 | pub const Resolution = enum(u32) { |
| 1738 | const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?; | 1593 | unresolved, |
| 1739 | const symbol = wasm.symbolLocSymbol(sym_loc); | 1594 | __zig_error_names, |
| 1740 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; | 1595 | __zig_error_name_table, |
| 1741 | table.limits = .{ .min = index, .max = index, .flags = 0x1 }; | 1596 | __heap_base, |
| 1742 | } | 1597 | __heap_end, |
| 1743 | } | 1598 | /// Next, an `ObjectData.Index`. |
| 1599 | /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. | ||
| 1600 | /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. | ||
| 1601 | _, | ||
| 1744 | 1602 | ||
| 1745 | /// From a given index, append the given `Atom` at the back of the linked list. | 1603 | const first_object = @intFromEnum(Resolution.__heap_end) + 1; |
| 1746 | /// Simply inserts it into the map of atoms when it doesn't exist yet. | 1604 | |
| 1747 | pub fn appendAtomAtIndex(wasm: *Wasm, index: Segment.Index, atom_index: Atom.Index) !void { | 1605 | pub const Unpacked = union(enum) { |
| 1748 | const gpa = wasm.base.comp.gpa; | 1606 | unresolved, |
| 1749 | const atom = wasm.getAtomPtr(atom_index); | 1607 | __zig_error_names, |
| 1750 | if (wasm.atoms.getPtr(index)) |last_index_ptr| { | 1608 | __zig_error_name_table, |
| 1751 | atom.prev = last_index_ptr.*; | 1609 | __heap_base, |
| 1752 | last_index_ptr.* = atom_index; | 1610 | __heap_end, |
| 1753 | } else { | 1611 | object: ObjectData.Index, |
| 1754 | try wasm.atoms.putNoClobber(gpa, index, atom_index); | 1612 | uav_exe: UavsExeIndex, |
| 1755 | } | 1613 | uav_obj: UavsObjIndex, |
| 1756 | } | 1614 | nav_exe: NavsExeIndex, |
| 1615 | nav_obj: NavsObjIndex, | ||
| 1616 | }; | ||
| 1757 | 1617 | ||
| 1758 | fn allocateAtoms(wasm: *Wasm) !void { | 1618 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { |
| 1759 | // first sort the data segments | 1619 | return switch (r) { |
| 1760 | try sortDataSegments(wasm); | 1620 | .unresolved => .unresolved, |
| 1761 | 1621 | .__zig_error_names => .__zig_error_names, | |
| 1762 | var it = wasm.atoms.iterator(); | 1622 | .__zig_error_name_table => .__zig_error_name_table, |
| 1763 | while (it.next()) |entry| { | 1623 | .__heap_base => .__heap_base, |
| 1764 | const segment = wasm.segmentPtr(entry.key_ptr.*); | 1624 | .__heap_end => .__heap_end, |
| 1765 | var atom_index = entry.value_ptr.*; | 1625 | _ => { |
| 1766 | if (entry.key_ptr.toOptional() == wasm.code_section_index) { | 1626 | const object_index = @intFromEnum(r) - first_object; |
| 1767 | // Code section is allocated upon writing as they are required to be ordered | 1627 | |
| 1768 | // to synchronise with the function section. | 1628 | const uav_index = if (object_index < wasm.object_datas.items.len) |
| 1769 | continue; | 1629 | return .{ .object = @enumFromInt(object_index) } |
| 1770 | } | 1630 | else |
| 1771 | var offset: u32 = 0; | 1631 | object_index - wasm.object_datas.items.len; |
| 1772 | while (true) { | 1632 | |
| 1773 | const atom = wasm.getAtomPtr(atom_index); | 1633 | const comp = wasm.base.comp; |
| 1774 | const symbol_loc = atom.symbolLoc(); | 1634 | const is_obj = comp.config.output_mode == .Obj; |
| 1775 | // Ensure we get the original symbol, so we verify the correct symbol on whether | 1635 | if (is_obj) { |
| 1776 | // it is dead or not and ensure an atom is removed when dead. | 1636 | const nav_index = if (uav_index < wasm.uavs_obj.entries.len) |
| 1777 | // This is required as we may have parsed aliases into atoms. | 1637 | return .{ .uav_obj = @enumFromInt(uav_index) } |
| 1778 | const sym = switch (symbol_loc.file) { | 1638 | else |
| 1779 | .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)], | 1639 | uav_index - wasm.uavs_obj.entries.len; |
| 1780 | .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)], | 1640 | |
| 1781 | _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)], | 1641 | return .{ .nav_obj = @enumFromInt(nav_index) }; |
| 1642 | } else { | ||
| 1643 | const nav_index = if (uav_index < wasm.uavs_exe.entries.len) | ||
| 1644 | return .{ .uav_exe = @enumFromInt(uav_index) } | ||
| 1645 | else | ||
| 1646 | uav_index - wasm.uavs_exe.entries.len; | ||
| 1647 | |||
| 1648 | return .{ .nav_exe = @enumFromInt(nav_index) }; | ||
| 1649 | } | ||
| 1650 | }, | ||
| 1782 | }; | 1651 | }; |
| 1652 | } | ||
| 1783 | 1653 | ||
| 1784 | // Dead symbols must be unlinked from the linked-list to prevent them | 1654 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { |
| 1785 | // from being emit into the binary. | 1655 | return switch (unpacked) { |
| 1786 | if (sym.isDead()) { | 1656 | .unresolved => .unresolved, |
| 1787 | if (entry.value_ptr.* == atom_index and atom.prev != .null) { | 1657 | .__zig_error_names => .__zig_error_names, |
| 1788 | // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update | 1658 | .__zig_error_name_table => .__zig_error_name_table, |
| 1789 | // the entry to point it to the previous atom to ensure we do not start with a dead symbol that | 1659 | .__heap_base => .__heap_base, |
| 1790 | // was removed and therefore do not emit any code at all. | 1660 | .__heap_end => .__heap_end, |
| 1791 | entry.value_ptr.* = atom.prev; | 1661 | .object => |i| @enumFromInt(first_object + @intFromEnum(i)), |
| 1792 | } | 1662 | inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + @intFromEnum(i)), |
| 1793 | if (atom.prev == .null) break; | 1663 | .nav_exe => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)), |
| 1794 | atom_index = atom.prev; | 1664 | .nav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)), |
| 1795 | atom.prev = .null; | 1665 | }; |
| 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 | } | 1666 | } |
| 1810 | segment.size = @intCast(segment.alignment.forward(offset)); | ||
| 1811 | } | ||
| 1812 | } | ||
| 1813 | 1667 | ||
| 1814 | /// For each data symbol, sets the virtual address. | 1668 | pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution { |
| 1815 | fn allocateVirtualAddresses(wasm: *Wasm) void { | 1669 | return pack(wasm, .{ .object = object_data_index }); |
| 1816 | for (wasm.resolved_symbols.keys()) |loc| { | 1670 | } |
| 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 | }; | ||
| 1827 | 1671 | ||
| 1828 | const atom = wasm.getAtom(atom_index); | 1672 | pub fn objectDataSegment(r: Resolution, wasm: *const Wasm) ?ObjectDataSegment.Index { |
| 1829 | const merge_segment = wasm.base.comp.config.output_mode != .Obj; | 1673 | return switch (unpack(r, wasm)) { |
| 1830 | const segment_info = switch (atom.file) { | 1674 | .unresolved => unreachable, |
| 1831 | .zig_object => wasm.zig_object.?.segment_info.items, | 1675 | .object => |i| i.ptr(wasm).segment, |
| 1832 | .none => wasm.segment_info.values(), | 1676 | .__zig_error_names, |
| 1833 | _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info, | 1677 | .__zig_error_name_table, |
| 1834 | }; | 1678 | .__heap_base, |
| 1835 | const segment_name = segment_info[symbol.index].outputName(merge_segment); | 1679 | .__heap_end, |
| 1836 | const segment_index = wasm.data_segments.get(segment_name).?; | 1680 | .uav_exe, |
| 1837 | const segment = wasm.segmentPtr(segment_index); | 1681 | .uav_obj, |
| 1838 | 1682 | .nav_exe, | |
| 1839 | // TLS symbols have their virtual address set relative to their own TLS segment, | 1683 | .nav_obj, |
| 1840 | // rather than the entire Data section. | 1684 | => null, |
| 1841 | if (symbol.hasFlag(.WASM_SYM_TLS)) { | 1685 | }; |
| 1842 | symbol.virtual_address = atom.offset; | ||
| 1843 | } else { | ||
| 1844 | symbol.virtual_address = atom.offset + segment.offset; | ||
| 1845 | } | 1686 | } |
| 1846 | } | ||
| 1847 | } | ||
| 1848 | 1687 | ||
| 1849 | fn sortDataSegments(wasm: *Wasm) !void { | 1688 | pub fn dataLoc(r: Resolution, wasm: *const Wasm) DataLoc { |
| 1850 | const gpa = wasm.base.comp.gpa; | 1689 | return switch (unpack(r, wasm)) { |
| 1851 | var new_mapping: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty; | 1690 | .unresolved => unreachable, |
| 1852 | try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count()); | 1691 | .object => |i| { |
| 1853 | errdefer new_mapping.deinit(gpa); | 1692 | const ptr = i.ptr(wasm); |
| 1693 | return .{ | ||
| 1694 | .segment = .fromObjectDataSegment(wasm, ptr.segment), | ||
| 1695 | .offset = ptr.offset, | ||
| 1696 | }; | ||
| 1697 | }, | ||
| 1698 | .__zig_error_names => .{ .segment = .__zig_error_names, .offset = 0 }, | ||
| 1699 | .__zig_error_name_table => .{ .segment = .__zig_error_name_table, .offset = 0 }, | ||
| 1700 | .__heap_base => .{ .segment = .__heap_base, .offset = 0 }, | ||
| 1701 | .__heap_end => .{ .segment = .__heap_end, .offset = 0 }, | ||
| 1702 | .uav_exe => @panic("TODO"), | ||
| 1703 | .uav_obj => @panic("TODO"), | ||
| 1704 | .nav_exe => @panic("TODO"), | ||
| 1705 | .nav_obj => @panic("TODO"), | ||
| 1706 | }; | ||
| 1707 | } | ||
| 1708 | }; | ||
| 1854 | 1709 | ||
| 1855 | const keys = try gpa.dupe([]const u8, wasm.data_segments.keys()); | 1710 | /// Points into `Wasm.object_data_imports`. |
| 1856 | defer gpa.free(keys); | 1711 | pub const Index = enum(u32) { |
| 1712 | _, | ||
| 1857 | 1713 | ||
| 1858 | const SortContext = struct { | 1714 | pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport { |
| 1859 | fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { | 1715 | return &wasm.object_data_imports.values()[@intFromEnum(i)]; |
| 1860 | return order(lhs) < order(rhs); | ||
| 1861 | } | 1716 | } |
| 1862 | 1717 | ||
| 1863 | fn order(name: []const u8) u8 { | 1718 | pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index { |
| 1864 | if (mem.startsWith(u8, name, ".rodata")) return 0; | 1719 | return @enumFromInt(wasm.object_data_imports.getIndex(name) orelse return null); |
| 1865 | if (mem.startsWith(u8, name, ".data")) return 1; | ||
| 1866 | if (mem.startsWith(u8, name, ".text")) return 2; | ||
| 1867 | return 3; | ||
| 1868 | } | 1720 | } |
| 1869 | }; | 1721 | }; |
| 1722 | }; | ||
| 1870 | 1723 | ||
| 1871 | mem.sort([]const u8, keys, {}, SortContext.sort); | 1724 | pub const DataPayload = extern struct { |
| 1872 | for (keys) |key| { | 1725 | off: Off, |
| 1873 | const segment_index = wasm.data_segments.get(key).?; | 1726 | /// The size in bytes of the data representing the segment within the section. |
| 1874 | new_mapping.putAssumeCapacity(key, segment_index); | 1727 | len: u32, |
| 1875 | } | ||
| 1876 | wasm.data_segments.deinit(gpa); | ||
| 1877 | wasm.data_segments = new_mapping; | ||
| 1878 | } | ||
| 1879 | 1728 | ||
| 1880 | /// Obtains all initfuncs from each object file, verifies its function signature, | 1729 | pub const Off = enum(u32) { |
| 1881 | /// and then appends it to our final `init_funcs` list. | 1730 | /// The payload is all zeroes (bss section). |
| 1882 | /// After all functions have been inserted, the functions will be ordered based | 1731 | none = std.math.maxInt(u32), |
| 1883 | /// on their priority. | 1732 | /// Points into string_bytes. No corresponding string_table entry. |
| 1884 | /// NOTE: This function must be called before we merged any other section. | 1733 | _, |
| 1885 | /// This is because all init funcs in the object files contain references to the | ||
| 1886 | /// original functions and their types. We need to know the type to verify it doesn't | ||
| 1887 | /// contain any parameters. | ||
| 1888 | fn 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 | } | ||
| 1919 | } | ||
| 1920 | 1734 | ||
| 1921 | // sort the initfunctions based on their priority | 1735 | pub fn unwrap(off: Off) ?u32 { |
| 1922 | mem.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); | 1736 | return if (off == .none) null else @intFromEnum(off); |
| 1737 | } | ||
| 1738 | }; | ||
| 1923 | 1739 | ||
| 1924 | if (wasm.init_funcs.items.len > 0) { | 1740 | pub fn slice(p: DataPayload, wasm: *const Wasm) []const u8 { |
| 1925 | const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?; | 1741 | return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len]; |
| 1926 | try wasm.mark(loc); | ||
| 1927 | } | 1742 | } |
| 1928 | } | 1743 | }; |
| 1929 | 1744 | ||
| 1930 | /// Creates a function body for the `__wasm_call_ctors` symbol. | 1745 | /// A reference to a local or exported global const. |
| 1931 | /// Loops over all constructors found in `init_funcs` and calls them | 1746 | pub const DataSegmentId = enum(u32) { |
| 1932 | /// respectively based on their priority which was sorted by `setupInitFunctions`. | 1747 | __zig_error_names, |
| 1933 | /// NOTE: This function must be called after we merged all sections to ensure the | 1748 | __zig_error_name_table, |
| 1934 | /// references to the function stored in the symbol have been finalized so we end | 1749 | /// All name string bytes for all `@tagName` implementations, concatenated together. |
| 1935 | /// up calling the resolved function. | 1750 | __zig_tag_names, |
| 1936 | fn initializeCallCtorsFunction(wasm: *Wasm) !void { | 1751 | /// All tag name slices for all `@tagName` implementations, concatenated together. |
| 1937 | const gpa = wasm.base.comp.gpa; | 1752 | __zig_tag_name_table, |
| 1938 | // No code to emit, so also no ctors to call | 1753 | /// This and `__heap_end` are better retrieved via a global, but there is |
| 1939 | if (wasm.code_section_index == .none) { | 1754 | /// some suboptimal code out there (wasi libc) that additionally needs them |
| 1940 | // Make sure to remove it from the resolved symbols so we do not emit | 1755 | /// as data symbols. |
| 1941 | // it within any section. TODO: Remove this once we implement garbage collection. | 1756 | __heap_base, |
| 1942 | const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?; | 1757 | __heap_end, |
| 1943 | assert(wasm.resolved_symbols.swapRemove(loc)); | 1758 | /// First, an `ObjectDataSegment.Index`. |
| 1944 | return; | 1759 | /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. |
| 1945 | } | 1760 | /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. |
| 1761 | _, | ||
| 1946 | 1762 | ||
| 1947 | var function_body = std.ArrayList(u8).init(gpa); | 1763 | const first_object = @intFromEnum(DataSegmentId.__heap_end) + 1; |
| 1948 | defer function_body.deinit(); | ||
| 1949 | const writer = function_body.writer(); | ||
| 1950 | 1764 | ||
| 1951 | // Create the function body | 1765 | pub const Category = enum { |
| 1952 | { | 1766 | /// Thread-local variables. |
| 1953 | // Write locals count (we have none) | 1767 | tls, |
| 1954 | try leb.writeUleb128(writer, @as(u32, 0)); | 1768 | /// Data that is not zero initialized and not threadlocal. |
| 1769 | data, | ||
| 1770 | /// Zero-initialized. Does not require corresponding bytes in the | ||
| 1771 | /// output file. | ||
| 1772 | zero, | ||
| 1773 | }; | ||
| 1955 | 1774 | ||
| 1956 | // call constructors | 1775 | pub const Unpacked = union(enum) { |
| 1957 | for (wasm.init_funcs.items) |init_func_loc| { | 1776 | __zig_error_names, |
| 1958 | const symbol = init_func_loc.getSymbol(wasm); | 1777 | __zig_error_name_table, |
| 1959 | const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count].func; | 1778 | __zig_tag_names, |
| 1960 | const ty = wasm.func_types.items[func.type_index]; | 1779 | __zig_tag_name_table, |
| 1780 | __heap_base, | ||
| 1781 | __heap_end, | ||
| 1782 | object: ObjectDataSegment.Index, | ||
| 1783 | uav_exe: UavsExeIndex, | ||
| 1784 | uav_obj: UavsObjIndex, | ||
| 1785 | nav_exe: NavsExeIndex, | ||
| 1786 | nav_obj: NavsObjIndex, | ||
| 1787 | }; | ||
| 1961 | 1788 | ||
| 1962 | // Call function by its function index | 1789 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) DataSegmentId { |
| 1963 | try writer.writeByte(std.wasm.opcode(.call)); | 1790 | return switch (unpacked) { |
| 1964 | try leb.writeUleb128(writer, symbol.index); | 1791 | .__zig_error_names => .__zig_error_names, |
| 1792 | .__zig_error_name_table => .__zig_error_name_table, | ||
| 1793 | .__zig_tag_names => .__zig_tag_names, | ||
| 1794 | .__zig_tag_name_table => .__zig_tag_name_table, | ||
| 1795 | .__heap_base => .__heap_base, | ||
| 1796 | .__heap_end => .__heap_end, | ||
| 1797 | .object => |i| @enumFromInt(first_object + @intFromEnum(i)), | ||
| 1798 | inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + @intFromEnum(i)), | ||
| 1799 | .nav_exe => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)), | ||
| 1800 | .nav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)), | ||
| 1801 | }; | ||
| 1802 | } | ||
| 1965 | 1803 | ||
| 1966 | // drop all returned values from the stack as __wasm_call_ctors has no return value | 1804 | pub fn unpack(id: DataSegmentId, wasm: *const Wasm) Unpacked { |
| 1967 | for (ty.returns) |_| { | 1805 | return switch (id) { |
| 1968 | try writer.writeByte(std.wasm.opcode(.drop)); | 1806 | .__zig_error_names => .__zig_error_names, |
| 1969 | } | 1807 | .__zig_error_name_table => .__zig_error_name_table, |
| 1970 | } | 1808 | .__zig_tag_names => .__zig_tag_names, |
| 1809 | .__zig_tag_name_table => .__zig_tag_name_table, | ||
| 1810 | .__heap_base => .__heap_base, | ||
| 1811 | .__heap_end => .__heap_end, | ||
| 1812 | _ => { | ||
| 1813 | const object_index = @intFromEnum(id) - first_object; | ||
| 1971 | 1814 | ||
| 1972 | // End function body | 1815 | const uav_index = if (object_index < wasm.object_data_segments.items.len) |
| 1973 | try writer.writeByte(std.wasm.opcode(.end)); | 1816 | return .{ .object = @enumFromInt(object_index) } |
| 1974 | } | 1817 | else |
| 1818 | object_index - wasm.object_data_segments.items.len; | ||
| 1975 | 1819 | ||
| 1976 | try wasm.createSyntheticFunction( | 1820 | const comp = wasm.base.comp; |
| 1977 | wasm.preloaded_strings.__wasm_call_ctors, | 1821 | const is_obj = comp.config.output_mode == .Obj; |
| 1978 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, | 1822 | if (is_obj) { |
| 1979 | &function_body, | 1823 | const nav_index = if (uav_index < wasm.uavs_obj.entries.len) |
| 1980 | ); | 1824 | return .{ .uav_obj = @enumFromInt(uav_index) } |
| 1981 | } | 1825 | else |
| 1826 | uav_index - wasm.uavs_obj.entries.len; | ||
| 1982 | 1827 | ||
| 1983 | fn createSyntheticFunction( | 1828 | return .{ .nav_obj = @enumFromInt(nav_index) }; |
| 1984 | wasm: *Wasm, | 1829 | } else { |
| 1985 | symbol_name: String, | 1830 | const nav_index = if (uav_index < wasm.uavs_exe.entries.len) |
| 1986 | func_ty: std.wasm.Type, | 1831 | return .{ .uav_exe = @enumFromInt(uav_index) } |
| 1987 | function_body: *std.ArrayList(u8), | 1832 | else |
| 1988 | ) !void { | 1833 | uav_index - wasm.uavs_exe.entries.len; |
| 1989 | const gpa = wasm.base.comp.gpa; | 1834 | |
| 1990 | const loc = wasm.globals.get(symbol_name).?; | 1835 | return .{ .nav_exe = @enumFromInt(nav_index) }; |
| 1991 | const symbol = wasm.symbolLocSymbol(loc); | 1836 | } |
| 1992 | if (symbol.isDead()) { | 1837 | }, |
| 1993 | return; | 1838 | }; |
| 1994 | } | 1839 | } |
| 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 | } | ||
| 2012 | 1840 | ||
| 2013 | /// Unlike `createSyntheticFunction` this function is to be called by | 1841 | pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId { |
| 2014 | /// the codegeneration backend. This will not allocate the created Atom yet. | 1842 | const comp = wasm.base.comp; |
| 2015 | /// Returns the index of the symbol. | 1843 | const is_obj = comp.config.output_mode == .Obj; |
| 2016 | pub fn createFunction( | 1844 | return pack(wasm, if (is_obj) .{ |
| 2017 | wasm: *Wasm, | 1845 | .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(nav_index).?), |
| 2018 | symbol_name: []const u8, | 1846 | } else .{ |
| 2019 | func_ty: std.wasm.Type, | 1847 | .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?), |
| 2020 | function_body: *std.ArrayList(u8), | 1848 | }); |
| 2021 | relocations: *std.ArrayList(Relocation), | 1849 | } |
| 2022 | ) !Symbol.Index { | ||
| 2023 | return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations); | ||
| 2024 | } | ||
| 2025 | 1850 | ||
| 2026 | /// If required, sets the function index in the `start` section. | 1851 | pub fn fromObjectDataSegment(wasm: *const Wasm, object_data_segment: ObjectDataSegment.Index) DataSegmentId { |
| 2027 | fn setupStartSection(wasm: *Wasm) !void { | 1852 | return pack(wasm, .{ .object = object_data_segment }); |
| 2028 | if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory)) |loc| { | ||
| 2029 | wasm.entry = wasm.symbolLocSymbol(loc).index; | ||
| 2030 | } | 1853 | } |
| 2031 | } | ||
| 2032 | 1854 | ||
| 2033 | fn initializeTLSFunction(wasm: *Wasm) !void { | 1855 | pub fn category(id: DataSegmentId, wasm: *const Wasm) Category { |
| 2034 | const comp = wasm.base.comp; | 1856 | return switch (unpack(id, wasm)) { |
| 2035 | const gpa = comp.gpa; | 1857 | .__zig_error_names, |
| 2036 | const shared_memory = comp.config.shared_memory; | 1858 | .__zig_error_name_table, |
| 1859 | .__zig_tag_names, | ||
| 1860 | .__zig_tag_name_table, | ||
| 1861 | .__heap_base, | ||
| 1862 | .__heap_end, | ||
| 1863 | => .data, | ||
| 2037 | 1864 | ||
| 2038 | if (!shared_memory) return; | 1865 | .object => |i| { |
| 1866 | const ptr = i.ptr(wasm); | ||
| 1867 | if (ptr.flags.tls) return .tls; | ||
| 1868 | if (wasm.isBss(ptr.name)) return .zero; | ||
| 1869 | return .data; | ||
| 1870 | }, | ||
| 1871 | inline .uav_exe, .uav_obj => |i| if (i.value(wasm).code.off == .none) .zero else .data, | ||
| 1872 | inline .nav_exe, .nav_obj => |i| { | ||
| 1873 | const zcu = wasm.base.comp.zcu.?; | ||
| 1874 | const ip = &zcu.intern_pool; | ||
| 1875 | const nav = ip.getNav(i.key(wasm).*); | ||
| 1876 | if (nav.isThreadlocal(ip)) return .tls; | ||
| 1877 | const code = i.value(wasm).code; | ||
| 1878 | return if (code.off == .none) .zero else .data; | ||
| 1879 | }, | ||
| 1880 | }; | ||
| 1881 | } | ||
| 2039 | 1882 | ||
| 2040 | // ensure function is marked as we must emit it | 1883 | pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2041 | wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls).?).mark(); | 1884 | return switch (unpack(id, wasm)) { |
| 1885 | .__zig_error_names, | ||
| 1886 | .__zig_error_name_table, | ||
| 1887 | .__zig_tag_names, | ||
| 1888 | .__zig_tag_name_table, | ||
| 1889 | .__heap_base, | ||
| 1890 | .__heap_end, | ||
| 1891 | => false, | ||
| 1892 | |||
| 1893 | .object => |i| i.ptr(wasm).flags.tls, | ||
| 1894 | .uav_exe, .uav_obj => false, | ||
| 1895 | inline .nav_exe, .nav_obj => |i| { | ||
| 1896 | const zcu = wasm.base.comp.zcu.?; | ||
| 1897 | const ip = &zcu.intern_pool; | ||
| 1898 | const nav = ip.getNav(i.key(wasm).*); | ||
| 1899 | return nav.isThreadlocal(ip); | ||
| 1900 | }, | ||
| 1901 | }; | ||
| 1902 | } | ||
| 2042 | 1903 | ||
| 2043 | var function_body = std.ArrayList(u8).init(gpa); | 1904 | pub fn isBss(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2044 | defer function_body.deinit(); | 1905 | return id.category(wasm) == .zero; |
| 2045 | const writer = function_body.writer(); | 1906 | } |
| 1907 | |||
| 1908 | pub fn name(id: DataSegmentId, wasm: *const Wasm) []const u8 { | ||
| 1909 | return switch (unpack(id, wasm)) { | ||
| 1910 | .__zig_error_names, | ||
| 1911 | .__zig_error_name_table, | ||
| 1912 | .__zig_tag_names, | ||
| 1913 | .__zig_tag_name_table, | ||
| 1914 | .uav_exe, | ||
| 1915 | .uav_obj, | ||
| 1916 | .__heap_base, | ||
| 1917 | .__heap_end, | ||
| 1918 | => ".data", | ||
| 1919 | |||
| 1920 | .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm), | ||
| 1921 | inline .nav_exe, .nav_obj => |i| { | ||
| 1922 | const zcu = wasm.base.comp.zcu.?; | ||
| 1923 | const ip = &zcu.intern_pool; | ||
| 1924 | const nav = ip.getNav(i.key(wasm).*); | ||
| 1925 | return nav.getLinkSection().toSlice(ip) orelse switch (category(id, wasm)) { | ||
| 1926 | .tls => ".tdata", | ||
| 1927 | .data => ".data", | ||
| 1928 | .zero => ".bss", | ||
| 1929 | }; | ||
| 1930 | }, | ||
| 1931 | }; | ||
| 1932 | } | ||
| 2046 | 1933 | ||
| 2047 | // locals | 1934 | pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment { |
| 2048 | try writer.writeByte(0); | 1935 | return switch (unpack(id, wasm)) { |
| 1936 | .__zig_error_names, .__zig_tag_names => .@"1", | ||
| 1937 | .__zig_error_name_table, .__zig_tag_name_table, .__heap_base, .__heap_end => wasm.pointerAlignment(), | ||
| 1938 | .object => |i| i.ptr(wasm).flags.alignment, | ||
| 1939 | inline .uav_exe, .uav_obj => |i| { | ||
| 1940 | const zcu = wasm.base.comp.zcu.?; | ||
| 1941 | const ip = &zcu.intern_pool; | ||
| 1942 | const ip_index = i.key(wasm).*; | ||
| 1943 | if (wasm.overaligned_uavs.get(ip_index)) |a| return a; | ||
| 1944 | const ty: Zcu.Type = .fromInterned(ip.typeOf(ip_index)); | ||
| 1945 | const result = ty.abiAlignment(zcu); | ||
| 1946 | assert(result != .none); | ||
| 1947 | return result; | ||
| 1948 | }, | ||
| 1949 | inline .nav_exe, .nav_obj => |i| { | ||
| 1950 | const zcu = wasm.base.comp.zcu.?; | ||
| 1951 | const ip = &zcu.intern_pool; | ||
| 1952 | const nav = ip.getNav(i.key(wasm).*); | ||
| 1953 | const explicit = nav.getAlignment(); | ||
| 1954 | if (explicit != .none) return explicit; | ||
| 1955 | const ty: Zcu.Type = .fromInterned(nav.typeOf(ip)); | ||
| 1956 | const result = ty.abiAlignment(zcu); | ||
| 1957 | assert(result != .none); | ||
| 1958 | return result; | ||
| 1959 | }, | ||
| 1960 | }; | ||
| 1961 | } | ||
| 2049 | 1962 | ||
| 2050 | // If there's a TLS segment, initialize it during runtime using the bulk-memory feature | 1963 | pub fn refCount(id: DataSegmentId, wasm: *const Wasm) u32 { |
| 2051 | if (wasm.data_segments.getIndex(".tdata")) |data_index| { | 1964 | return switch (unpack(id, wasm)) { |
| 2052 | const segment_index = wasm.data_segments.entries.items(.value)[data_index]; | 1965 | .__zig_error_names => @intCast(wasm.error_name_offs.items.len), |
| 2053 | const segment = wasm.segmentPtr(segment_index); | 1966 | .__zig_error_name_table => wasm.error_name_table_ref_count, |
| 1967 | .__zig_tag_names => @intCast(wasm.tag_name_offs.items.len), | ||
| 1968 | .__zig_tag_name_table => wasm.tag_name_table_ref_count, | ||
| 1969 | .object, .uav_obj, .nav_obj, .__heap_base, .__heap_end => 0, | ||
| 1970 | inline .uav_exe, .nav_exe => |i| i.value(wasm).count, | ||
| 1971 | }; | ||
| 1972 | } | ||
| 2054 | 1973 | ||
| 2055 | const param_local: u32 = 0; | 1974 | pub fn isPassive(id: DataSegmentId, wasm: *const Wasm) bool { |
| 1975 | const comp = wasm.base.comp; | ||
| 1976 | if (comp.config.import_memory) return true; | ||
| 1977 | return switch (unpack(id, wasm)) { | ||
| 1978 | .__zig_error_names, | ||
| 1979 | .__zig_error_name_table, | ||
| 1980 | .__zig_tag_names, | ||
| 1981 | .__zig_tag_name_table, | ||
| 1982 | .__heap_base, | ||
| 1983 | .__heap_end, | ||
| 1984 | => false, | ||
| 1985 | |||
| 1986 | .object => |i| i.ptr(wasm).flags.is_passive, | ||
| 1987 | .uav_exe, .uav_obj, .nav_exe, .nav_obj => false, | ||
| 1988 | }; | ||
| 1989 | } | ||
| 2056 | 1990 | ||
| 2057 | try writer.writeByte(std.wasm.opcode(.local_get)); | 1991 | pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool { |
| 2058 | try leb.writeUleb128(writer, param_local); | 1992 | return switch (unpack(id, wasm)) { |
| 1993 | .__zig_error_names, | ||
| 1994 | .__zig_error_name_table, | ||
| 1995 | .__zig_tag_names, | ||
| 1996 | .__zig_tag_name_table, | ||
| 1997 | .__heap_base, | ||
| 1998 | .__heap_end, | ||
| 1999 | => false, | ||
| 2059 | 2000 | ||
| 2060 | const tls_base_loc = wasm.globals.get(wasm.preloaded_strings.__tls_base).?; | 2001 | .object => |i| i.ptr(wasm).payload.off == .none, |
| 2061 | try writer.writeByte(std.wasm.opcode(.global_set)); | 2002 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none, |
| 2062 | try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index); | 2003 | }; |
| 2004 | } | ||
| 2063 | 2005 | ||
| 2064 | // load stack values for the bulk-memory operation | 2006 | pub fn size(id: DataSegmentId, wasm: *const Wasm) u32 { |
| 2065 | { | 2007 | return switch (unpack(id, wasm)) { |
| 2066 | try writer.writeByte(std.wasm.opcode(.local_get)); | 2008 | .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), |
| 2067 | try leb.writeUleb128(writer, param_local); | 2009 | .__zig_error_name_table => { |
| 2010 | const comp = wasm.base.comp; | ||
| 2011 | const zcu = comp.zcu.?; | ||
| 2012 | const errors_len = wasm.error_name_offs.items.len; | ||
| 2013 | const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); | ||
| 2014 | return @intCast(errors_len * elem_size); | ||
| 2015 | }, | ||
| 2016 | .__zig_tag_names => @intCast(wasm.tag_name_bytes.items.len), | ||
| 2017 | .__zig_tag_name_table => { | ||
| 2018 | const comp = wasm.base.comp; | ||
| 2019 | const zcu = comp.zcu.?; | ||
| 2020 | const table_len = wasm.tag_name_offs.items.len; | ||
| 2021 | const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); | ||
| 2022 | return @intCast(table_len * elem_size); | ||
| 2023 | }, | ||
| 2024 | .__heap_base, .__heap_end => wasm.pointerSize(), | ||
| 2025 | .object => |i| i.ptr(wasm).payload.len, | ||
| 2026 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, | ||
| 2027 | }; | ||
| 2028 | } | ||
| 2029 | }; | ||
| 2068 | 2030 | ||
| 2069 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 2031 | pub const DataLoc = struct { |
| 2070 | try leb.writeUleb128(writer, @as(u32, 0)); //segment offset | 2032 | segment: Wasm.DataSegmentId, |
| 2033 | offset: u32, | ||
| 2071 | 2034 | ||
| 2072 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 2035 | pub fn fromObjectDataIndex(wasm: *const Wasm, i: Wasm.ObjectData.Index) DataLoc { |
| 2073 | try leb.writeUleb128(writer, @as(u32, segment.size)); //segment offset | 2036 | const ptr = i.ptr(wasm); |
| 2074 | } | 2037 | return .{ |
| 2038 | .segment = .fromObjectDataSegment(wasm, ptr.segment), | ||
| 2039 | .offset = ptr.offset, | ||
| 2040 | }; | ||
| 2041 | } | ||
| 2075 | 2042 | ||
| 2076 | // perform the bulk-memory operation to initialize the data segment | 2043 | pub fn fromDataImportId(wasm: *const Wasm, id: Wasm.DataImportId) DataLoc { |
| 2077 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | 2044 | return switch (id.unpack(wasm)) { |
| 2078 | try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init)); | 2045 | .object_data_import => |i| .fromObjectDataImportIndex(wasm, i), |
| 2079 | // segment immediate | 2046 | .zcu_import => |i| .fromZcuImport(wasm, i), |
| 2080 | try leb.writeUleb128(writer, @as(u32, @intCast(data_index))); | 2047 | }; |
| 2081 | // memory index immediate (always 0) | ||
| 2082 | try leb.writeUleb128(writer, @as(u32, 0)); | ||
| 2083 | } | 2048 | } |
| 2084 | 2049 | ||
| 2085 | // If we have to perform any TLS relocations, call the corresponding function | 2050 | pub fn fromObjectDataImportIndex(wasm: *const Wasm, i: Wasm.ObjectDataImport.Index) DataLoc { |
| 2086 | // which performs all runtime TLS relocations. This is a synthetic function, | 2051 | return i.value(wasm).resolution.dataLoc(wasm); |
| 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 | } | 2052 | } |
| 2093 | 2053 | ||
| 2094 | try writer.writeByte(std.wasm.opcode(.end)); | 2054 | pub fn fromZcuImport(wasm: *const Wasm, zcu_import: ZcuImportIndex) DataLoc { |
| 2055 | const nav_index = zcu_import.ptr(wasm).*; | ||
| 2056 | return .{ | ||
| 2057 | .segment = .fromNav(wasm, nav_index), | ||
| 2058 | .offset = 0, | ||
| 2059 | }; | ||
| 2060 | } | ||
| 2061 | }; | ||
| 2095 | 2062 | ||
| 2096 | try wasm.createSyntheticFunction( | 2063 | /// Index into `Wasm.uavs`. |
| 2097 | wasm.preloaded_strings.__wasm_init_tls, | 2064 | pub const UavIndex = enum(u32) { |
| 2098 | std.wasm.Type{ .params = &.{.i32}, .returns = &.{} }, | 2065 | _, |
| 2099 | &function_body, | 2066 | }; |
| 2100 | ); | ||
| 2101 | } | ||
| 2102 | 2067 | ||
| 2103 | fn setupImports(wasm: *Wasm) !void { | 2068 | pub const CustomSegment = extern struct { |
| 2104 | const gpa = wasm.base.comp.gpa; | 2069 | payload: Payload, |
| 2105 | log.debug("Merging imports", .{}); | 2070 | flags: SymbolFlags, |
| 2106 | for (wasm.resolved_symbols.keys()) |symbol_loc| { | 2071 | section_name: String, |
| 2107 | const object_id = symbol_loc.file.unwrap() orelse { | ||
| 2108 | // Synthetic symbols will already exist in the `import` section | ||
| 2109 | continue; | ||
| 2110 | }; | ||
| 2111 | 2072 | ||
| 2112 | const symbol = wasm.symbolLocSymbol(symbol_loc); | 2073 | pub const Payload = DataPayload; |
| 2113 | if (symbol.isDead()) continue; | 2074 | }; |
| 2114 | if (!symbol.requiresImport()) continue; | 2075 | |
| 2115 | if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue; | 2076 | /// An index into string_bytes where a wasm expression is found. |
| 2077 | pub const Expr = enum(u32) { | ||
| 2078 | _, | ||
| 2116 | 2079 | ||
| 2117 | log.debug("Symbol '{s}' will be imported from the host", .{wasm.stringSlice(symbol.name)}); | 2080 | pub const end = @intFromEnum(std.wasm.Opcode.end); |
| 2118 | const import = objectImport(wasm, object_id, symbol_loc.index); | ||
| 2119 | 2081 | ||
| 2120 | // We copy the import to a new import to ensure the names contain references | 2082 | pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 { |
| 2121 | // to the internal string table, rather than of the object file. | 2083 | const start_slice = wasm.string_bytes.items[@intFromEnum(index)..]; |
| 2122 | const new_imp: Import = .{ | 2084 | const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) { |
| 2123 | .module_name = import.module_name, | 2085 | error.InvalidInitOpcode => unreachable, |
| 2124 | .name = import.name, | ||
| 2125 | .kind = import.kind, | ||
| 2126 | }; | 2086 | }; |
| 2127 | // TODO: De-duplicate imports when they contain the same names and type | 2087 | return start_slice[0..end_pos :end]; |
| 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 | } | 2088 | } |
| 2155 | wasm.imported_functions_count = function_index; | 2089 | }; |
| 2156 | wasm.imported_globals_count = global_index; | ||
| 2157 | wasm.imported_tables_count = table_index; | ||
| 2158 | 2090 | ||
| 2159 | log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{ | 2091 | pub const FunctionType = extern struct { |
| 2160 | function_index, | 2092 | params: ValtypeList, |
| 2161 | global_index, | 2093 | returns: ValtypeList, |
| 2162 | table_index, | ||
| 2163 | }); | ||
| 2164 | } | ||
| 2165 | 2094 | ||
| 2166 | /// Takes the global, function and table section from each linked object file | 2095 | /// Index into func_types |
| 2167 | /// and merges it into a single section for each. | 2096 | pub const Index = enum(u32) { |
| 2168 | fn mergeSections(wasm: *Wasm) !void { | 2097 | _, |
| 2169 | const gpa = wasm.base.comp.gpa; | ||
| 2170 | 2098 | ||
| 2171 | var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa); | 2099 | pub fn ptr(i: Index, wasm: *const Wasm) *FunctionType { |
| 2172 | defer removed_duplicates.deinit(); | 2100 | return &wasm.func_types.keys()[@intFromEnum(i)]; |
| 2101 | } | ||
| 2173 | 2102 | ||
| 2174 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 2103 | pub fn fmt(i: Index, wasm: *const Wasm) Formatter { |
| 2175 | const object_id = sym_loc.file.unwrap() orelse { | 2104 | return i.ptr(wasm).fmt(wasm); |
| 2176 | // Synthetic symbols already live in the corresponding sections. | 2105 | } |
| 2177 | continue; | 2106 | }; |
| 2178 | }; | ||
| 2179 | 2107 | ||
| 2180 | const symbol = objectSymbol(wasm, object_id, sym_loc.index); | 2108 | pub const format = @compileError("can't format without *Wasm reference"); |
| 2181 | if (symbol.isDead() or symbol.isUndefined()) { | 2109 | |
| 2182 | // Skip undefined symbols as they go in the `import` section | 2110 | pub fn eql(a: FunctionType, b: FunctionType) bool { |
| 2183 | continue; | 2111 | return a.params == b.params and a.returns == b.returns; |
| 2184 | } | 2112 | } |
| 2185 | 2113 | ||
| 2186 | switch (symbol.tag) { | 2114 | pub fn fmt(ft: FunctionType, wasm: *const Wasm) Formatter { |
| 2187 | .function => { | 2115 | return .{ .wasm = wasm, .ft = ft }; |
| 2188 | const gop = try wasm.functions.getOrPut( | 2116 | } |
| 2189 | gpa, | 2117 | |
| 2190 | .{ .file = sym_loc.file, .index = symbol.index }, | 2118 | const Formatter = struct { |
| 2191 | ); | 2119 | wasm: *const Wasm, |
| 2192 | if (gop.found_existing) { | 2120 | ft: FunctionType, |
| 2193 | // We found an alias to the same function, discard this symbol in favor of | 2121 | |
| 2194 | // the original symbol and point the discard function to it. This ensures | 2122 | pub fn format( |
| 2195 | // we only emit a single function, instead of duplicates. | 2123 | self: Formatter, |
| 2196 | // we favor keeping the global over a local. | 2124 | comptime format_string: []const u8, |
| 2197 | const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index }; | 2125 | options: std.fmt.FormatOptions, |
| 2198 | const original_sym = wasm.symbolLocSymbol(original_loc); | 2126 | writer: anytype, |
| 2199 | if (original_sym.isLocal() and symbol.isGlobal()) { | 2127 | ) !void { |
| 2200 | original_sym.unmark(); | 2128 | if (format_string.len != 0) std.fmt.invalidFmtError(format_string, self); |
| 2201 | try wasm.discarded.put(gpa, original_loc, sym_loc); | 2129 | _ = options; |
| 2202 | try removed_duplicates.append(original_loc); | 2130 | const params = self.ft.params.slice(self.wasm); |
| 2203 | } else { | 2131 | const returns = self.ft.returns.slice(self.wasm); |
| 2204 | symbol.unmark(); | 2132 | |
| 2205 | try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc); | 2133 | try writer.writeByte('('); |
| 2206 | try removed_duplicates.append(sym_loc); | 2134 | for (params, 0..) |param, i| { |
| 2207 | continue; | 2135 | try writer.print("{s}", .{@tagName(param)}); |
| 2136 | if (i + 1 != params.len) { | ||
| 2137 | try writer.writeAll(", "); | ||
| 2138 | } | ||
| 2139 | } | ||
| 2140 | try writer.writeAll(") -> "); | ||
| 2141 | if (returns.len == 0) { | ||
| 2142 | try writer.writeAll("nil"); | ||
| 2143 | } else { | ||
| 2144 | for (returns, 0..) |return_ty, i| { | ||
| 2145 | try writer.print("{s}", .{@tagName(return_ty)}); | ||
| 2146 | if (i + 1 != returns.len) { | ||
| 2147 | try writer.writeAll(", "); | ||
| 2208 | } | 2148 | } |
| 2209 | } | 2149 | } |
| 2210 | gop.value_ptr.* = .{ | 2150 | } |
| 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 | } | 2151 | } |
| 2233 | } | 2152 | }; |
| 2153 | }; | ||
| 2234 | 2154 | ||
| 2235 | // For any removed duplicates, remove them from the resolved symbols list | 2155 | /// Represents a function entry, holding the index to its type |
| 2236 | for (removed_duplicates.items) |sym_loc| { | 2156 | pub const Func = extern struct { |
| 2237 | assert(wasm.resolved_symbols.swapRemove(sym_loc)); | 2157 | type_index: FunctionType.Index, |
| 2238 | gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)}); | 2158 | }; |
| 2239 | } | ||
| 2240 | 2159 | ||
| 2241 | log.debug("Merged ({d}) functions", .{wasm.functions.count()}); | 2160 | /// Type reflection is used on the field names to autopopulate each field |
| 2242 | log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len}); | 2161 | /// during initialization. |
| 2243 | log.debug("Merged ({d}) tables", .{wasm.tables.items.len}); | 2162 | const PreloadedStrings = struct { |
| 2244 | } | 2163 | __heap_base: String, |
| 2164 | __heap_end: String, | ||
| 2165 | __indirect_function_table: String, | ||
| 2166 | __linear_memory: String, | ||
| 2167 | __stack_pointer: String, | ||
| 2168 | __tls_align: String, | ||
| 2169 | __tls_base: String, | ||
| 2170 | __tls_size: String, | ||
| 2171 | __wasm_apply_global_tls_relocs: String, | ||
| 2172 | __wasm_call_ctors: String, | ||
| 2173 | __wasm_init_memory: String, | ||
| 2174 | __wasm_init_memory_flag: String, | ||
| 2175 | __wasm_init_tls: String, | ||
| 2176 | __zig_error_names: String, | ||
| 2177 | __zig_error_name_table: String, | ||
| 2178 | __zig_errors_len: String, | ||
| 2179 | _initialize: String, | ||
| 2180 | _start: String, | ||
| 2181 | memory: String, | ||
| 2182 | }; | ||
| 2245 | 2183 | ||
| 2246 | /// Merges function types of all object files into the final | 2184 | /// Index into string_bytes |
| 2247 | /// 'types' section, while assigning the type index to the representing | 2185 | pub const String = enum(u32) { |
| 2248 | /// section (import, export, function). | 2186 | _, |
| 2249 | fn mergeTypes(wasm: *Wasm) !void { | 2187 | |
| 2250 | const gpa = wasm.base.comp.gpa; | 2188 | const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage); |
| 2251 | // A map to track which functions have already had their | 2189 | |
| 2252 | // type inserted. If we do this for the same function multiple times, | 2190 | const TableContext = struct { |
| 2253 | // it will be overwritten with the incorrect type. | 2191 | bytes: []const u8, |
| 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 | 2192 | ||
| 2264 | const symbol = objectSymbol(wasm, object_id, sym_loc.index); | 2193 | pub fn eql(_: @This(), a: String, b: String) bool { |
| 2265 | if (symbol.tag != .function or symbol.isDead()) { | 2194 | return a == b; |
| 2266 | // Only functions have types. Only retrieve the type of referenced functions. | ||
| 2267 | continue; | ||
| 2268 | } | 2195 | } |
| 2269 | 2196 | ||
| 2270 | if (symbol.isUndefined()) { | 2197 | pub fn hash(ctx: @This(), key: String) u64 { |
| 2271 | log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)}); | 2198 | return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0)); |
| 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 | } | 2199 | } |
| 2281 | } | 2200 | }; |
| 2282 | log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len}); | ||
| 2283 | } | ||
| 2284 | 2201 | ||
| 2285 | fn checkExportNames(wasm: *Wasm) !void { | 2202 | const TableIndexAdapter = struct { |
| 2286 | const force_exp_names = wasm.export_symbol_names; | 2203 | bytes: []const u8, |
| 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 | 2204 | ||
| 2300 | const symbol = wasm.symbolLocSymbol(loc); | 2205 | pub fn eql(ctx: @This(), a: []const u8, b: String) bool { |
| 2301 | symbol.setFlag(.WASM_SYM_EXPORTED); | 2206 | return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0)); |
| 2302 | } | 2207 | } |
| 2303 | 2208 | ||
| 2304 | if (failed_exports) { | 2209 | pub fn hash(_: @This(), adapted_key: []const u8) u64 { |
| 2305 | return error.FlushFailure; | 2210 | assert(mem.indexOfScalar(u8, adapted_key, 0) == null); |
| 2211 | return std.hash_map.hashString(adapted_key); | ||
| 2306 | } | 2212 | } |
| 2213 | }; | ||
| 2214 | |||
| 2215 | pub fn slice(index: String, wasm: *const Wasm) [:0]const u8 { | ||
| 2216 | const start_slice = wasm.string_bytes.items[@intFromEnum(index)..]; | ||
| 2217 | return start_slice[0..mem.indexOfScalar(u8, start_slice, 0).? :0]; | ||
| 2307 | } | 2218 | } |
| 2308 | } | ||
| 2309 | 2219 | ||
| 2310 | fn setupExports(wasm: *Wasm) !void { | 2220 | pub fn toOptional(i: String) OptionalString { |
| 2311 | const comp = wasm.base.comp; | 2221 | const result: OptionalString = @enumFromInt(@intFromEnum(i)); |
| 2312 | const gpa = comp.gpa; | 2222 | assert(result != .none); |
| 2313 | if (comp.config.output_mode == .Obj) return; | 2223 | return result; |
| 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 | } | 2224 | } |
| 2225 | }; | ||
| 2343 | 2226 | ||
| 2344 | log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len}); | 2227 | pub const OptionalString = enum(u32) { |
| 2345 | } | 2228 | none = std.math.maxInt(u32), |
| 2229 | _, | ||
| 2346 | 2230 | ||
| 2347 | fn setupStart(wasm: *Wasm) !void { | 2231 | pub fn unwrap(i: OptionalString) ?String { |
| 2348 | const comp = wasm.base.comp; | 2232 | if (i == .none) return null; |
| 2349 | const diags = &wasm.base.comp.link_diags; | 2233 | return @enumFromInt(@intFromEnum(i)); |
| 2350 | // do not export entry point if user set none or no default was set. | 2234 | } |
| 2351 | const entry_name = wasm.entry_name.unwrap() orelse return; | 2235 | |
| 2352 | 2236 | pub fn slice(index: OptionalString, wasm: *const Wasm) ?[:0]const u8 { | |
| 2353 | const symbol_loc = wasm.globals.get(entry_name) orelse { | 2237 | return (index.unwrap() orelse return null).slice(wasm); |
| 2354 | var err = try diags.addErrorWithNotes(1); | 2238 | } |
| 2355 | try err.addMsg("entry symbol '{s}' missing", .{wasm.stringSlice(entry_name)}); | 2239 | }; |
| 2356 | try err.addNote("'-fno-entry' suppresses this error", .{}); | ||
| 2357 | return error.LinkFailure; | ||
| 2358 | }; | ||
| 2359 | 2240 | ||
| 2360 | const symbol = wasm.symbolLocSymbol(symbol_loc); | 2241 | /// Stored identically to `String`. The bytes are reinterpreted as |
| 2361 | if (symbol.tag != .function) | 2242 | /// `std.wasm.Valtype` elements. |
| 2362 | return diags.fail("entry symbol '{s}' is not a function", .{wasm.stringSlice(entry_name)}); | 2243 | pub const ValtypeList = enum(u32) { |
| 2244 | _, | ||
| 2363 | 2245 | ||
| 2364 | // Ensure the symbol is exported so host environment can access it | 2246 | pub fn fromString(s: String) ValtypeList { |
| 2365 | if (comp.config.output_mode != .Obj) { | 2247 | return @enumFromInt(@intFromEnum(s)); |
| 2366 | symbol.setFlag(.WASM_SYM_EXPORTED); | ||
| 2367 | } | 2248 | } |
| 2368 | } | ||
| 2369 | 2249 | ||
| 2370 | /// Sets up the memory section of the wasm module, as well as the stack. | 2250 | pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype { |
| 2371 | fn setupMemory(wasm: *Wasm) !void { | 2251 | return @ptrCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm)); |
| 2372 | const comp = wasm.base.comp; | 2252 | } |
| 2373 | const diags = &wasm.base.comp.link_diags; | 2253 | }; |
| 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 | 2254 | ||
| 2388 | const is_obj = comp.config.output_mode == .Obj; | 2255 | /// Index into `Wasm.imports`. |
| 2256 | pub const ZcuImportIndex = enum(u32) { | ||
| 2257 | _, | ||
| 2389 | 2258 | ||
| 2390 | const stack_ptr = if (wasm.globals.get(wasm.preloaded_strings.__stack_pointer)) |loc| index: { | 2259 | pub fn ptr(index: ZcuImportIndex, wasm: *const Wasm) *InternPool.Nav.Index { |
| 2391 | const sym = wasm.symbolLocSymbol(loc); | 2260 | return &wasm.imports.keys()[@intFromEnum(index)]; |
| 2392 | break :index sym.index - wasm.imported_globals_count; | 2261 | } |
| 2393 | } else null; | ||
| 2394 | 2262 | ||
| 2395 | if (place_stack_first and !is_obj) { | 2263 | pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String { |
| 2396 | memory_ptr = stack_alignment.forward(memory_ptr); | 2264 | const zcu = wasm.base.comp.zcu.?; |
| 2397 | memory_ptr += wasm.base.stack_size; | 2265 | const ip = &zcu.intern_pool; |
| 2398 | // We always put the stack pointer global at index 0 | 2266 | const nav_index = index.ptr(wasm).*; |
| 2399 | if (stack_ptr) |index| { | 2267 | const ext = ip.getNav(nav_index).getResolvedExtern(ip).?; |
| 2400 | wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); | 2268 | const name_slice = ext.name.toSlice(ip); |
| 2401 | } | 2269 | return wasm.getExistingString(name_slice).?; |
| 2402 | } | 2270 | } |
| 2403 | 2271 | ||
| 2404 | var offset: u32 = @as(u32, @intCast(memory_ptr)); | 2272 | pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString { |
| 2405 | var data_seg_it = wasm.data_segments.iterator(); | 2273 | const zcu = wasm.base.comp.zcu.?; |
| 2406 | while (data_seg_it.next()) |entry| { | 2274 | const ip = &zcu.intern_pool; |
| 2407 | const segment = wasm.segmentPtr(entry.value_ptr.*); | 2275 | const nav_index = index.ptr(wasm).*; |
| 2408 | memory_ptr = segment.alignment.forward(memory_ptr); | 2276 | const ext = ip.getNav(nav_index).getResolvedExtern(ip).?; |
| 2277 | const lib_name = ext.lib_name.toSlice(ip) orelse return .none; | ||
| 2278 | return wasm.getExistingString(lib_name).?.toOptional(); | ||
| 2279 | } | ||
| 2409 | 2280 | ||
| 2410 | // set TLS-related symbols | 2281 | pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index { |
| 2411 | if (mem.eql(u8, entry.key_ptr.*, ".tdata")) { | 2282 | const comp = wasm.base.comp; |
| 2412 | if (wasm.globals.get(wasm.preloaded_strings.__tls_size)) |loc| { | 2283 | const target = &comp.root_mod.resolved_target.result; |
| 2413 | const sym = wasm.symbolLocSymbol(loc); | 2284 | const zcu = comp.zcu.?; |
| 2414 | wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size); | 2285 | const ip = &zcu.intern_pool; |
| 2415 | } | 2286 | const nav_index = index.ptr(wasm).*; |
| 2416 | if (wasm.globals.get(wasm.preloaded_strings.__tls_align)) |loc| { | 2287 | const ext = ip.getNav(nav_index).getResolvedExtern(ip).?; |
| 2417 | const sym = wasm.symbolLocSymbol(loc); | 2288 | const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?; |
| 2418 | wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?); | 2289 | return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?; |
| 2419 | } | 2290 | } |
| 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 | 2291 | ||
| 2429 | memory_ptr += segment.size; | 2292 | pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type { |
| 2430 | segment.offset = offset; | 2293 | _ = index; |
| 2431 | offset += segment.size; | 2294 | _ = wasm; |
| 2295 | unreachable; // Zig has no way to create Wasm globals yet. | ||
| 2432 | } | 2296 | } |
| 2297 | }; | ||
| 2433 | 2298 | ||
| 2434 | // create the memory init flag which is used by the init memory function | 2299 | /// 0. Index into `Wasm.object_function_imports`. |
| 2435 | if (shared_memory and wasm.hasPassiveInitializationSegments()) { | 2300 | /// 1. Index into `Wasm.imports`. |
| 2436 | // align to pointer size | 2301 | pub const FunctionImportId = enum(u32) { |
| 2437 | memory_ptr = mem.alignForward(u64, memory_ptr, 4); | 2302 | _, |
| 2438 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory_flag, .data); | 2303 | |
| 2439 | const sym = wasm.symbolLocSymbol(loc); | 2304 | pub const Unpacked = union(enum) { |
| 2440 | sym.mark(); | 2305 | object_function_import: FunctionImport.Index, |
| 2441 | sym.virtual_address = @as(u32, @intCast(memory_ptr)); | 2306 | zcu_import: ZcuImportIndex, |
| 2442 | memory_ptr += 4; | 2307 | }; |
| 2308 | |||
| 2309 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) FunctionImportId { | ||
| 2310 | return switch (unpacked) { | ||
| 2311 | .object_function_import => |i| @enumFromInt(@intFromEnum(i)), | ||
| 2312 | .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_function_imports.entries.len), | ||
| 2313 | }; | ||
| 2443 | } | 2314 | } |
| 2444 | 2315 | ||
| 2445 | if (!place_stack_first and !is_obj) { | 2316 | pub fn unpack(id: FunctionImportId, wasm: *const Wasm) Unpacked { |
| 2446 | memory_ptr = stack_alignment.forward(memory_ptr); | 2317 | const i = @intFromEnum(id); |
| 2447 | memory_ptr += wasm.base.stack_size; | 2318 | if (i < wasm.object_function_imports.entries.len) return .{ .object_function_import = @enumFromInt(i) }; |
| 2448 | if (stack_ptr) |index| { | 2319 | const zcu_import_i = i - wasm.object_function_imports.entries.len; |
| 2449 | wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); | 2320 | return .{ .zcu_import = @enumFromInt(zcu_import_i) }; |
| 2450 | } | ||
| 2451 | } | 2321 | } |
| 2452 | 2322 | ||
| 2453 | // One of the linked object files has a reference to the __heap_base symbol. | 2323 | pub fn fromObject(function_import_index: FunctionImport.Index, wasm: *const Wasm) FunctionImportId { |
| 2454 | // We must set its virtual address so it can be used in relocations. | 2324 | return pack(.{ .object_function_import = function_import_index }, wasm); |
| 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 | } | 2325 | } |
| 2459 | 2326 | ||
| 2460 | // Setup the max amount of pages | 2327 | pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId { |
| 2461 | // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 | 2328 | return pack(.{ .zcu_import = zcu_import }, wasm); |
| 2462 | const max_memory_allowed: u64 = (1 << 32) - 1; | 2329 | } |
| 2463 | 2330 | ||
| 2464 | if (wasm.initial_memory) |initial_memory| { | 2331 | /// This function is allowed O(N) lookup because it is only called during |
| 2465 | if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { | 2332 | /// diagnostic generation. |
| 2466 | var err = try diags.addErrorWithNotes(0); | 2333 | pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation { |
| 2467 | try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size}); | 2334 | switch (id.unpack(wasm)) { |
| 2468 | } | 2335 | .object_function_import => |obj_func_index| { |
| 2469 | if (memory_ptr > initial_memory) { | 2336 | // TODO binary search |
| 2470 | var err = try diags.addErrorWithNotes(0); | 2337 | for (wasm.objects.items, 0..) |o, i| { |
| 2471 | try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr}); | 2338 | if (o.function_imports.off <= @intFromEnum(obj_func_index) and |
| 2472 | } | 2339 | o.function_imports.off + o.function_imports.len > @intFromEnum(obj_func_index)) |
| 2473 | if (initial_memory > max_memory_allowed) { | 2340 | { |
| 2474 | var err = try diags.addErrorWithNotes(0); | 2341 | return .pack(.{ .object_index = @enumFromInt(i) }, wasm); |
| 2475 | try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed}); | 2342 | } |
| 2343 | } else unreachable; | ||
| 2344 | }, | ||
| 2345 | .zcu_import => return .zig_object_nofile, // TODO give a better source location | ||
| 2476 | } | 2346 | } |
| 2477 | memory_ptr = initial_memory; | ||
| 2478 | } | 2347 | } |
| 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 | 2348 | ||
| 2485 | if (wasm.globals.get(wasm.preloaded_strings.__heap_end)) |loc| { | 2349 | pub fn importName(id: FunctionImportId, wasm: *const Wasm) String { |
| 2486 | const symbol = wasm.symbolLocSymbol(loc); | 2350 | return switch (unpack(id, wasm)) { |
| 2487 | symbol.virtual_address = @as(u32, @intCast(memory_ptr)); | 2351 | inline .object_function_import, .zcu_import => |i| i.importName(wasm), |
| 2352 | }; | ||
| 2488 | } | 2353 | } |
| 2489 | 2354 | ||
| 2490 | if (wasm.max_memory) |max_memory| { | 2355 | pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString { |
| 2491 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { | 2356 | return switch (unpack(id, wasm)) { |
| 2492 | var err = try diags.addErrorWithNotes(0); | 2357 | inline .object_function_import, .zcu_import => |i| i.moduleName(wasm), |
| 2493 | try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size}); | 2358 | }; |
| 2494 | } | 2359 | } |
| 2495 | if (memory_ptr > max_memory) { | 2360 | |
| 2496 | var err = try diags.addErrorWithNotes(0); | 2361 | pub fn functionType(id: FunctionImportId, wasm: *Wasm) FunctionType.Index { |
| 2497 | try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr}); | 2362 | return switch (unpack(id, wasm)) { |
| 2498 | } | 2363 | inline .object_function_import, .zcu_import => |i| i.functionType(wasm), |
| 2499 | if (max_memory > max_memory_allowed) { | 2364 | }; |
| 2500 | var err = try diags.addErrorWithNotes(0); | 2365 | } |
| 2501 | try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed}); | 2366 | |
| 2502 | } | 2367 | /// Asserts not emitting an object, and `Wasm.import_symbols` is false. |
| 2503 | wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); | 2368 | pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool { |
| 2504 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); | 2369 | assert(!wasm.import_symbols); |
| 2505 | if (shared_memory) { | 2370 | assert(wasm.base.comp.config.output_mode != .Obj); |
| 2506 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED); | 2371 | return switch (unpack(id, wasm)) { |
| 2372 | .object_function_import => |i| { | ||
| 2373 | const import = i.value(wasm); | ||
| 2374 | return import.flags.binding == .strong and import.module_name != .none; | ||
| 2375 | }, | ||
| 2376 | .zcu_import => |i| { | ||
| 2377 | const zcu = wasm.base.comp.zcu.?; | ||
| 2378 | const ip = &zcu.intern_pool; | ||
| 2379 | const ext = ip.getNav(i.ptr(wasm).*).getResolvedExtern(ip).?; | ||
| 2380 | return !ext.is_weak_linkage and ext.lib_name != .none; | ||
| 2381 | }, | ||
| 2382 | }; | ||
| 2383 | } | ||
| 2384 | }; | ||
| 2385 | |||
| 2386 | /// 0. Index into `object_global_imports`. | ||
| 2387 | /// 1. Index into `imports`. | ||
| 2388 | pub const GlobalImportId = enum(u32) { | ||
| 2389 | _, | ||
| 2390 | |||
| 2391 | pub const Unpacked = union(enum) { | ||
| 2392 | object_global_import: GlobalImport.Index, | ||
| 2393 | zcu_import: ZcuImportIndex, | ||
| 2394 | }; | ||
| 2395 | |||
| 2396 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) GlobalImportId { | ||
| 2397 | return switch (unpacked) { | ||
| 2398 | .object_global_import => |i| @enumFromInt(@intFromEnum(i)), | ||
| 2399 | .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_global_imports.entries.len), | ||
| 2400 | }; | ||
| 2401 | } | ||
| 2402 | |||
| 2403 | pub fn unpack(id: GlobalImportId, wasm: *const Wasm) Unpacked { | ||
| 2404 | const i = @intFromEnum(id); | ||
| 2405 | if (i < wasm.object_global_imports.entries.len) return .{ .object_global_import = @enumFromInt(i) }; | ||
| 2406 | const zcu_import_i = i - wasm.object_global_imports.entries.len; | ||
| 2407 | return .{ .zcu_import = @enumFromInt(zcu_import_i) }; | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | pub fn fromObject(object_global_import: GlobalImport.Index, wasm: *const Wasm) GlobalImportId { | ||
| 2411 | return pack(.{ .object_global_import = object_global_import }, wasm); | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | /// This function is allowed O(N) lookup because it is only called during | ||
| 2415 | /// diagnostic generation. | ||
| 2416 | pub fn sourceLocation(id: GlobalImportId, wasm: *const Wasm) SourceLocation { | ||
| 2417 | switch (id.unpack(wasm)) { | ||
| 2418 | .object_global_import => |obj_global_index| { | ||
| 2419 | // TODO binary search | ||
| 2420 | for (wasm.objects.items, 0..) |o, i| { | ||
| 2421 | if (o.global_imports.off <= @intFromEnum(obj_global_index) and | ||
| 2422 | o.global_imports.off + o.global_imports.len > @intFromEnum(obj_global_index)) | ||
| 2423 | { | ||
| 2424 | return .pack(.{ .object_index = @enumFromInt(i) }, wasm); | ||
| 2425 | } | ||
| 2426 | } else unreachable; | ||
| 2427 | }, | ||
| 2428 | .zcu_import => return .zig_object_nofile, // TODO give a better source location | ||
| 2507 | } | 2429 | } |
| 2508 | log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max}); | ||
| 2509 | } | 2430 | } |
| 2510 | } | ||
| 2511 | 2431 | ||
| 2512 | /// From a given object's index and the index of the segment, returns the corresponding | 2432 | pub fn importName(id: GlobalImportId, wasm: *const Wasm) String { |
| 2513 | /// index of the segment within the final data section. When the segment does not yet | 2433 | return switch (unpack(id, wasm)) { |
| 2514 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. | 2434 | inline .object_global_import, .zcu_import => |i| i.importName(wasm), |
| 2515 | pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Segment.Index { | 2435 | }; |
| 2516 | const comp = wasm.base.comp; | 2436 | } |
| 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 | 2437 | ||
| 2523 | switch (symbol.tag) { | 2438 | pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString { |
| 2524 | .data => { | 2439 | return switch (unpack(id, wasm)) { |
| 2525 | const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index]; | 2440 | inline .object_global_import, .zcu_import => |i| i.moduleName(wasm), |
| 2526 | const merge_segment = comp.config.output_mode != .Obj; | 2441 | }; |
| 2527 | const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); | 2442 | } |
| 2528 | if (!result.found_existing) { | 2443 | |
| 2529 | result.value_ptr.* = index; | 2444 | pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type { |
| 2530 | var flags: u32 = 0; | 2445 | return switch (unpack(id, wasm)) { |
| 2531 | if (shared_memory) { | 2446 | inline .object_global_import, .zcu_import => |i| i.globalType(wasm), |
| 2532 | flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); | 2447 | }; |
| 2533 | } | 2448 | } |
| 2534 | try wasm.segments.append(gpa, .{ | 2449 | }; |
| 2535 | .alignment = .@"1", | 2450 | |
| 2536 | .size = 0, | 2451 | /// 0. Index into `Wasm.object_data_imports`. |
| 2537 | .offset = 0, | 2452 | /// 1. Index into `Wasm.imports`. |
| 2538 | .flags = flags, | 2453 | pub const DataImportId = enum(u32) { |
| 2539 | }); | 2454 | _, |
| 2540 | try wasm.segment_info.putNoClobber(gpa, index, .{ | 2455 | |
| 2541 | .name = try gpa.dupe(u8, segment_info.name), | 2456 | pub const Unpacked = union(enum) { |
| 2542 | .alignment = segment_info.alignment, | 2457 | object_data_import: ObjectDataImport.Index, |
| 2543 | .flags = segment_info.flags, | 2458 | zcu_import: ZcuImportIndex, |
| 2544 | }); | 2459 | }; |
| 2545 | return index; | 2460 | |
| 2546 | } else return result.value_ptr.*; | 2461 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId { |
| 2547 | }, | 2462 | return switch (unpacked) { |
| 2548 | .function => return wasm.code_section_index.unwrap() orelse blk: { | 2463 | .object_data_import => |i| @enumFromInt(@intFromEnum(i)), |
| 2549 | wasm.code_section_index = index.toOptional(); | 2464 | .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_data_imports.entries.len), |
| 2550 | try wasm.appendDummySegment(); | 2465 | }; |
| 2551 | break :blk index; | 2466 | } |
| 2552 | }, | 2467 | |
| 2553 | .section => { | 2468 | pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked { |
| 2554 | const section_name = wasm.objectSymbol(object_id, symbol_index).name; | 2469 | const i = @intFromEnum(id); |
| 2555 | 2470 | if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @enumFromInt(i) }; | |
| 2556 | inline for (@typeInfo(CustomSections).@"struct".fields) |field| { | 2471 | const zcu_import_i = i - wasm.object_data_imports.entries.len; |
| 2557 | if (@field(wasm.custom_sections, field.name).name == section_name) { | 2472 | return .{ .zcu_import = @enumFromInt(zcu_import_i) }; |
| 2558 | const field_ptr = &@field(wasm.custom_sections, field.name).index; | 2473 | } |
| 2559 | return field_ptr.unwrap() orelse { | 2474 | |
| 2560 | field_ptr.* = index.toOptional(); | 2475 | pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId { |
| 2561 | try wasm.appendDummySegment(); | 2476 | return pack(.{ .zcu_import = zcu_import }, wasm); |
| 2562 | return index; | 2477 | } |
| 2563 | }; | 2478 | |
| 2564 | } | 2479 | pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId { |
| 2565 | } else { | 2480 | return pack(.{ .object_data_import = object_data_import }, wasm); |
| 2566 | return diags.failParse(objectPath(wasm, object_id), "unknown section: {s}", .{ | 2481 | } |
| 2567 | wasm.stringSlice(section_name), | 2482 | |
| 2568 | }); | 2483 | pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation { |
| 2569 | } | 2484 | switch (id.unpack(wasm)) { |
| 2485 | .object_data_import => |obj_data_index| { | ||
| 2486 | // TODO binary search | ||
| 2487 | for (wasm.objects.items, 0..) |o, i| { | ||
| 2488 | if (o.data_imports.off <= @intFromEnum(obj_data_index) and | ||
| 2489 | o.data_imports.off + o.data_imports.len > @intFromEnum(obj_data_index)) | ||
| 2490 | { | ||
| 2491 | return .pack(.{ .object_index = @enumFromInt(i) }, wasm); | ||
| 2492 | } | ||
| 2493 | } else unreachable; | ||
| 2494 | }, | ||
| 2495 | .zcu_import => return .zig_object_nofile, // TODO give a better source location | ||
| 2496 | } | ||
| 2497 | } | ||
| 2498 | }; | ||
| 2499 | |||
| 2500 | /// Index into `Wasm.symbol_table`. | ||
| 2501 | pub const SymbolTableIndex = enum(u32) { | ||
| 2502 | _, | ||
| 2503 | |||
| 2504 | pub fn key(i: @This(), wasm: *const Wasm) *String { | ||
| 2505 | return &wasm.symbol_table.keys()[@intFromEnum(i)]; | ||
| 2506 | } | ||
| 2507 | }; | ||
| 2508 | |||
| 2509 | pub const OutReloc = struct { | ||
| 2510 | tag: Object.RelocationType, | ||
| 2511 | offset: u32, | ||
| 2512 | pointee: Pointee, | ||
| 2513 | addend: i32, | ||
| 2514 | |||
| 2515 | pub const Pointee = union { | ||
| 2516 | symbol_index: SymbolTableIndex, | ||
| 2517 | type_index: FunctionType.Index, | ||
| 2518 | }; | ||
| 2519 | |||
| 2520 | pub const Slice = extern struct { | ||
| 2521 | /// Index into `out_relocs`. | ||
| 2522 | off: u32, | ||
| 2523 | len: u32, | ||
| 2524 | |||
| 2525 | pub fn slice(s: Slice, wasm: *const Wasm) []OutReloc { | ||
| 2526 | return wasm.relocations.items[s.off..][0..s.len]; | ||
| 2527 | } | ||
| 2528 | }; | ||
| 2529 | }; | ||
| 2530 | |||
| 2531 | pub const ObjectRelocation = struct { | ||
| 2532 | tag: Tag, | ||
| 2533 | /// Offset of the value to rewrite relative to the relevant section's contents. | ||
| 2534 | /// When `offset` is zero, its position is immediately after the id and size of the section. | ||
| 2535 | offset: u32, | ||
| 2536 | pointee: Pointee, | ||
| 2537 | /// Populated only for `memory_addr_*`, `function_offset_i32` and `section_offset_i32`. | ||
| 2538 | addend: i32, | ||
| 2539 | |||
| 2540 | pub const Tag = enum(u8) { | ||
| 2541 | // These use `Pointee.function`. | ||
| 2542 | function_index_i32, | ||
| 2543 | function_index_leb, | ||
| 2544 | function_offset_i32, | ||
| 2545 | function_offset_i64, | ||
| 2546 | table_index_i32, | ||
| 2547 | table_index_i64, | ||
| 2548 | table_index_rel_sleb, | ||
| 2549 | table_index_rel_sleb64, | ||
| 2550 | table_index_sleb, | ||
| 2551 | table_index_sleb64, | ||
| 2552 | // These use `Pointee.symbol_name`. | ||
| 2553 | function_import_index_i32, | ||
| 2554 | function_import_index_leb, | ||
| 2555 | function_import_offset_i32, | ||
| 2556 | function_import_offset_i64, | ||
| 2557 | table_import_index_i32, | ||
| 2558 | table_import_index_i64, | ||
| 2559 | table_import_index_rel_sleb, | ||
| 2560 | table_import_index_rel_sleb64, | ||
| 2561 | table_import_index_sleb, | ||
| 2562 | table_import_index_sleb64, | ||
| 2563 | // These use `Pointee.global`. | ||
| 2564 | global_index_i32, | ||
| 2565 | global_index_leb, | ||
| 2566 | // These use `Pointee.symbol_name`. | ||
| 2567 | global_import_index_i32, | ||
| 2568 | global_import_index_leb, | ||
| 2569 | // These use `Pointee.data`. | ||
| 2570 | memory_addr_i32, | ||
| 2571 | memory_addr_i64, | ||
| 2572 | memory_addr_leb, | ||
| 2573 | memory_addr_leb64, | ||
| 2574 | memory_addr_locrel_i32, | ||
| 2575 | memory_addr_rel_sleb, | ||
| 2576 | memory_addr_rel_sleb64, | ||
| 2577 | memory_addr_sleb, | ||
| 2578 | memory_addr_sleb64, | ||
| 2579 | memory_addr_tls_sleb, | ||
| 2580 | memory_addr_tls_sleb64, | ||
| 2581 | // These use `Pointee.symbol_name`. | ||
| 2582 | memory_addr_import_i32, | ||
| 2583 | memory_addr_import_i64, | ||
| 2584 | memory_addr_import_leb, | ||
| 2585 | memory_addr_import_leb64, | ||
| 2586 | memory_addr_import_locrel_i32, | ||
| 2587 | memory_addr_import_rel_sleb, | ||
| 2588 | memory_addr_import_rel_sleb64, | ||
| 2589 | memory_addr_import_sleb, | ||
| 2590 | memory_addr_import_sleb64, | ||
| 2591 | memory_addr_import_tls_sleb, | ||
| 2592 | memory_addr_import_tls_sleb64, | ||
| 2593 | /// Uses `Pointee.section`. | ||
| 2594 | section_offset_i32, | ||
| 2595 | /// Uses `Pointee.table`. | ||
| 2596 | table_number_leb, | ||
| 2597 | /// Uses `Pointee.symbol_name`. | ||
| 2598 | table_import_number_leb, | ||
| 2599 | /// Uses `Pointee.type_index`. | ||
| 2600 | type_index_leb, | ||
| 2601 | |||
| 2602 | pub fn fromType(t: Object.RelocationType) Tag { | ||
| 2603 | return switch (t) { | ||
| 2604 | .event_index_leb => unreachable, | ||
| 2605 | .function_index_i32 => .function_index_i32, | ||
| 2606 | .function_index_leb => .function_index_leb, | ||
| 2607 | .function_offset_i32 => .function_offset_i32, | ||
| 2608 | .function_offset_i64 => .function_offset_i64, | ||
| 2609 | .global_index_i32 => .global_index_i32, | ||
| 2610 | .global_index_leb => .global_index_leb, | ||
| 2611 | .memory_addr_i32 => .memory_addr_i32, | ||
| 2612 | .memory_addr_i64 => .memory_addr_i64, | ||
| 2613 | .memory_addr_leb => .memory_addr_leb, | ||
| 2614 | .memory_addr_leb64 => .memory_addr_leb64, | ||
| 2615 | .memory_addr_locrel_i32 => .memory_addr_locrel_i32, | ||
| 2616 | .memory_addr_rel_sleb => .memory_addr_rel_sleb, | ||
| 2617 | .memory_addr_rel_sleb64 => .memory_addr_rel_sleb64, | ||
| 2618 | .memory_addr_sleb => .memory_addr_sleb, | ||
| 2619 | .memory_addr_sleb64 => .memory_addr_sleb64, | ||
| 2620 | .memory_addr_tls_sleb => .memory_addr_tls_sleb, | ||
| 2621 | .memory_addr_tls_sleb64 => .memory_addr_tls_sleb64, | ||
| 2622 | .section_offset_i32 => .section_offset_i32, | ||
| 2623 | .table_index_i32 => .table_index_i32, | ||
| 2624 | .table_index_i64 => .table_index_i64, | ||
| 2625 | .table_index_rel_sleb => .table_index_rel_sleb, | ||
| 2626 | .table_index_rel_sleb64 => .table_index_rel_sleb64, | ||
| 2627 | .table_index_sleb => .table_index_sleb, | ||
| 2628 | .table_index_sleb64 => .table_index_sleb64, | ||
| 2629 | .table_number_leb => .table_number_leb, | ||
| 2630 | .type_index_leb => .type_index_leb, | ||
| 2631 | }; | ||
| 2632 | } | ||
| 2633 | |||
| 2634 | pub fn fromTypeImport(t: Object.RelocationType) Tag { | ||
| 2635 | return switch (t) { | ||
| 2636 | .event_index_leb => unreachable, | ||
| 2637 | .function_index_i32 => .function_import_index_i32, | ||
| 2638 | .function_index_leb => .function_import_index_leb, | ||
| 2639 | .function_offset_i32 => .function_import_offset_i32, | ||
| 2640 | .function_offset_i64 => .function_import_offset_i64, | ||
| 2641 | .global_index_i32 => .global_import_index_i32, | ||
| 2642 | .global_index_leb => .global_import_index_leb, | ||
| 2643 | .memory_addr_i32 => .memory_addr_import_i32, | ||
| 2644 | .memory_addr_i64 => .memory_addr_import_i64, | ||
| 2645 | .memory_addr_leb => .memory_addr_import_leb, | ||
| 2646 | .memory_addr_leb64 => .memory_addr_import_leb64, | ||
| 2647 | .memory_addr_locrel_i32 => .memory_addr_import_locrel_i32, | ||
| 2648 | .memory_addr_rel_sleb => .memory_addr_import_rel_sleb, | ||
| 2649 | .memory_addr_rel_sleb64 => .memory_addr_import_rel_sleb64, | ||
| 2650 | .memory_addr_sleb => .memory_addr_import_sleb, | ||
| 2651 | .memory_addr_sleb64 => .memory_addr_import_sleb64, | ||
| 2652 | .memory_addr_tls_sleb => .memory_addr_import_tls_sleb, | ||
| 2653 | .memory_addr_tls_sleb64 => .memory_addr_import_tls_sleb64, | ||
| 2654 | .section_offset_i32 => unreachable, | ||
| 2655 | .table_index_i32 => .table_import_index_i32, | ||
| 2656 | .table_index_i64 => .table_import_index_i64, | ||
| 2657 | .table_index_rel_sleb => .table_import_index_rel_sleb, | ||
| 2658 | .table_index_rel_sleb64 => .table_import_index_rel_sleb64, | ||
| 2659 | .table_index_sleb => .table_import_index_sleb, | ||
| 2660 | .table_index_sleb64 => .table_import_index_sleb64, | ||
| 2661 | .table_number_leb => .table_import_number_leb, | ||
| 2662 | .type_index_leb => unreachable, | ||
| 2663 | }; | ||
| 2664 | } | ||
| 2665 | }; | ||
| 2666 | |||
| 2667 | pub const Pointee = union { | ||
| 2668 | symbol_name: String, | ||
| 2669 | data: ObjectData.Index, | ||
| 2670 | type_index: FunctionType.Index, | ||
| 2671 | section: ObjectSectionIndex, | ||
| 2672 | function: ObjectFunctionIndex, | ||
| 2673 | global: ObjectGlobalIndex, | ||
| 2674 | table: ObjectTableIndex, | ||
| 2675 | }; | ||
| 2676 | |||
| 2677 | pub const Slice = extern struct { | ||
| 2678 | /// Index into `relocations`. | ||
| 2679 | off: u32, | ||
| 2680 | len: u32, | ||
| 2681 | |||
| 2682 | const empty: Slice = .{ .off = 0, .len = 0 }; | ||
| 2683 | |||
| 2684 | pub fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag { | ||
| 2685 | return wasm.object_relocations.items(.tag)[s.off..][0..s.len]; | ||
| 2686 | } | ||
| 2687 | |||
| 2688 | pub fn offsets(s: Slice, wasm: *const Wasm) []const u32 { | ||
| 2689 | return wasm.object_relocations.items(.offset)[s.off..][0..s.len]; | ||
| 2690 | } | ||
| 2691 | |||
| 2692 | pub fn pointees(s: Slice, wasm: *const Wasm) []const Pointee { | ||
| 2693 | return wasm.object_relocations.items(.pointee)[s.off..][0..s.len]; | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | pub fn addends(s: Slice, wasm: *const Wasm) []const i32 { | ||
| 2697 | return wasm.object_relocations.items(.addend)[s.off..][0..s.len]; | ||
| 2698 | } | ||
| 2699 | }; | ||
| 2700 | |||
| 2701 | pub const IterableSlice = struct { | ||
| 2702 | slice: Slice, | ||
| 2703 | /// Offset at which point to stop iterating. | ||
| 2704 | end: u32, | ||
| 2705 | |||
| 2706 | const empty: IterableSlice = .{ .slice = .empty, .end = 0 }; | ||
| 2707 | |||
| 2708 | fn init(relocs: Slice, offset: u32, size: u32, wasm: *const Wasm) IterableSlice { | ||
| 2709 | const offsets = relocs.offsets(wasm); | ||
| 2710 | const start = std.sort.lowerBound(u32, offsets, offset, order); | ||
| 2711 | return .{ | ||
| 2712 | .slice = .{ | ||
| 2713 | .off = @intCast(relocs.off + start), | ||
| 2714 | .len = @intCast(relocs.len - start), | ||
| 2715 | }, | ||
| 2716 | .end = offset + size, | ||
| 2717 | }; | ||
| 2718 | } | ||
| 2719 | |||
| 2720 | fn order(lhs: u32, rhs: u32) std.math.Order { | ||
| 2721 | return std.math.order(lhs, rhs); | ||
| 2722 | } | ||
| 2723 | }; | ||
| 2724 | }; | ||
| 2725 | |||
| 2726 | pub const MemoryImport = extern struct { | ||
| 2727 | module_name: String, | ||
| 2728 | limits_min: u32, | ||
| 2729 | limits_max: u32, | ||
| 2730 | source_location: SourceLocation, | ||
| 2731 | limits_has_max: bool, | ||
| 2732 | limits_is_shared: bool, | ||
| 2733 | padding: [2]u8 = .{ 0, 0 }, | ||
| 2734 | |||
| 2735 | pub fn limits(mi: *const MemoryImport) std.wasm.Limits { | ||
| 2736 | return .{ | ||
| 2737 | .flags = .{ | ||
| 2738 | .has_max = mi.limits_has_max, | ||
| 2739 | .is_shared = mi.limits_is_shared, | ||
| 2740 | }, | ||
| 2741 | .min = mi.limits_min, | ||
| 2742 | .max = mi.limits_max, | ||
| 2743 | }; | ||
| 2744 | } | ||
| 2745 | }; | ||
| 2746 | |||
| 2747 | pub const Alignment = InternPool.Alignment; | ||
| 2748 | |||
| 2749 | pub const InitFunc = extern struct { | ||
| 2750 | priority: u32, | ||
| 2751 | function_index: ObjectFunctionIndex, | ||
| 2752 | |||
| 2753 | pub fn lessThan(ctx: void, lhs: InitFunc, rhs: InitFunc) bool { | ||
| 2754 | _ = ctx; | ||
| 2755 | if (lhs.priority == rhs.priority) { | ||
| 2756 | return @intFromEnum(lhs.function_index) < @intFromEnum(rhs.function_index); | ||
| 2757 | } else { | ||
| 2758 | return lhs.priority < rhs.priority; | ||
| 2759 | } | ||
| 2760 | } | ||
| 2761 | }; | ||
| 2762 | |||
| 2763 | pub const Comdat = struct { | ||
| 2764 | name: String, | ||
| 2765 | /// Must be zero, no flags are currently defined by the tool-convention. | ||
| 2766 | flags: u32, | ||
| 2767 | symbols: Comdat.Symbol.Slice, | ||
| 2768 | |||
| 2769 | pub const Symbol = struct { | ||
| 2770 | kind: Comdat.Symbol.Type, | ||
| 2771 | /// Index of the data segment/function/global/event/table within a WASM module. | ||
| 2772 | /// The object must not be an import. | ||
| 2773 | index: u32, | ||
| 2774 | |||
| 2775 | pub const Slice = struct { | ||
| 2776 | /// Index into Wasm object_comdat_symbols | ||
| 2777 | off: u32, | ||
| 2778 | len: u32, | ||
| 2779 | }; | ||
| 2780 | |||
| 2781 | pub const Type = enum(u8) { | ||
| 2782 | data = 0, | ||
| 2783 | function = 1, | ||
| 2784 | global = 2, | ||
| 2785 | event = 3, | ||
| 2786 | table = 4, | ||
| 2787 | section = 5, | ||
| 2788 | }; | ||
| 2789 | }; | ||
| 2790 | }; | ||
| 2791 | |||
| 2792 | /// Stored as a u8 so it can reuse the string table mechanism. | ||
| 2793 | pub const Feature = packed struct(u8) { | ||
| 2794 | prefix: Prefix, | ||
| 2795 | /// Type of the feature, must be unique in the sequence of features. | ||
| 2796 | tag: Tag, | ||
| 2797 | |||
| 2798 | pub const sentinel: Feature = @bitCast(@as(u8, 0)); | ||
| 2799 | |||
| 2800 | /// Stored identically to `String`. The bytes are reinterpreted as `Feature` | ||
| 2801 | /// elements. Elements must be sorted before string-interning. | ||
| 2802 | pub const Set = enum(u32) { | ||
| 2803 | _, | ||
| 2804 | |||
| 2805 | pub fn fromString(s: String) Set { | ||
| 2806 | return @enumFromInt(@intFromEnum(s)); | ||
| 2807 | } | ||
| 2808 | |||
| 2809 | pub fn string(s: Set) String { | ||
| 2810 | return @enumFromInt(@intFromEnum(s)); | ||
| 2811 | } | ||
| 2812 | |||
| 2813 | pub fn slice(s: Set, wasm: *const Wasm) [:sentinel]const Feature { | ||
| 2814 | return @ptrCast(string(s).slice(wasm)); | ||
| 2815 | } | ||
| 2816 | }; | ||
| 2817 | |||
| 2818 | /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem. | ||
| 2819 | /// Additionally the name uses convention matching the wasm binary format. | ||
| 2820 | pub const Tag = enum(u6) { | ||
| 2821 | atomics, | ||
| 2822 | @"bulk-memory", | ||
| 2823 | @"exception-handling", | ||
| 2824 | @"extended-const", | ||
| 2825 | @"half-precision", | ||
| 2826 | multimemory, | ||
| 2827 | multivalue, | ||
| 2828 | @"mutable-globals", | ||
| 2829 | @"nontrapping-fptoint", | ||
| 2830 | @"reference-types", | ||
| 2831 | @"relaxed-simd", | ||
| 2832 | @"sign-ext", | ||
| 2833 | simd128, | ||
| 2834 | @"tail-call", | ||
| 2835 | @"shared-mem", | ||
| 2836 | |||
| 2837 | pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag { | ||
| 2838 | return @enumFromInt(@intFromEnum(feature)); | ||
| 2839 | } | ||
| 2840 | |||
| 2841 | pub fn toCpuFeature(tag: Tag) ?std.Target.wasm.Feature { | ||
| 2842 | return if (@intFromEnum(tag) < @typeInfo(std.Target.wasm.Feature).@"enum".fields.len) | ||
| 2843 | @enumFromInt(@intFromEnum(tag)) | ||
| 2844 | else | ||
| 2845 | null; | ||
| 2846 | } | ||
| 2847 | |||
| 2848 | pub const format = @compileError("use @tagName instead"); | ||
| 2849 | }; | ||
| 2850 | |||
| 2851 | /// Provides information about the usage of the feature. | ||
| 2852 | pub const Prefix = enum(u2) { | ||
| 2853 | /// Reserved so that a 0-byte Feature is invalid and therefore can be a sentinel. | ||
| 2854 | invalid, | ||
| 2855 | /// Object uses this feature, and the link fails if feature is not in | ||
| 2856 | /// the allowed set. | ||
| 2857 | @"+", | ||
| 2858 | /// Object does not use this feature, and the link fails if this | ||
| 2859 | /// feature is in the allowed set. | ||
| 2860 | @"-", | ||
| 2861 | /// Object uses this feature, and the link fails if this feature is not | ||
| 2862 | /// in the allowed set, or if any object does not use this feature. | ||
| 2863 | @"=", | ||
| 2864 | }; | ||
| 2865 | |||
| 2866 | pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { | ||
| 2867 | _ = opt; | ||
| 2868 | _ = fmt; | ||
| 2869 | try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) }); | ||
| 2870 | } | ||
| 2871 | |||
| 2872 | pub fn lessThan(_: void, a: Feature, b: Feature) bool { | ||
| 2873 | assert(a != b); | ||
| 2874 | const a_int: u8 = @bitCast(a); | ||
| 2875 | const b_int: u8 = @bitCast(b); | ||
| 2876 | return a_int < b_int; | ||
| 2877 | } | ||
| 2878 | }; | ||
| 2879 | |||
| 2880 | pub fn open( | ||
| 2881 | arena: Allocator, | ||
| 2882 | comp: *Compilation, | ||
| 2883 | emit: Path, | ||
| 2884 | options: link.File.OpenOptions, | ||
| 2885 | ) !*Wasm { | ||
| 2886 | // TODO: restore saved linker state, don't truncate the file, and | ||
| 2887 | // participate in incremental compilation. | ||
| 2888 | return createEmpty(arena, comp, emit, options); | ||
| 2889 | } | ||
| 2890 | |||
| 2891 | pub fn createEmpty( | ||
| 2892 | arena: Allocator, | ||
| 2893 | comp: *Compilation, | ||
| 2894 | emit: Path, | ||
| 2895 | options: link.File.OpenOptions, | ||
| 2896 | ) !*Wasm { | ||
| 2897 | const target = comp.root_mod.resolved_target.result; | ||
| 2898 | assert(target.ofmt == .wasm); | ||
| 2899 | |||
| 2900 | const use_lld = build_options.have_llvm and comp.config.use_lld; | ||
| 2901 | const use_llvm = comp.config.use_llvm; | ||
| 2902 | const output_mode = comp.config.output_mode; | ||
| 2903 | const wasi_exec_model = comp.config.wasi_exec_model; | ||
| 2904 | |||
| 2905 | // If using LLD to link, this code should produce an object file so that it | ||
| 2906 | // can be passed to LLD. | ||
| 2907 | // If using LLVM to generate the object file for the zig compilation unit, | ||
| 2908 | // we need a place to put the object file so that it can be subsequently | ||
| 2909 | // handled. | ||
| 2910 | const zcu_object_sub_path = if (!use_lld and !use_llvm) | ||
| 2911 | null | ||
| 2912 | else | ||
| 2913 | try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); | ||
| 2914 | |||
| 2915 | const wasm = try arena.create(Wasm); | ||
| 2916 | wasm.* = .{ | ||
| 2917 | .base = .{ | ||
| 2918 | .tag = .wasm, | ||
| 2919 | .comp = comp, | ||
| 2920 | .emit = emit, | ||
| 2921 | .zcu_object_sub_path = zcu_object_sub_path, | ||
| 2922 | // Garbage collection is so crucial to WebAssembly that we design | ||
| 2923 | // the linker around the assumption that it will be on in the vast | ||
| 2924 | // majority of cases, and therefore express "no garbage collection" | ||
| 2925 | // in terms of setting the no_strip and must_link flags on all | ||
| 2926 | // symbols. | ||
| 2927 | .gc_sections = options.gc_sections orelse (output_mode != .Obj), | ||
| 2928 | .print_gc_sections = options.print_gc_sections, | ||
| 2929 | .stack_size = options.stack_size orelse switch (target.os.tag) { | ||
| 2930 | .freestanding => 1 * 1024 * 1024, // 1 MiB | ||
| 2931 | else => 16 * 1024 * 1024, // 16 MiB | ||
| 2932 | }, | ||
| 2933 | .allow_shlib_undefined = options.allow_shlib_undefined orelse false, | ||
| 2934 | .file = null, | ||
| 2935 | .disable_lld_caching = options.disable_lld_caching, | ||
| 2936 | .build_id = options.build_id, | ||
| 2937 | }, | ||
| 2938 | .name = undefined, | ||
| 2939 | .string_table = .empty, | ||
| 2940 | .string_bytes = .empty, | ||
| 2941 | .import_table = options.import_table, | ||
| 2942 | .export_table = options.export_table, | ||
| 2943 | .import_symbols = options.import_symbols, | ||
| 2944 | .export_symbol_names = options.export_symbol_names, | ||
| 2945 | .global_base = options.global_base, | ||
| 2946 | .initial_memory = options.initial_memory, | ||
| 2947 | .max_memory = options.max_memory, | ||
| 2948 | |||
| 2949 | .entry_name = undefined, | ||
| 2950 | .dump_argv_list = .empty, | ||
| 2951 | .object_host_name = .none, | ||
| 2952 | .preloaded_strings = undefined, | ||
| 2953 | }; | ||
| 2954 | if (use_llvm and comp.config.have_zcu) { | ||
| 2955 | wasm.llvm_object = try LlvmObject.create(arena, comp); | ||
| 2956 | } | ||
| 2957 | errdefer wasm.base.destroy(); | ||
| 2958 | |||
| 2959 | if (options.object_host_name) |name| wasm.object_host_name = (try wasm.internString(name)).toOptional(); | ||
| 2960 | |||
| 2961 | inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| { | ||
| 2962 | @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name); | ||
| 2963 | } | ||
| 2964 | |||
| 2965 | wasm.entry_name = switch (options.entry) { | ||
| 2966 | .disabled => .none, | ||
| 2967 | .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), | ||
| 2968 | .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), | ||
| 2969 | .named => |name| (try wasm.internString(name)).toOptional(), | ||
| 2970 | }; | ||
| 2971 | |||
| 2972 | if (use_lld and (use_llvm or !comp.config.have_zcu)) { | ||
| 2973 | // LLVM emits the object file (if any); LLD links it into the final product. | ||
| 2974 | return wasm; | ||
| 2975 | } | ||
| 2976 | |||
| 2977 | // What path should this Wasm linker code output to? | ||
| 2978 | // If using LLD to link, this code should produce an object file so that it | ||
| 2979 | // can be passed to LLD. | ||
| 2980 | const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; | ||
| 2981 | |||
| 2982 | wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{ | ||
| 2983 | .truncate = true, | ||
| 2984 | .read = true, | ||
| 2985 | .mode = if (fs.has_executable_bit) | ||
| 2986 | if (target.os.tag == .wasi and output_mode == .Exe) | ||
| 2987 | fs.File.default_mode | 0b001_000_000 | ||
| 2988 | else | ||
| 2989 | fs.File.default_mode | ||
| 2990 | else | ||
| 2991 | 0, | ||
| 2992 | }); | ||
| 2993 | wasm.name = sub_path; | ||
| 2994 | |||
| 2995 | return wasm; | ||
| 2996 | } | ||
| 2997 | |||
| 2998 | fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void { | ||
| 2999 | const diags = &wasm.base.comp.link_diags; | ||
| 3000 | const obj = link.openObject(path, false, false) catch |err| { | ||
| 3001 | switch (diags.failParse(path, "failed to open object: {s}", .{@errorName(err)})) { | ||
| 3002 | error.LinkFailure => return, | ||
| 3003 | } | ||
| 3004 | }; | ||
| 3005 | wasm.parseObject(obj) catch |err| { | ||
| 3006 | switch (diags.failParse(path, "failed to parse object: {s}", .{@errorName(err)})) { | ||
| 3007 | error.LinkFailure => return, | ||
| 3008 | } | ||
| 3009 | }; | ||
| 3010 | } | ||
| 3011 | |||
| 3012 | fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { | ||
| 3013 | log.debug("parseObject {}", .{obj.path}); | ||
| 3014 | const gpa = wasm.base.comp.gpa; | ||
| 3015 | const gc_sections = wasm.base.gc_sections; | ||
| 3016 | |||
| 3017 | defer obj.file.close(); | ||
| 3018 | |||
| 3019 | try wasm.objects.ensureUnusedCapacity(gpa, 1); | ||
| 3020 | const stat = try obj.file.stat(); | ||
| 3021 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; | ||
| 3022 | |||
| 3023 | const file_contents = try gpa.alloc(u8, size); | ||
| 3024 | defer gpa.free(file_contents); | ||
| 3025 | |||
| 3026 | const n = try obj.file.preadAll(file_contents, 0); | ||
| 3027 | if (n != file_contents.len) return error.UnexpectedEndOfFile; | ||
| 3028 | |||
| 3029 | var ss: Object.ScratchSpace = .{}; | ||
| 3030 | defer ss.deinit(gpa); | ||
| 3031 | |||
| 3032 | const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections); | ||
| 3033 | wasm.objects.appendAssumeCapacity(object); | ||
| 3034 | } | ||
| 3035 | |||
| 3036 | fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void { | ||
| 3037 | log.debug("parseArchive {}", .{obj.path}); | ||
| 3038 | const gpa = wasm.base.comp.gpa; | ||
| 3039 | const gc_sections = wasm.base.gc_sections; | ||
| 3040 | |||
| 3041 | defer obj.file.close(); | ||
| 3042 | |||
| 3043 | const stat = try obj.file.stat(); | ||
| 3044 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; | ||
| 3045 | |||
| 3046 | const file_contents = try gpa.alloc(u8, size); | ||
| 3047 | defer gpa.free(file_contents); | ||
| 3048 | |||
| 3049 | const n = try obj.file.preadAll(file_contents, 0); | ||
| 3050 | if (n != file_contents.len) return error.UnexpectedEndOfFile; | ||
| 3051 | |||
| 3052 | var archive = try Archive.parse(gpa, file_contents); | ||
| 3053 | defer archive.deinit(gpa); | ||
| 3054 | |||
| 3055 | // In this case we must force link all embedded object files within the archive | ||
| 3056 | // We loop over all symbols, and then group them by offset as the offset | ||
| 3057 | // notates where the object file starts. | ||
| 3058 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); | ||
| 3059 | defer offsets.deinit(); | ||
| 3060 | for (archive.toc.values()) |symbol_offsets| { | ||
| 3061 | for (symbol_offsets.items) |sym_offset| { | ||
| 3062 | try offsets.put(sym_offset, {}); | ||
| 3063 | } | ||
| 3064 | } | ||
| 3065 | |||
| 3066 | var ss: Object.ScratchSpace = .{}; | ||
| 3067 | defer ss.deinit(gpa); | ||
| 3068 | |||
| 3069 | try wasm.objects.ensureUnusedCapacity(gpa, offsets.count()); | ||
| 3070 | for (offsets.keys()) |file_offset| { | ||
| 3071 | const object = try archive.parseObject(wasm, file_contents, file_offset, obj.path, wasm.object_host_name, &ss, obj.must_link, gc_sections); | ||
| 3072 | wasm.objects.appendAssumeCapacity(object); | ||
| 3073 | } | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | pub fn deinit(wasm: *Wasm) void { | ||
| 3077 | const gpa = wasm.base.comp.gpa; | ||
| 3078 | if (wasm.llvm_object) |llvm_object| llvm_object.deinit(); | ||
| 3079 | |||
| 3080 | wasm.navs_exe.deinit(gpa); | ||
| 3081 | wasm.navs_obj.deinit(gpa); | ||
| 3082 | wasm.uavs_exe.deinit(gpa); | ||
| 3083 | wasm.uavs_obj.deinit(gpa); | ||
| 3084 | wasm.overaligned_uavs.deinit(gpa); | ||
| 3085 | wasm.zcu_funcs.deinit(gpa); | ||
| 3086 | wasm.nav_exports.deinit(gpa); | ||
| 3087 | wasm.uav_exports.deinit(gpa); | ||
| 3088 | wasm.imports.deinit(gpa); | ||
| 3089 | |||
| 3090 | wasm.flush_buffer.deinit(gpa); | ||
| 3091 | |||
| 3092 | wasm.mir_instructions.deinit(gpa); | ||
| 3093 | wasm.mir_extra.deinit(gpa); | ||
| 3094 | wasm.all_zcu_locals.deinit(gpa); | ||
| 3095 | |||
| 3096 | if (wasm.dwarf) |*dwarf| dwarf.deinit(); | ||
| 3097 | |||
| 3098 | wasm.object_function_imports.deinit(gpa); | ||
| 3099 | wasm.object_functions.deinit(gpa); | ||
| 3100 | wasm.object_global_imports.deinit(gpa); | ||
| 3101 | wasm.object_globals.deinit(gpa); | ||
| 3102 | wasm.object_table_imports.deinit(gpa); | ||
| 3103 | wasm.object_tables.deinit(gpa); | ||
| 3104 | wasm.object_memory_imports.deinit(gpa); | ||
| 3105 | wasm.object_memories.deinit(gpa); | ||
| 3106 | wasm.object_relocations.deinit(gpa); | ||
| 3107 | wasm.object_data_imports.deinit(gpa); | ||
| 3108 | wasm.object_data_segments.deinit(gpa); | ||
| 3109 | wasm.object_datas.deinit(gpa); | ||
| 3110 | wasm.object_custom_segments.deinit(gpa); | ||
| 3111 | wasm.object_init_funcs.deinit(gpa); | ||
| 3112 | wasm.object_comdats.deinit(gpa); | ||
| 3113 | wasm.object_relocations_table.deinit(gpa); | ||
| 3114 | wasm.object_comdat_symbols.deinit(gpa); | ||
| 3115 | wasm.objects.deinit(gpa); | ||
| 3116 | |||
| 3117 | wasm.func_types.deinit(gpa); | ||
| 3118 | wasm.function_exports.deinit(gpa); | ||
| 3119 | wasm.hidden_function_exports.deinit(gpa); | ||
| 3120 | wasm.function_imports.deinit(gpa); | ||
| 3121 | wasm.functions.deinit(gpa); | ||
| 3122 | wasm.globals.deinit(gpa); | ||
| 3123 | wasm.global_exports.deinit(gpa); | ||
| 3124 | wasm.global_imports.deinit(gpa); | ||
| 3125 | wasm.table_imports.deinit(gpa); | ||
| 3126 | wasm.tables.deinit(gpa); | ||
| 3127 | wasm.data_imports.deinit(gpa); | ||
| 3128 | wasm.data_segments.deinit(gpa); | ||
| 3129 | wasm.symbol_table.deinit(gpa); | ||
| 3130 | wasm.out_relocs.deinit(gpa); | ||
| 3131 | wasm.uav_fixups.deinit(gpa); | ||
| 3132 | wasm.nav_fixups.deinit(gpa); | ||
| 3133 | wasm.func_table_fixups.deinit(gpa); | ||
| 3134 | |||
| 3135 | wasm.zcu_indirect_function_set.deinit(gpa); | ||
| 3136 | wasm.object_indirect_function_import_set.deinit(gpa); | ||
| 3137 | wasm.object_indirect_function_set.deinit(gpa); | ||
| 3138 | |||
| 3139 | wasm.string_bytes.deinit(gpa); | ||
| 3140 | wasm.string_table.deinit(gpa); | ||
| 3141 | wasm.dump_argv_list.deinit(gpa); | ||
| 3142 | |||
| 3143 | wasm.params_scratch.deinit(gpa); | ||
| 3144 | wasm.returns_scratch.deinit(gpa); | ||
| 3145 | |||
| 3146 | wasm.error_name_bytes.deinit(gpa); | ||
| 3147 | wasm.error_name_offs.deinit(gpa); | ||
| 3148 | wasm.tag_name_bytes.deinit(gpa); | ||
| 3149 | wasm.tag_name_offs.deinit(gpa); | ||
| 3150 | |||
| 3151 | wasm.missing_exports.deinit(gpa); | ||
| 3152 | } | ||
| 3153 | |||
| 3154 | pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | ||
| 3155 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | ||
| 3156 | @panic("Attempted to compile for object format that was disabled by build configuration"); | ||
| 3157 | } | ||
| 3158 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); | ||
| 3159 | |||
| 3160 | dev.check(.wasm_backend); | ||
| 3161 | |||
| 3162 | const zcu = pt.zcu; | ||
| 3163 | const gpa = zcu.gpa; | ||
| 3164 | try wasm.functions.ensureUnusedCapacity(gpa, 1); | ||
| 3165 | try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); | ||
| 3166 | |||
| 3167 | const ip = &zcu.intern_pool; | ||
| 3168 | const owner_nav = zcu.funcInfo(func_index).owner_nav; | ||
| 3169 | log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); | ||
| 3170 | |||
| 3171 | const zds: ZcuDataStarts = .init(wasm); | ||
| 3172 | |||
| 3173 | // This converts AIR to MIR but does not yet lower to wasm code. | ||
| 3174 | // That lowering happens during `flush`, after garbage collection, which | ||
| 3175 | // can affect function and global indexes, which affects the LEB integer | ||
| 3176 | // encoding, which affects the output binary size. | ||
| 3177 | const function = try CodeGen.function(wasm, pt, func_index, air, liveness); | ||
| 3178 | wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function }); | ||
| 3179 | wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {}); | ||
| 3180 | |||
| 3181 | try zds.finish(wasm, pt); | ||
| 3182 | } | ||
| 3183 | |||
| 3184 | // Generate code for the "Nav", storing it in memory to be later written to | ||
| 3185 | // the file on flush(). | ||
| 3186 | pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | ||
| 3187 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | ||
| 3188 | @panic("Attempted to compile for object format that was disabled by build configuration"); | ||
| 3189 | } | ||
| 3190 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav_index); | ||
| 3191 | const zcu = pt.zcu; | ||
| 3192 | const ip = &zcu.intern_pool; | ||
| 3193 | const nav = ip.getNav(nav_index); | ||
| 3194 | const comp = wasm.base.comp; | ||
| 3195 | const gpa = comp.gpa; | ||
| 3196 | const is_obj = comp.config.output_mode == .Obj; | ||
| 3197 | const target = &comp.root_mod.resolved_target.result; | ||
| 3198 | |||
| 3199 | const nav_init, const chased_nav_index = switch (ip.indexToKey(nav.status.fully_resolved.val)) { | ||
| 3200 | .func => return, // global const which is a function alias | ||
| 3201 | .@"extern" => |ext| { | ||
| 3202 | if (is_obj) { | ||
| 3203 | assert(!wasm.navs_obj.contains(ext.owner_nav)); | ||
| 3204 | } else { | ||
| 3205 | assert(!wasm.navs_exe.contains(ext.owner_nav)); | ||
| 3206 | } | ||
| 3207 | const name = try wasm.internString(ext.name.toSlice(ip)); | ||
| 3208 | if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name); | ||
| 3209 | try wasm.imports.ensureUnusedCapacity(gpa, 1); | ||
| 3210 | try wasm.function_imports.ensureUnusedCapacity(gpa, 1); | ||
| 3211 | try wasm.data_imports.ensureUnusedCapacity(gpa, 1); | ||
| 3212 | const zcu_import = wasm.addZcuImportReserved(ext.owner_nav); | ||
| 3213 | if (ip.isFunctionType(nav.typeOf(ip))) { | ||
| 3214 | wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm)); | ||
| 3215 | // Ensure there is a corresponding function type table entry. | ||
| 3216 | const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?; | ||
| 3217 | _ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); | ||
| 3218 | } else { | ||
| 3219 | wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm)); | ||
| 3220 | } | ||
| 3221 | return; | ||
| 3222 | }, | ||
| 3223 | .variable => |variable| .{ variable.init, variable.owner_nav }, | ||
| 3224 | else => .{ nav.status.fully_resolved.val, nav_index }, | ||
| 3225 | }; | ||
| 3226 | //log.debug("updateNav {} {d}", .{ nav.fqn.fmt(ip), chased_nav_index }); | ||
| 3227 | assert(!wasm.imports.contains(chased_nav_index)); | ||
| 3228 | |||
| 3229 | if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) { | ||
| 3230 | if (is_obj) { | ||
| 3231 | assert(!wasm.navs_obj.contains(chased_nav_index)); | ||
| 3232 | } else { | ||
| 3233 | assert(!wasm.navs_exe.contains(chased_nav_index)); | ||
| 3234 | } | ||
| 3235 | return; | ||
| 3236 | } | ||
| 3237 | |||
| 3238 | if (is_obj) { | ||
| 3239 | const zcu_data_starts: ZcuDataStarts = .initObj(wasm); | ||
| 3240 | const navs_i = try refNavObj(wasm, chased_nav_index); | ||
| 3241 | const zcu_data = try lowerZcuData(wasm, pt, nav_init); | ||
| 3242 | navs_i.value(wasm).* = zcu_data; | ||
| 3243 | try zcu_data_starts.finishObj(wasm, pt); | ||
| 3244 | } else { | ||
| 3245 | const zcu_data_starts: ZcuDataStarts = .initExe(wasm); | ||
| 3246 | const navs_i = try refNavExe(wasm, chased_nav_index); | ||
| 3247 | const zcu_data = try lowerZcuData(wasm, pt, nav_init); | ||
| 3248 | navs_i.value(wasm).code = zcu_data.code; | ||
| 3249 | try zcu_data_starts.finishExe(wasm, pt); | ||
| 3250 | } | ||
| 3251 | } | ||
| 3252 | |||
| 3253 | pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { | ||
| 3254 | const comp = wasm.base.comp; | ||
| 3255 | const diags = &comp.link_diags; | ||
| 3256 | if (wasm.dwarf) |*dw| { | ||
| 3257 | dw.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { | ||
| 3258 | error.Overflow => return error.Overflow, | ||
| 3259 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3260 | else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), | ||
| 3261 | }; | ||
| 3262 | } | ||
| 3263 | } | ||
| 3264 | |||
| 3265 | pub fn deleteExport( | ||
| 3266 | wasm: *Wasm, | ||
| 3267 | exported: Zcu.Exported, | ||
| 3268 | name: InternPool.NullTerminatedString, | ||
| 3269 | ) void { | ||
| 3270 | if (wasm.llvm_object != null) return; | ||
| 3271 | |||
| 3272 | const zcu = wasm.base.comp.zcu.?; | ||
| 3273 | const ip = &zcu.intern_pool; | ||
| 3274 | const name_slice = name.toSlice(ip); | ||
| 3275 | const export_name = wasm.getExistingString(name_slice).?; | ||
| 3276 | switch (exported) { | ||
| 3277 | .nav => |nav_index| { | ||
| 3278 | log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @intFromEnum(nav_index) }); | ||
| 3279 | assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name })); | ||
| 2570 | }, | 3280 | }, |
| 2571 | else => unreachable, | 3281 | .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })), |
| 2572 | } | 3282 | } |
| 2573 | } | 3283 | } |
| 2574 | 3284 | ||
| 2575 | /// Appends a new segment with default field values | 3285 | pub fn updateExports( |
| 2576 | fn appendDummySegment(wasm: *Wasm) !void { | 3286 | wasm: *Wasm, |
| 2577 | const gpa = wasm.base.comp.gpa; | 3287 | pt: Zcu.PerThread, |
| 2578 | try wasm.segments.append(gpa, .{ | 3288 | exported: Zcu.Exported, |
| 2579 | .alignment = .@"1", | 3289 | export_indices: []const Zcu.Export.Index, |
| 2580 | .size = 0, | 3290 | ) !void { |
| 2581 | .offset = 0, | 3291 | if (build_options.skip_non_native and builtin.object_format != .wasm) { |
| 2582 | .flags = 0, | 3292 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 2583 | }); | 3293 | } |
| 3294 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); | ||
| 3295 | |||
| 3296 | const zcu = pt.zcu; | ||
| 3297 | const gpa = zcu.gpa; | ||
| 3298 | const ip = &zcu.intern_pool; | ||
| 3299 | for (export_indices) |export_idx| { | ||
| 3300 | const exp = export_idx.ptr(zcu); | ||
| 3301 | const name_slice = exp.opts.name.toSlice(ip); | ||
| 3302 | const name = try wasm.internString(name_slice); | ||
| 3303 | switch (exported) { | ||
| 3304 | .nav => |nav_index| { | ||
| 3305 | log.debug("updateExports '{s}' nav={d}", .{ name_slice, @intFromEnum(nav_index) }); | ||
| 3306 | try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx); | ||
| 3307 | }, | ||
| 3308 | .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx), | ||
| 3309 | } | ||
| 3310 | } | ||
| 2584 | } | 3311 | } |
| 2585 | 3312 | ||
| 2586 | pub fn loadInput(wasm: *Wasm, input: link.Input) !void { | 3313 | pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| ... | @@ -2596,7 +3323,9 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { | ... | @@ -2596,7 +3323,9 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| 2596 | .res => unreachable, | 3323 | .res => unreachable, |
| 2597 | .dso_exact => unreachable, | 3324 | .dso_exact => unreachable, |
| 2598 | .dso => unreachable, | 3325 | .dso => unreachable, |
| 2599 | .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)), | 3326 | .object, .archive => |obj| { |
| 3327 | try argv.append(gpa, try obj.path.toString(comp.arena)); | ||
| 3328 | }, | ||
| 2600 | } | 3329 | } |
| 2601 | } | 3330 | } |
| 2602 | 3331 | ||
| ... | @@ -2612,791 +3341,472 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { | ... | @@ -2612,791 +3341,472 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| 2612 | pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 3341 | pub 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; | 3342 | const comp = wasm.base.comp; |
| 2614 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 3343 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 3344 | const diags = &comp.link_diags; | ||
| 2615 | 3345 | ||
| 2616 | if (use_lld) { | 3346 | if (use_lld) { |
| 2617 | return wasm.linkWithLLD(arena, tid, prog_node); | 3347 | return wasm.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) { |
| 3348 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3349 | error.LinkFailure => return error.LinkFailure, | ||
| 3350 | else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}), | ||
| 3351 | }; | ||
| 2618 | } | 3352 | } |
| 2619 | return wasm.flushModule(arena, tid, prog_node); | 3353 | return wasm.flushModule(arena, tid, prog_node); |
| 2620 | } | 3354 | } |
| 2621 | 3355 | ||
| 2622 | pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { | 3356 | pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 2623 | const tracy = trace(@src()); | 3357 | const tracy = trace(@src()); |
| 2624 | defer tracy.end(); | 3358 | defer tracy.end(); |
| 2625 | 3359 | ||
| 2626 | const comp = wasm.base.comp; | 3360 | const sub_prog_node = prog_node.start("Wasm Prelink", 0); |
| 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(); | 3361 | defer sub_prog_node.end(); |
| 2638 | 3362 | ||
| 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 | ||
| 2693 | fn 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; | 3363 | const comp = wasm.base.comp; |
| 2700 | const diags = &comp.link_diags; | ||
| 2701 | const gpa = comp.gpa; | 3364 | const gpa = comp.gpa; |
| 2702 | const use_llvm = comp.config.use_llvm; | 3365 | const rdynamic = comp.config.rdynamic; |
| 2703 | const use_lld = build_options.have_llvm and comp.config.use_lld; | 3366 | const is_obj = comp.config.output_mode == .Obj; |
| 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 | 3367 | ||
| 2708 | // Size of each section header | 3368 | assert(wasm.missing_exports.entries.len == 0); |
| 2709 | const header_size = 5 + 1; | 3369 | for (wasm.export_symbol_names) |exp_name| { |
| 2710 | // The amount of sections that will be written | 3370 | const exp_name_interned = try wasm.internString(exp_name); |
| 2711 | var section_count: u32 = 0; | 3371 | if (wasm.object_function_imports.getPtr(exp_name_interned)) |import| { |
| 2712 | // Index of the code section. Used to tell relocation table where the section lives. | 3372 | if (import.resolution != .unresolved) { |
| 2713 | var code_section_index: ?u32 = null; | 3373 | import.flags.exported = true; |
| 2714 | // Index of the data section. Used to tell relocation table where the section lives. | 3374 | continue; |
| 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 | } | 3375 | } |
| 2739 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.returns.len))); | 3376 | } |
| 2740 | for (func_type.returns) |ret_ty| { | 3377 | if (wasm.object_global_imports.getPtr(exp_name_interned)) |import| { |
| 2741 | try leb.writeUleb128(binary_writer, std.wasm.valtype(ret_ty)); | 3378 | if (import.resolution != .unresolved) { |
| 3379 | import.flags.exported = true; | ||
| 3380 | continue; | ||
| 2742 | } | 3381 | } |
| 2743 | } | 3382 | } |
| 2744 | 3383 | if (wasm.object_table_imports.getPtr(exp_name_interned)) |import| { | |
| 2745 | try writeVecSectionHeader( | 3384 | if (import.resolution != .unresolved) { |
| 2746 | binary_bytes.items, | 3385 | import.flags.exported = true; |
| 2747 | header_offset, | 3386 | continue; |
| 2748 | .type, | 3387 | } |
| 2749 | @intCast(binary_bytes.items.len - header_offset - header_size), | 3388 | } |
| 2750 | @intCast(wasm.func_types.items.len), | 3389 | try wasm.missing_exports.put(gpa, exp_name_interned, {}); |
| 2751 | ); | ||
| 2752 | section_count += 1; | ||
| 2753 | } | 3390 | } |
| 2754 | 3391 | ||
| 2755 | // Import section | 3392 | if (wasm.entry_name.unwrap()) |entry_name| { |
| 2756 | if (wasm.imports.count() != 0 or import_memory) { | 3393 | if (wasm.object_function_imports.getPtr(entry_name)) |import| { |
| 2757 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3394 | if (import.resolution != .unresolved) { |
| 2758 | 3395 | import.flags.exported = true; | |
| 2759 | var it = wasm.imports.iterator(); | 3396 | wasm.entry_resolution = import.resolution; |
| 2760 | while (it.next()) |entry| { | 3397 | } |
| 2761 | assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined()); | ||
| 2762 | const import = entry.value_ptr.*; | ||
| 2763 | try wasm.emitImport(binary_writer, import); | ||
| 2764 | } | 3398 | } |
| 3399 | } | ||
| 2765 | 3400 | ||
| 2766 | if (import_memory) { | 3401 | if (comp.zcu != null) { |
| 2767 | const mem_imp: Import = .{ | 3402 | // Zig always depends on a stack pointer global. |
| 2768 | .module_name = wasm.host_name, | 3403 | // If emitting an object, it's an import. Otherwise, the linker synthesizes it. |
| 2769 | .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory, | 3404 | if (is_obj) { |
| 2770 | .kind = .{ .memory = wasm.memories.limits }, | 3405 | @panic("TODO"); |
| 2771 | }; | 3406 | } else { |
| 2772 | try wasm.emitImport(binary_writer, mem_imp); | 3407 | try wasm.globals.put(gpa, .__stack_pointer, {}); |
| 3408 | assert(wasm.globals.entries.len - 1 == @intFromEnum(GlobalIndex.stack_pointer)); | ||
| 2773 | } | 3409 | } |
| 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 | } | 3410 | } |
| 2784 | 3411 | ||
| 2785 | // Function section | 3412 | // These loops do both recursive marking of alive symbols well as checking for undefined symbols. |
| 2786 | if (wasm.functions.count() != 0) { | 3413 | // At the end, output functions and globals will be populated. |
| 2787 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3414 | for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| { |
| 2788 | for (wasm.functions.values()) |function| { | 3415 | if (import.flags.isIncluded(rdynamic)) { |
| 2789 | try leb.writeUleb128(binary_writer, function.func.type_index); | 3416 | try markFunctionImport(wasm, name, import, @enumFromInt(i)); |
| 2790 | } | 3417 | } |
| 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 | } | 3418 | } |
| 2801 | 3419 | // Also treat init functions as roots. | |
| 2802 | // Table section | 3420 | for (wasm.object_init_funcs.items) |init_func| { |
| 2803 | if (wasm.tables.items.len > 0) { | 3421 | const func = init_func.function_index.ptr(wasm); |
| 2804 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3422 | if (func.object_index.ptr(wasm).is_included) { |
| 2805 | 3423 | try markFunction(wasm, init_func.function_index, false); | |
| 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 | } | 3424 | } |
| 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 | } | 3425 | } |
| 3426 | wasm.functions_end_prelink = @intCast(wasm.functions.entries.len); | ||
| 2820 | 3427 | ||
| 2821 | // Memory section | 3428 | for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| { |
| 2822 | if (!import_memory) { | 3429 | if (import.flags.isIncluded(rdynamic)) { |
| 2823 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3430 | try markGlobalImport(wasm, name, import, @enumFromInt(i)); |
| 2824 | 3431 | } | |
| 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 | } | 3432 | } |
| 3433 | wasm.globals_end_prelink = @intCast(wasm.globals.entries.len); | ||
| 3434 | wasm.global_exports_len = @intCast(wasm.global_exports.items.len); | ||
| 2835 | 3435 | ||
| 2836 | // Global section (used to emit stack pointer) | 3436 | for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| { |
| 2837 | if (wasm.wasm_globals.items.len > 0) { | 3437 | if (import.flags.isIncluded(rdynamic)) { |
| 2838 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3438 | try markTableImport(wasm, name, import, @enumFromInt(i)); |
| 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 | } | 3439 | } |
| 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 | } | 3440 | } |
| 2855 | 3441 | ||
| 2856 | // Export section | 3442 | for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| { |
| 2857 | if (wasm.exports.items.len != 0 or export_memory) { | 3443 | if (import.flags.isIncluded(rdynamic)) { |
| 2858 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3444 | try markDataImport(wasm, name, import, @enumFromInt(i)); |
| 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 | } | 3445 | } |
| 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 | } | 3446 | } |
| 2884 | 3447 | ||
| 2885 | if (wasm.entry) |entry_index| { | 3448 | // This is a wild ass guess at how to merge memories, haven't checked yet |
| 2886 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3449 | // what the proper way to do this is. |
| 2887 | try writeVecSectionHeader( | 3450 | for (wasm.object_memory_imports.values()) |*memory_import| { |
| 2888 | binary_bytes.items, | 3451 | wasm.memories.limits.min = @min(wasm.memories.limits.min, memory_import.limits_min); |
| 2889 | header_offset, | 3452 | wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max); |
| 2890 | .start, | 3453 | wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max; |
| 2891 | @intCast(binary_bytes.items.len - header_offset - header_size), | ||
| 2892 | entry_index, | ||
| 2893 | ); | ||
| 2894 | } | 3454 | } |
| 2895 | 3455 | ||
| 2896 | // element section (function table) | 3456 | wasm.function_imports_len_prelink = @intCast(wasm.function_imports.entries.len); |
| 2897 | if (wasm.function_table.count() > 0) { | 3457 | wasm.data_imports_len_prelink = @intCast(wasm.data_imports.entries.len); |
| 2898 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3458 | } |
| 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 | 3459 | ||
| 2944 | // Code section | 3460 | pub fn markFunctionImport( |
| 2945 | if (wasm.code_section_index != .none) { | 3461 | wasm: *Wasm, |
| 2946 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3462 | name: String, |
| 2947 | const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count | 3463 | import: *FunctionImport, |
| 3464 | func_index: FunctionImport.Index, | ||
| 3465 | ) link.File.FlushError!void { | ||
| 3466 | if (import.flags.alive) return; | ||
| 3467 | import.flags.alive = true; | ||
| 2948 | 3468 | ||
| 2949 | var func_it = wasm.functions.iterator(); | 3469 | const comp = wasm.base.comp; |
| 2950 | while (func_it.next()) |entry| { | 3470 | const gpa = comp.gpa; |
| 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 | 3471 | ||
| 2955 | if (!is_obj) { | 3472 | try wasm.functions.ensureUnusedCapacity(gpa, 1); |
| 2956 | atom.resolveRelocs(wasm); | 3473 | |
| 2957 | } | 3474 | if (import.resolution == .unresolved) { |
| 2958 | atom.offset = @intCast(binary_bytes.items.len - start_offset); | 3475 | if (name == wasm.preloaded_strings.__wasm_init_memory) { |
| 2959 | try leb.writeUleb128(binary_writer, atom.size); | 3476 | try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{}); |
| 2960 | try binary_writer.writeAll(atom.code.items); | 3477 | } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) { |
| 3478 | try wasm.resolveFunctionSynthetic(import, .__wasm_apply_global_tls_relocs, &.{}, &.{}); | ||
| 3479 | } else if (name == wasm.preloaded_strings.__wasm_call_ctors) { | ||
| 3480 | try wasm.resolveFunctionSynthetic(import, .__wasm_call_ctors, &.{}, &.{}); | ||
| 3481 | } else if (name == wasm.preloaded_strings.__wasm_init_tls) { | ||
| 3482 | try wasm.resolveFunctionSynthetic(import, .__wasm_init_tls, &.{.i32}, &.{}); | ||
| 3483 | } else { | ||
| 3484 | try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm)); | ||
| 2961 | } | 3485 | } |
| 3486 | } else { | ||
| 3487 | try markFunction(wasm, import.resolution.unpack(wasm).object_function, import.flags.exported); | ||
| 3488 | } | ||
| 3489 | } | ||
| 2962 | 3490 | ||
| 2963 | try writeVecSectionHeader( | 3491 | /// Recursively mark alive everything referenced by the function. |
| 2964 | binary_bytes.items, | 3492 | fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.File.FlushError!void { |
| 2965 | header_offset, | 3493 | const comp = wasm.base.comp; |
| 2966 | .code, | 3494 | const gpa = comp.gpa; |
| 2967 | @intCast(binary_bytes.items.len - header_offset - header_size), | 3495 | const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i)); |
| 2968 | @intCast(wasm.functions.count()), | 3496 | if (gop.found_existing) return; |
| 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 | 3497 | ||
| 3009 | // Pad with zeroes to ensure all segments are aligned | 3498 | const rdynamic = comp.config.rdynamic; |
| 3010 | if (current_offset != atom.offset) { | 3499 | const is_obj = comp.config.output_mode == .Obj; |
| 3011 | const diff = atom.offset - current_offset; | 3500 | const function = i.ptr(wasm); |
| 3012 | try binary_writer.writeByteNTimes(0, diff); | 3501 | markObject(wasm, function.object_index); |
| 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 | 3502 | ||
| 3019 | current_offset += atom.size; | 3503 | if (!is_obj and (override_export or function.flags.isExported(rdynamic))) { |
| 3020 | if (atom.prev != .null) { | 3504 | const symbol_name = function.name.unwrap().?; |
| 3021 | atom_index = atom.prev; | 3505 | if (!override_export and function.flags.visibility_hidden) { |
| 3022 | } else { | 3506 | try wasm.hidden_function_exports.put(gpa, symbol_name, @enumFromInt(gop.index)); |
| 3023 | // also pad with zeroes when last atom to ensure | 3507 | } else { |
| 3024 | // segments are aligned. | 3508 | try wasm.function_exports.put(gpa, symbol_name, @enumFromInt(gop.index)); |
| 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 | } | 3509 | } |
| 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 | } | 3510 | } |
| 3045 | 3511 | ||
| 3046 | if (is_obj) { | 3512 | try wasm.markRelocations(function.relocations(wasm)); |
| 3047 | // relocations need to point to the index of a symbol in the final symbol table. To save memory, | 3513 | } |
| 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 | 3514 | ||
| 3093 | var debug_bytes = std.ArrayList(u8).init(gpa); | 3515 | fn markObject(wasm: *Wasm, i: ObjectIndex) void { |
| 3094 | defer debug_bytes.deinit(); | 3516 | i.ptr(wasm).is_included = true; |
| 3517 | } | ||
| 3095 | 3518 | ||
| 3096 | inline for (@typeInfo(CustomSections).@"struct".fields) |field| { | 3519 | /// Recursively mark alive everything referenced by the global. |
| 3097 | if (@field(wasm.custom_sections, field.name).index.unwrap()) |index| { | 3520 | fn markGlobalImport( |
| 3098 | var atom = wasm.getAtomPtr(wasm.atoms.get(index).?); | 3521 | wasm: *Wasm, |
| 3099 | while (true) { | 3522 | name: String, |
| 3100 | atom.resolveRelocs(wasm); | 3523 | import: *GlobalImport, |
| 3101 | try debug_bytes.appendSlice(atom.code.items); | 3524 | global_index: GlobalImport.Index, |
| 3102 | if (atom.prev == .null) break; | 3525 | ) link.File.FlushError!void { |
| 3103 | atom = wasm.getAtomPtr(atom.prev); | 3526 | if (import.flags.alive) return; |
| 3104 | } | 3527 | import.flags.alive = true; |
| 3105 | try emitDebugSection(&binary_bytes, debug_bytes.items, field.name); | ||
| 3106 | debug_bytes.clearRetainingCapacity(); | ||
| 3107 | } | ||
| 3108 | } | ||
| 3109 | 3528 | ||
| 3110 | try emitProducerSection(&binary_bytes); | 3529 | const comp = wasm.base.comp; |
| 3111 | if (feature_count > 0) { | 3530 | const gpa = comp.gpa; |
| 3112 | try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count); | ||
| 3113 | } | ||
| 3114 | } | ||
| 3115 | 3531 | ||
| 3116 | // Only when writing all sections executed properly we write the magic | 3532 | try wasm.globals.ensureUnusedCapacity(gpa, 1); |
| 3117 | // bytes. This allows us to easily detect what went wrong while generating | 3533 | |
| 3118 | // the final binary. | 3534 | if (import.resolution == .unresolved) { |
| 3119 | { | 3535 | if (name == wasm.preloaded_strings.__heap_base) { |
| 3120 | const src = std.wasm.magic ++ std.wasm.version; | 3536 | import.resolution = .__heap_base; |
| 3121 | binary_bytes.items[0..src.len].* = src; | 3537 | wasm.globals.putAssumeCapacity(.__heap_base, {}); |
| 3538 | } else if (name == wasm.preloaded_strings.__heap_end) { | ||
| 3539 | import.resolution = .__heap_end; | ||
| 3540 | wasm.globals.putAssumeCapacity(.__heap_end, {}); | ||
| 3541 | } else if (name == wasm.preloaded_strings.__stack_pointer) { | ||
| 3542 | import.resolution = .__stack_pointer; | ||
| 3543 | wasm.globals.putAssumeCapacity(.__stack_pointer, {}); | ||
| 3544 | } else if (name == wasm.preloaded_strings.__tls_align) { | ||
| 3545 | import.resolution = .__tls_align; | ||
| 3546 | wasm.globals.putAssumeCapacity(.__tls_align, {}); | ||
| 3547 | } else if (name == wasm.preloaded_strings.__tls_base) { | ||
| 3548 | import.resolution = .__tls_base; | ||
| 3549 | wasm.globals.putAssumeCapacity(.__tls_base, {}); | ||
| 3550 | } else if (name == wasm.preloaded_strings.__tls_size) { | ||
| 3551 | import.resolution = .__tls_size; | ||
| 3552 | wasm.globals.putAssumeCapacity(.__tls_size, {}); | ||
| 3553 | } else { | ||
| 3554 | try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm)); | ||
| 3555 | } | ||
| 3556 | } else { | ||
| 3557 | try markGlobal(wasm, import.resolution.unpack(wasm).object_global, import.flags.exported); | ||
| 3122 | } | 3558 | } |
| 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 | |||
| 3132 | fn 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 | } | 3559 | } |
| 3149 | 3560 | ||
| 3150 | fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { | 3561 | fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.File.FlushError!void { |
| 3151 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | 3562 | const comp = wasm.base.comp; |
| 3563 | const gpa = comp.gpa; | ||
| 3564 | const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i)); | ||
| 3565 | if (gop.found_existing) return; | ||
| 3152 | 3566 | ||
| 3153 | const writer = binary_bytes.writer(); | 3567 | const rdynamic = comp.config.rdynamic; |
| 3154 | const producers = "producers"; | 3568 | const is_obj = comp.config.output_mode == .Obj; |
| 3155 | try leb.writeUleb128(writer, @as(u32, @intCast(producers.len))); | 3569 | const global = i.ptr(wasm); |
| 3156 | try writer.writeAll(producers); | ||
| 3157 | 3570 | ||
| 3158 | try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by | 3571 | if (!is_obj and (override_export or global.flags.isExported(rdynamic))) try wasm.global_exports.append(gpa, .{ |
| 3572 | .name = global.name.unwrap().?, | ||
| 3573 | .global_index = @enumFromInt(gop.index), | ||
| 3574 | }); | ||
| 3159 | 3575 | ||
| 3160 | // used for the Zig version | 3576 | try wasm.markRelocations(global.relocations(wasm)); |
| 3161 | var version_buf: [100]u8 = undefined; | 3577 | } |
| 3162 | const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver}); | ||
| 3163 | 3578 | ||
| 3164 | // language field | 3579 | fn markTableImport( |
| 3165 | { | 3580 | wasm: *Wasm, |
| 3166 | const language = "language"; | 3581 | name: String, |
| 3167 | try leb.writeUleb128(writer, @as(u32, @intCast(language.len))); | 3582 | import: *TableImport, |
| 3168 | try writer.writeAll(language); | 3583 | table_index: TableImport.Index, |
| 3584 | ) link.File.FlushError!void { | ||
| 3585 | if (import.flags.alive) return; | ||
| 3586 | import.flags.alive = true; | ||
| 3169 | 3587 | ||
| 3170 | // field_value_count (TODO: Parse object files for producer sections to detect their language) | 3588 | const comp = wasm.base.comp; |
| 3171 | try leb.writeUleb128(writer, @as(u32, 1)); | 3589 | const gpa = comp.gpa; |
| 3172 | 3590 | ||
| 3173 | // versioned name | 3591 | try wasm.tables.ensureUnusedCapacity(gpa, 1); |
| 3174 | { | ||
| 3175 | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" | ||
| 3176 | try writer.writeAll("Zig"); | ||
| 3177 | 3592 | ||
| 3178 | try leb.writeUleb128(writer, @as(u32, @intCast(version.len))); | 3593 | if (import.resolution == .unresolved) { |
| 3179 | try writer.writeAll(version); | 3594 | if (name == wasm.preloaded_strings.__indirect_function_table) { |
| 3595 | import.resolution = .__indirect_function_table; | ||
| 3596 | wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); | ||
| 3597 | } else { | ||
| 3598 | try wasm.table_imports.put(gpa, name, table_index); | ||
| 3180 | } | 3599 | } |
| 3600 | } else { | ||
| 3601 | wasm.tables.putAssumeCapacity(import.resolution, {}); | ||
| 3602 | // Tables have no relocations. | ||
| 3181 | } | 3603 | } |
| 3604 | } | ||
| 3182 | 3605 | ||
| 3183 | // processed-by field | 3606 | fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void { |
| 3184 | { | 3607 | const comp = wasm.base.comp; |
| 3185 | const processed_by = "processed-by"; | 3608 | const segment = segment_index.ptr(wasm); |
| 3186 | try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len))); | 3609 | if (segment.flags.alive) return; |
| 3187 | try writer.writeAll(processed_by); | 3610 | segment.flags.alive = true; |
| 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 | 3611 | ||
| 3197 | try leb.writeUleb128(writer, @as(u32, @intCast(version.len))); | 3612 | wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or |
| 3198 | try writer.writeAll(version); | 3613 | (comp.config.import_memory and !wasm.isBss(segment.name)); |
| 3199 | } | ||
| 3200 | } | ||
| 3201 | 3614 | ||
| 3202 | try writeCustomSectionHeader( | 3615 | try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {}); |
| 3203 | binary_bytes.items, | 3616 | try wasm.markRelocations(segment.relocations(wasm)); |
| 3204 | header_offset, | ||
| 3205 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | ||
| 3206 | ); | ||
| 3207 | } | 3617 | } |
| 3208 | 3618 | ||
| 3209 | fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8), build_id: []const u8) !void { | 3619 | pub fn markDataImport( |
| 3210 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | 3620 | wasm: *Wasm, |
| 3211 | 3621 | name: String, | |
| 3212 | const writer = binary_bytes.writer(); | 3622 | import: *ObjectDataImport, |
| 3213 | const hdr_build_id = "build_id"; | 3623 | data_index: ObjectDataImport.Index, |
| 3214 | try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len))); | 3624 | ) link.File.FlushError!void { |
| 3215 | try writer.writeAll(hdr_build_id); | 3625 | if (import.flags.alive) return; |
| 3626 | import.flags.alive = true; | ||
| 3216 | 3627 | ||
| 3217 | try leb.writeUleb128(writer, @as(u32, 1)); | 3628 | const comp = wasm.base.comp; |
| 3218 | try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len))); | 3629 | const gpa = comp.gpa; |
| 3219 | try writer.writeAll(build_id); | ||
| 3220 | 3630 | ||
| 3221 | try writeCustomSectionHeader( | 3631 | if (import.resolution == .unresolved) { |
| 3222 | binary_bytes.items, | 3632 | if (name == wasm.preloaded_strings.__heap_base) { |
| 3223 | header_offset, | 3633 | import.resolution = .__heap_base; |
| 3224 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | 3634 | wasm.data_segments.putAssumeCapacity(.__heap_base, {}); |
| 3225 | ); | 3635 | } else if (name == wasm.preloaded_strings.__heap_end) { |
| 3636 | import.resolution = .__heap_end; | ||
| 3637 | wasm.data_segments.putAssumeCapacity(.__heap_end, {}); | ||
| 3638 | } else { | ||
| 3639 | try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm)); | ||
| 3640 | } | ||
| 3641 | } else if (import.resolution.objectDataSegment(wasm)) |segment_index| { | ||
| 3642 | try markDataSegment(wasm, segment_index); | ||
| 3643 | } | ||
| 3226 | } | 3644 | } |
| 3227 | 3645 | ||
| 3228 | fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void { | 3646 | fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void { |
| 3229 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | 3647 | const gpa = wasm.base.comp.gpa; |
| 3648 | for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| { | ||
| 3649 | if (offset >= relocs.end) break; | ||
| 3650 | switch (tag) { | ||
| 3651 | .function_import_index_leb, | ||
| 3652 | .function_import_index_i32, | ||
| 3653 | .function_import_offset_i32, | ||
| 3654 | .function_import_offset_i64, | ||
| 3655 | => { | ||
| 3656 | const name = pointee.symbol_name; | ||
| 3657 | const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?); | ||
| 3658 | try markFunctionImport(wasm, name, i.value(wasm), i); | ||
| 3659 | }, | ||
| 3660 | .table_import_index_sleb, | ||
| 3661 | .table_import_index_i32, | ||
| 3662 | .table_import_index_sleb64, | ||
| 3663 | .table_import_index_i64, | ||
| 3664 | .table_import_index_rel_sleb, | ||
| 3665 | .table_import_index_rel_sleb64, | ||
| 3666 | => { | ||
| 3667 | const name = pointee.symbol_name; | ||
| 3668 | try wasm.object_indirect_function_import_set.put(gpa, name, {}); | ||
| 3669 | const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?); | ||
| 3670 | try markFunctionImport(wasm, name, i.value(wasm), i); | ||
| 3671 | }, | ||
| 3672 | .global_import_index_leb, .global_import_index_i32 => { | ||
| 3673 | const name = pointee.symbol_name; | ||
| 3674 | const i: GlobalImport.Index = @enumFromInt(wasm.object_global_imports.getIndex(name).?); | ||
| 3675 | try markGlobalImport(wasm, name, i.value(wasm), i); | ||
| 3676 | }, | ||
| 3677 | .table_import_number_leb => { | ||
| 3678 | const name = pointee.symbol_name; | ||
| 3679 | const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?); | ||
| 3680 | try markTableImport(wasm, name, i.value(wasm), i); | ||
| 3681 | }, | ||
| 3682 | .memory_addr_import_leb, | ||
| 3683 | .memory_addr_import_sleb, | ||
| 3684 | .memory_addr_import_i32, | ||
| 3685 | .memory_addr_import_rel_sleb, | ||
| 3686 | .memory_addr_import_leb64, | ||
| 3687 | .memory_addr_import_sleb64, | ||
| 3688 | .memory_addr_import_i64, | ||
| 3689 | .memory_addr_import_rel_sleb64, | ||
| 3690 | .memory_addr_import_tls_sleb, | ||
| 3691 | .memory_addr_import_locrel_i32, | ||
| 3692 | .memory_addr_import_tls_sleb64, | ||
| 3693 | => { | ||
| 3694 | const name = pointee.symbol_name; | ||
| 3695 | const i = ObjectDataImport.Index.fromSymbolName(wasm, name).?; | ||
| 3696 | try markDataImport(wasm, name, i.value(wasm), i); | ||
| 3697 | }, | ||
| 3698 | |||
| 3699 | .function_index_leb, | ||
| 3700 | .function_index_i32, | ||
| 3701 | .function_offset_i32, | ||
| 3702 | .function_offset_i64, | ||
| 3703 | => try markFunction(wasm, pointee.function.chaseWeak(wasm), false), | ||
| 3704 | .table_index_sleb, | ||
| 3705 | .table_index_i32, | ||
| 3706 | .table_index_sleb64, | ||
| 3707 | .table_index_i64, | ||
| 3708 | .table_index_rel_sleb, | ||
| 3709 | .table_index_rel_sleb64, | ||
| 3710 | => { | ||
| 3711 | const function = pointee.function; | ||
| 3712 | try wasm.object_indirect_function_set.put(gpa, function, {}); | ||
| 3713 | try markFunction(wasm, function.chaseWeak(wasm), false); | ||
| 3714 | }, | ||
| 3715 | .global_index_leb, | ||
| 3716 | .global_index_i32, | ||
| 3717 | => try markGlobal(wasm, pointee.global.chaseWeak(wasm), false), | ||
| 3718 | .table_number_leb, | ||
| 3719 | => try markTable(wasm, pointee.table.chaseWeak(wasm)), | ||
| 3720 | |||
| 3721 | .section_offset_i32 => { | ||
| 3722 | log.warn("TODO: ensure section {d} is included in output", .{pointee.section}); | ||
| 3723 | }, | ||
| 3230 | 3724 | ||
| 3231 | const writer = binary_bytes.writer(); | 3725 | .memory_addr_leb, |
| 3232 | const target_features = "target_features"; | 3726 | .memory_addr_sleb, |
| 3233 | try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len))); | 3727 | .memory_addr_i32, |
| 3234 | try writer.writeAll(target_features); | 3728 | .memory_addr_rel_sleb, |
| 3729 | .memory_addr_leb64, | ||
| 3730 | .memory_addr_sleb64, | ||
| 3731 | .memory_addr_i64, | ||
| 3732 | .memory_addr_rel_sleb64, | ||
| 3733 | .memory_addr_tls_sleb, | ||
| 3734 | .memory_addr_locrel_i32, | ||
| 3735 | .memory_addr_tls_sleb64, | ||
| 3736 | => try markDataSegment(wasm, pointee.data.ptr(wasm).segment), | ||
| 3235 | 3737 | ||
| 3236 | try leb.writeUleb128(writer, features_count); | 3738 | .type_index_leb => continue, |
| 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 | } | 3739 | } |
| 3246 | } | 3740 | } |
| 3741 | } | ||
| 3247 | 3742 | ||
| 3248 | try writeCustomSectionHeader( | 3743 | fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void { |
| 3249 | binary_bytes.items, | 3744 | try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {}); |
| 3250 | header_offset, | ||
| 3251 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | ||
| 3252 | ); | ||
| 3253 | } | 3745 | } |
| 3254 | 3746 | ||
| 3255 | fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { | 3747 | pub fn flushModule( |
| 3748 | wasm: *Wasm, | ||
| 3749 | arena: Allocator, | ||
| 3750 | tid: Zcu.PerThread.Id, | ||
| 3751 | prog_node: std.Progress.Node, | ||
| 3752 | ) link.File.FlushError!void { | ||
| 3753 | // The goal is to never use this because it's only needed if we need to | ||
| 3754 | // write to InternPool, but flushModule is too late to be writing to the | ||
| 3755 | // InternPool. | ||
| 3756 | _ = tid; | ||
| 3256 | const comp = wasm.base.comp; | 3757 | const comp = wasm.base.comp; |
| 3257 | const import_memory = comp.config.import_memory; | 3758 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 3258 | const Name = struct { | 3759 | const diags = &comp.link_diags; |
| 3259 | index: u32, | 3760 | const gpa = comp.gpa; |
| 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 | 3761 | ||
| 3268 | // we must de-duplicate symbols that point to the same function | 3762 | if (wasm.llvm_object) |llvm_object| { |
| 3269 | var funcs = std.AutoArrayHashMap(u32, Name).init(arena); | 3763 | try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); |
| 3270 | try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count); | 3764 | if (use_lld) return; |
| 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 | } | 3765 | } |
| 3300 | 3766 | ||
| 3301 | mem.sort(Name, funcs.values(), {}, Name.lessThan); | 3767 | if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items); |
| 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 | 3768 | ||
| 3309 | try wasm.emitNameSubsection(.function, funcs.values(), writer); | 3769 | if (wasm.base.zcu_object_sub_path) |path| { |
| 3310 | try wasm.emitNameSubsection(.global, globals.items, writer); | 3770 | const module_obj_path: Path = .{ |
| 3311 | try wasm.emitNameSubsection(.data_segment, segments.items, writer); | 3771 | .root_dir = wasm.base.emit.root_dir, |
| 3772 | .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname| | ||
| 3773 | try fs.path.join(arena, &.{ dirname, path }) | ||
| 3774 | else | ||
| 3775 | path, | ||
| 3776 | }; | ||
| 3777 | openParseObjectReportingFailure(wasm, module_obj_path); | ||
| 3778 | try prelink(wasm, prog_node); | ||
| 3779 | } | ||
| 3312 | 3780 | ||
| 3313 | try writeCustomSectionHeader( | 3781 | const tracy = trace(@src()); |
| 3314 | binary_bytes.items, | 3782 | defer tracy.end(); |
| 3315 | header_offset, | ||
| 3316 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | ||
| 3317 | ); | ||
| 3318 | } | ||
| 3319 | 3783 | ||
| 3320 | fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { | 3784 | const sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 3321 | const gpa = wasm.base.comp.gpa; | 3785 | defer sub_prog_node.end(); |
| 3322 | 3786 | ||
| 3323 | // We must emit subsection size, so first write to a temporary list | 3787 | const functions_end_zcu: u32 = @intCast(wasm.functions.entries.len); |
| 3324 | var section_list = std.ArrayList(u8).init(gpa); | 3788 | defer wasm.functions.shrinkRetainingCapacity(functions_end_zcu); |
| 3325 | defer section_list.deinit(); | ||
| 3326 | const sub_writer = section_list.writer(); | ||
| 3327 | 3789 | ||
| 3328 | try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len))); | 3790 | const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len); |
| 3329 | for (names) |name| { | 3791 | defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu); |
| 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 | } | ||
| 3335 | 3792 | ||
| 3336 | // From now, write to the actual writer | 3793 | const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len); |
| 3337 | try leb.writeUleb128(writer, @intFromEnum(section_id)); | 3794 | defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu); |
| 3338 | try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len))); | ||
| 3339 | try writer.writeAll(section_list.items); | ||
| 3340 | } | ||
| 3341 | 3795 | ||
| 3342 | fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void { | 3796 | const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len); |
| 3343 | try writer.writeByte(limits.flags); | 3797 | defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu); |
| 3344 | try leb.writeUleb128(writer, limits.min); | ||
| 3345 | if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) { | ||
| 3346 | try leb.writeUleb128(writer, limits.max); | ||
| 3347 | } | ||
| 3348 | } | ||
| 3349 | 3798 | ||
| 3350 | fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { | 3799 | wasm.flush_buffer.clear(); |
| 3351 | switch (init_expr) { | 3800 | try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{}); |
| 3352 | .i32_const => |val| { | 3801 | try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values()); |
| 3353 | try writer.writeByte(std.wasm.opcode(.i32_const)); | 3802 | try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values()); |
| 3354 | try leb.writeIleb128(writer, val); | 3803 | try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values()); |
| 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 | } | ||
| 3375 | 3804 | ||
| 3376 | fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void { | 3805 | return wasm.flush_buffer.finish(wasm) catch |err| switch (err) { |
| 3377 | const module_name = wasm.stringSlice(import.module_name); | 3806 | error.OutOfMemory => return error.OutOfMemory, |
| 3378 | try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len))); | 3807 | error.LinkFailure => return error.LinkFailure, |
| 3379 | try writer.writeAll(module_name); | 3808 | else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}), |
| 3380 | 3809 | }; | |
| 3381 | const name = wasm.stringSlice(import.name); | ||
| 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 | } | 3810 | } |
| 3401 | 3811 | ||
| 3402 | fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { | 3812 | fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { |
| ... | @@ -3406,6 +3816,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -3406,6 +3816,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3406 | defer tracy.end(); | 3816 | defer tracy.end(); |
| 3407 | 3817 | ||
| 3408 | const comp = wasm.base.comp; | 3818 | const comp = wasm.base.comp; |
| 3819 | const diags = &comp.link_diags; | ||
| 3409 | const shared_memory = comp.config.shared_memory; | 3820 | const shared_memory = comp.config.shared_memory; |
| 3410 | const export_memory = comp.config.export_memory; | 3821 | const export_memory = comp.config.export_memory; |
| 3411 | const import_memory = comp.config.import_memory; | 3822 | const import_memory = comp.config.import_memory; |
| ... | @@ -3459,7 +3870,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -3459,7 +3870,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3459 | } | 3870 | } |
| 3460 | try man.addOptionalFile(module_obj_path); | 3871 | try man.addOptionalFile(module_obj_path); |
| 3461 | try man.addOptionalFilePath(compiler_rt_path); | 3872 | try man.addOptionalFilePath(compiler_rt_path); |
| 3462 | man.hash.addOptionalBytes(wasm.optionalStringSlice(wasm.entry_name)); | 3873 | man.hash.addOptionalBytes(wasm.entry_name.slice(wasm)); |
| 3463 | man.hash.add(wasm.base.stack_size); | 3874 | man.hash.add(wasm.base.stack_size); |
| 3464 | man.hash.add(wasm.base.build_id); | 3875 | man.hash.add(wasm.base.build_id); |
| 3465 | man.hash.add(import_memory); | 3876 | man.hash.add(import_memory); |
| ... | @@ -3608,7 +4019,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -3608,7 +4019,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3608 | try argv.append("--export-dynamic"); | 4019 | try argv.append("--export-dynamic"); |
| 3609 | } | 4020 | } |
| 3610 | 4021 | ||
| 3611 | if (wasm.optionalStringSlice(wasm.entry_name)) |entry_name| { | 4022 | if (wasm.entry_name.slice(wasm)) |entry_name| { |
| 3612 | try argv.appendSlice(&.{ "--entry", entry_name }); | 4023 | try argv.appendSlice(&.{ "--entry", entry_name }); |
| 3613 | } else { | 4024 | } else { |
| 3614 | try argv.append("--no-entry"); | 4025 | try argv.append("--no-entry"); |
| ... | @@ -3750,14 +4161,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -3750,14 +4161,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3750 | switch (term) { | 4161 | switch (term) { |
| 3751 | .Exited => |code| { | 4162 | .Exited => |code| { |
| 3752 | if (code != 0) { | 4163 | if (code != 0) { |
| 3753 | const diags = &comp.link_diags; | ||
| 3754 | diags.lockAndParseLldStderr(linker_command, stderr); | 4164 | diags.lockAndParseLldStderr(linker_command, stderr); |
| 3755 | return error.LLDReportedFailure; | 4165 | return error.LinkFailure; |
| 3756 | } | 4166 | } |
| 3757 | }, | 4167 | }, |
| 3758 | else => { | 4168 | else => { |
| 3759 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | 4169 | return diags.fail("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); |
| 3760 | return error.LLDCrashed; | ||
| 3761 | }, | 4170 | }, |
| 3762 | } | 4171 | } |
| 3763 | 4172 | ||
| ... | @@ -3771,7 +4180,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -3771,7 +4180,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3771 | if (comp.clang_passthrough_mode) { | 4180 | if (comp.clang_passthrough_mode) { |
| 3772 | std.process.exit(exit_code); | 4181 | std.process.exit(exit_code); |
| 3773 | } else { | 4182 | } else { |
| 3774 | return error.LLDReportedFailure; | 4183 | return diags.fail("{s} returned exit code {d}:\n{s}", .{ argv.items[0], exit_code }); |
| 3775 | } | 4184 | } |
| 3776 | } | 4185 | } |
| 3777 | } | 4186 | } |
| ... | @@ -3811,969 +4220,507 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: | ... | @@ -3811,969 +4220,507 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3811 | } | 4220 | } |
| 3812 | } | 4221 | } |
| 3813 | 4222 | ||
| 3814 | fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 { | 4223 | fn defaultEntrySymbolName( |
| 3815 | // section id + fixed leb contents size + fixed leb vector length | 4224 | preloaded_strings: *const PreloadedStrings, |
| 3816 | const header_size = 1 + 5 + 5; | 4225 | wasi_exec_model: std.builtin.WasiExecModel, |
| 3817 | const offset = @as(u32, @intCast(bytes.items.len)); | 4226 | ) String { |
| 3818 | try bytes.appendSlice(&[_]u8{0} ** header_size); | 4227 | return switch (wasi_exec_model) { |
| 3819 | return offset; | 4228 | .reactor => preloaded_strings._initialize, |
| 4229 | .command => preloaded_strings._start, | ||
| 4230 | }; | ||
| 4231 | } | ||
| 4232 | |||
| 4233 | pub fn internOptionalString(wasm: *Wasm, optional_bytes: ?[]const u8) Allocator.Error!OptionalString { | ||
| 4234 | const bytes = optional_bytes orelse return .none; | ||
| 4235 | const string = try internString(wasm, bytes); | ||
| 4236 | return string.toOptional(); | ||
| 4237 | } | ||
| 4238 | |||
| 4239 | pub fn internString(wasm: *Wasm, bytes: []const u8) Allocator.Error!String { | ||
| 4240 | assert(mem.indexOfScalar(u8, bytes, 0) == null); | ||
| 4241 | wasm.string_bytes_lock.lock(); | ||
| 4242 | defer wasm.string_bytes_lock.unlock(); | ||
| 4243 | const gpa = wasm.base.comp.gpa; | ||
| 4244 | const gop = try wasm.string_table.getOrPutContextAdapted( | ||
| 4245 | gpa, | ||
| 4246 | @as([]const u8, bytes), | ||
| 4247 | @as(String.TableIndexAdapter, .{ .bytes = wasm.string_bytes.items }), | ||
| 4248 | @as(String.TableContext, .{ .bytes = wasm.string_bytes.items }), | ||
| 4249 | ); | ||
| 4250 | if (gop.found_existing) return gop.key_ptr.*; | ||
| 4251 | |||
| 4252 | try wasm.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1); | ||
| 4253 | const new_off: String = @enumFromInt(wasm.string_bytes.items.len); | ||
| 4254 | |||
| 4255 | wasm.string_bytes.appendSliceAssumeCapacity(bytes); | ||
| 4256 | wasm.string_bytes.appendAssumeCapacity(0); | ||
| 4257 | |||
| 4258 | gop.key_ptr.* = new_off; | ||
| 4259 | |||
| 4260 | return new_off; | ||
| 3820 | } | 4261 | } |
| 3821 | 4262 | ||
| 3822 | fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u32 { | 4263 | // TODO implement instead by appending to string_bytes |
| 3823 | // unlike regular section, we don't emit the count | 4264 | pub fn internStringFmt(wasm: *Wasm, comptime format: []const u8, args: anytype) Allocator.Error!String { |
| 3824 | const header_size = 1 + 5; | 4265 | var buffer: [32]u8 = undefined; |
| 3825 | const offset = @as(u32, @intCast(bytes.items.len)); | 4266 | const slice = std.fmt.bufPrint(&buffer, format, args) catch unreachable; |
| 3826 | try bytes.appendSlice(&[_]u8{0} ** header_size); | 4267 | return internString(wasm, slice); |
| 3827 | return offset; | ||
| 3828 | } | 4268 | } |
| 3829 | 4269 | ||
| 3830 | fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void { | 4270 | pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String { |
| 3831 | var buf: [1 + 5 + 5]u8 = undefined; | 4271 | assert(mem.indexOfScalar(u8, bytes, 0) == null); |
| 3832 | buf[0] = @intFromEnum(section); | 4272 | return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{ |
| 3833 | leb.writeUnsignedFixed(5, buf[1..6], size); | 4273 | .bytes = wasm.string_bytes.items, |
| 3834 | leb.writeUnsignedFixed(5, buf[6..], items); | 4274 | })); |
| 3835 | buffer[offset..][0..buf.len].* = buf; | ||
| 3836 | } | 4275 | } |
| 3837 | 4276 | ||
| 3838 | fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void { | 4277 | pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) Allocator.Error!ValtypeList { |
| 3839 | var buf: [1 + 5]u8 = undefined; | 4278 | return .fromString(try internString(wasm, @ptrCast(valtype_list))); |
| 3840 | buf[0] = 0; // 0 = 'custom' section | ||
| 3841 | leb.writeUnsignedFixed(5, buf[1..6], size); | ||
| 3842 | buffer[offset..][0..buf.len].* = buf; | ||
| 3843 | } | 4279 | } |
| 3844 | 4280 | ||
| 3845 | fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { | 4281 | pub fn getExistingValtypeList(wasm: *const Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList { |
| 3846 | const offset = try reserveCustomSectionHeader(binary_bytes); | 4282 | return .fromString(getExistingString(wasm, @ptrCast(valtype_list)) orelse return null); |
| 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 | } | 4283 | } |
| 3864 | 4284 | ||
| 3865 | fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { | 4285 | pub fn addFuncType(wasm: *Wasm, ft: FunctionType) Allocator.Error!FunctionType.Index { |
| 3866 | const writer = binary_bytes.writer(); | 4286 | const gpa = wasm.base.comp.gpa; |
| 3867 | 4287 | const gop = try wasm.func_types.getOrPut(gpa, ft); | |
| 3868 | try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE)); | 4288 | return @enumFromInt(gop.index); |
| 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 | } | 4289 | } |
| 3913 | 4290 | ||
| 3914 | fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { | 4291 | pub fn getExistingFuncType(wasm: *const Wasm, ft: FunctionType) ?FunctionType.Index { |
| 3915 | const writer = binary_bytes.writer(); | 4292 | const index = wasm.func_types.getIndex(ft) orelse return null; |
| 3916 | try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO)); | 4293 | return @enumFromInt(index); |
| 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 | } | 4294 | } |
| 3936 | 4295 | ||
| 3937 | pub fn getUleb128Size(uint_value: anytype) u32 { | 4296 | pub fn getExistingFuncType2(wasm: *const Wasm, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype) FunctionType.Index { |
| 3938 | const T = @TypeOf(uint_value); | 4297 | return getExistingFuncType(wasm, .{ |
| 3939 | const U = if (@typeInfo(T).int.bits < 8) u8 else T; | 4298 | .params = getExistingValtypeList(wasm, params).?, |
| 3940 | var value = @as(U, @intCast(uint_value)); | 4299 | .returns = getExistingValtypeList(wasm, returns).?, |
| 3941 | 4300 | }).?; | |
| 3942 | var size: u32 = 0; | ||
| 3943 | while (value != 0) : (size += 1) { | ||
| 3944 | value >>= 7; | ||
| 3945 | } | ||
| 3946 | return size; | ||
| 3947 | } | 4301 | } |
| 3948 | 4302 | ||
| 3949 | /// For each relocatable section, emits a custom "relocation.<section_name>" section | 4303 | pub fn internFunctionType( |
| 3950 | fn emitCodeRelocations( | ||
| 3951 | wasm: *Wasm, | 4304 | wasm: *Wasm, |
| 3952 | binary_bytes: *std.ArrayList(u8), | 4305 | cc: std.builtin.CallingConvention, |
| 3953 | section_index: u32, | 4306 | params: []const InternPool.Index, |
| 3954 | symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), | 4307 | return_type: Zcu.Type, |
| 3955 | ) !void { | 4308 | target: *const std.Target, |
| 3956 | const code_index = wasm.code_section_index.unwrap() orelse return; | 4309 | ) Allocator.Error!FunctionType.Index { |
| 3957 | const writer = binary_bytes.writer(); | 4310 | try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch); |
| 3958 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | 4311 | return wasm.addFuncType(.{ |
| 3959 | 4312 | .params = try wasm.internValtypeList(wasm.params_scratch.items), | |
| 3960 | // write custom section information | 4313 | .returns = try wasm.internValtypeList(wasm.returns_scratch.items), |
| 3961 | const name = "reloc.CODE"; | 4314 | }); |
| 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 | } | 4315 | } |
| 3996 | 4316 | ||
| 3997 | fn emitDataRelocations( | 4317 | pub fn getExistingFunctionType( |
| 3998 | wasm: *Wasm, | 4318 | wasm: *Wasm, |
| 3999 | binary_bytes: *std.ArrayList(u8), | 4319 | cc: std.builtin.CallingConvention, |
| 4000 | section_index: u32, | 4320 | params: []const InternPool.Index, |
| 4001 | symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), | 4321 | return_type: Zcu.Type, |
| 4002 | ) !void { | 4322 | target: *const std.Target, |
| 4003 | if (wasm.data_segments.count() == 0) return; | 4323 | ) ?FunctionType.Index { |
| 4004 | const writer = binary_bytes.writer(); | 4324 | convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) { |
| 4005 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | 4325 | error.OutOfMemory => return null, |
| 4006 | 4326 | }; | |
| 4007 | // write custom section information | 4327 | return wasm.getExistingFuncType(.{ |
| 4008 | const name = "reloc.DATA"; | 4328 | .params = wasm.getExistingValtypeList(wasm.params_scratch.items) orelse return null, |
| 4009 | try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); | 4329 | .returns = wasm.getExistingValtypeList(wasm.returns_scratch.items) orelse return null, |
| 4010 | try writer.writeAll(name); | 4330 | }); |
| 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 | } | 4331 | } |
| 4046 | 4332 | ||
| 4047 | fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { | 4333 | pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr { |
| 4048 | const comp = wasm.base.comp; | 4334 | const gpa = wasm.base.comp.gpa; |
| 4049 | const import_memory = comp.config.import_memory; | 4335 | // We can't use string table deduplication here since these expressions can |
| 4050 | 4336 | // have null bytes in them however it may be interesting to explore since | |
| 4051 | var it = wasm.data_segments.iterator(); | 4337 | // it is likely for globals to share initialization values. Then again |
| 4052 | while (it.next()) |entry| { | 4338 | // there may not be very many globals in total. |
| 4053 | const segment = wasm.segmentPtr(entry.value_ptr.*); | 4339 | try wasm.string_bytes.appendSlice(gpa, bytes); |
| 4054 | if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) { | 4340 | return @enumFromInt(wasm.string_bytes.items.len - bytes.len); |
| 4055 | return true; | ||
| 4056 | } | ||
| 4057 | } | ||
| 4058 | return false; | ||
| 4059 | } | 4341 | } |
| 4060 | 4342 | ||
| 4061 | /// Searches for a matching function signature. When no matching signature is found, | 4343 | pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataPayload { |
| 4062 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. | ||
| 4063 | pub 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; | 4344 | const gpa = wasm.base.comp.gpa; |
| 4070 | const index: u32 = @intCast(wasm.func_types.items.len); | 4345 | try wasm.string_bytes.appendSlice(gpa, bytes); |
| 4071 | const params = try gpa.dupe(std.wasm.Valtype, func_type.params); | 4346 | return .{ |
| 4072 | errdefer gpa.free(params); | 4347 | .off = @enumFromInt(wasm.string_bytes.items.len - bytes.len), |
| 4073 | const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns); | 4348 | .len = @intCast(bytes.len), |
| 4074 | errdefer gpa.free(returns); | 4349 | }; |
| 4075 | try wasm.func_types.append(gpa, .{ | ||
| 4076 | .params = params, | ||
| 4077 | .returns = returns, | ||
| 4078 | }); | ||
| 4079 | return index; | ||
| 4080 | } | 4350 | } |
| 4081 | 4351 | ||
| 4082 | /// For the given `nav`, stores the corresponding type representing the function signature. | 4352 | pub fn uavSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex { |
| 4083 | /// Asserts declaration has an associated `Atom`. | 4353 | const comp = wasm.base.comp; |
| 4084 | /// Returns the index into the list of types. | 4354 | assert(comp.config.output_mode == .Obj); |
| 4085 | pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 { | 4355 | const gpa = comp.gpa; |
| 4086 | return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type); | 4356 | const name = try wasm.internStringFmt("__anon_{d}", .{@intFromEnum(ip_index)}); |
| 4357 | const gop = try wasm.symbol_table.getOrPut(gpa, name); | ||
| 4358 | gop.value_ptr.* = {}; | ||
| 4359 | return @enumFromInt(gop.index); | ||
| 4087 | } | 4360 | } |
| 4088 | 4361 | ||
| 4089 | /// Returns the symbol index of the error name table. | 4362 | pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Error!SymbolTableIndex { |
| 4090 | /// | 4363 | const comp = wasm.base.comp; |
| 4091 | /// When the symbol does not yet exist, it will create a new one instead. | 4364 | assert(comp.config.output_mode == .Obj); |
| 4092 | pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 { | 4365 | const zcu = comp.zcu.?; |
| 4093 | const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt); | 4366 | const ip = &zcu.intern_pool; |
| 4094 | return @intFromEnum(sym_index); | 4367 | const gpa = comp.gpa; |
| 4368 | const nav = ip.getNav(nav_index); | ||
| 4369 | const name = try wasm.internString(nav.fqn.toSlice(ip)); | ||
| 4370 | const gop = try wasm.symbol_table.getOrPut(gpa, name); | ||
| 4371 | gop.value_ptr.* = {}; | ||
| 4372 | return @enumFromInt(gop.index); | ||
| 4095 | } | 4373 | } |
| 4096 | 4374 | ||
| 4097 | /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`. | 4375 | pub fn errorNameTableSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex { |
| 4098 | /// When the index was not found, a new `Atom` will be created, and its index will be returned. | 4376 | const comp = wasm.base.comp; |
| 4099 | /// The newly created Atom is empty with default fields as specified by `Atom.empty`. | 4377 | assert(comp.config.output_mode == .Obj); |
| 4100 | pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index { | 4378 | const gpa = comp.gpa; |
| 4101 | return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav); | 4379 | const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__zig_error_name_table); |
| 4380 | gop.value_ptr.* = {}; | ||
| 4381 | return @enumFromInt(gop.index); | ||
| 4102 | } | 4382 | } |
| 4103 | 4383 | ||
| 4104 | /// Verifies all resolved symbols and checks whether itself needs to be marked alive, | 4384 | pub fn stackPointerSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex { |
| 4105 | /// as well as any of its references. | ||
| 4106 | fn 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; | 4385 | const comp = wasm.base.comp; |
| 4112 | 4386 | assert(comp.config.output_mode == .Obj); | |
| 4113 | for (wasm.resolved_symbols.keys()) |sym_loc| { | 4387 | const gpa = comp.gpa; |
| 4114 | const sym = wasm.symbolLocSymbol(sym_loc); | 4388 | const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__stack_pointer); |
| 4115 | if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { | 4389 | gop.value_ptr.* = {}; |
| 4116 | try wasm.mark(sym_loc); | 4390 | return @enumFromInt(gop.index); |
| 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 | } | 4391 | } |
| 4129 | 4392 | ||
| 4130 | /// Marks a symbol as 'alive' recursively so itself and any references it contains to | 4393 | pub fn tagNameSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex { |
| 4131 | /// other symbols will not be omit from the binary. | 4394 | const comp = wasm.base.comp; |
| 4132 | fn mark(wasm: *Wasm, loc: SymbolLoc) !void { | 4395 | assert(comp.config.output_mode == .Obj); |
| 4133 | const symbol = wasm.symbolLocSymbol(loc); | 4396 | const gpa = comp.gpa; |
| 4134 | if (symbol.isAlive()) { | 4397 | const name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(ip_index)}); |
| 4135 | // Symbol is already marked alive, including its references. | 4398 | const gop = try wasm.symbol_table.getOrPut(gpa, name); |
| 4136 | // This means we can skip it so we don't end up marking the same symbols | 4399 | gop.value_ptr.* = {}; |
| 4137 | // multiple times. | 4400 | return @enumFromInt(gop.index); |
| 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 | } | 4401 | } |
| 4159 | 4402 | ||
| 4160 | fn defaultEntrySymbolName( | 4403 | pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableIndex { |
| 4161 | preloaded_strings: *const PreloadedStrings, | 4404 | const comp = wasm.base.comp; |
| 4162 | wasi_exec_model: std.builtin.WasiExecModel, | 4405 | assert(comp.config.output_mode == .Obj); |
| 4163 | ) String { | 4406 | const gpa = comp.gpa; |
| 4164 | return switch (wasi_exec_model) { | 4407 | const gop = try wasm.symbol_table.getOrPut(gpa, name); |
| 4165 | .reactor => preloaded_strings._initialize, | 4408 | gop.value_ptr.* = {}; |
| 4166 | .command => preloaded_strings._start, | 4409 | return @enumFromInt(gop.index); |
| 4167 | }; | ||
| 4168 | } | 4410 | } |
| 4169 | 4411 | ||
| 4170 | pub const Atom = struct { | 4412 | pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex { |
| 4171 | /// Represents the index of the file this atom was generated from. | 4413 | const comp = wasm.base.comp; |
| 4172 | /// This is `none` when the atom was generated by a synthetic linker symbol. | 4414 | const zcu = comp.zcu.?; |
| 4173 | file: OptionalObjectId, | 4415 | const ip = &zcu.intern_pool; |
| 4174 | /// symbol index of the symbol representing this atom | 4416 | const gpa = comp.gpa; |
| 4175 | sym_index: Symbol.Index, | 4417 | assert(comp.config.output_mode == .Obj); |
| 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 | 4418 | ||
| 4230 | /// Returns the location of the symbol that represents this `Atom` | 4419 | if (orig_ptr_ty != .none) { |
| 4231 | pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { | 4420 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); |
| 4232 | return .{ | 4421 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; |
| 4233 | .file = atom.file, | 4422 | if (explicit_alignment.compare(.gt, abi_alignment)) { |
| 4234 | .index = atom.sym_index, | 4423 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); |
| 4235 | }; | 4424 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; |
| 4425 | } | ||
| 4236 | } | 4426 | } |
| 4237 | 4427 | ||
| 4238 | /// Resolves the relocations within the atom, writing the new value | 4428 | const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index); |
| 4239 | /// at the calculated offset. | 4429 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 4240 | pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void { | 4430 | // Lowering the value is delayed to avoid recursion. |
| 4241 | if (atom.relocs.items.len == 0) return; | 4431 | .code = undefined, |
| 4242 | const symbol_name = wasm.symbolLocName(atom.symbolLoc()); | 4432 | .relocs = undefined, |
| 4243 | log.debug("Resolving relocs in atom '{s}' count({d})", .{ | 4433 | }; |
| 4244 | symbol_name, | 4434 | return @enumFromInt(gop.index); |
| 4245 | atom.relocs.items.len, | 4435 | } |
| 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 | 4436 | ||
| 4260 | switch (reloc.relocation_type) { | 4437 | pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex { |
| 4261 | .R_WASM_TABLE_INDEX_I32, | 4438 | const comp = wasm.base.comp; |
| 4262 | .R_WASM_FUNCTION_OFFSET_I32, | 4439 | const zcu = comp.zcu.?; |
| 4263 | .R_WASM_GLOBAL_INDEX_I32, | 4440 | const ip = &zcu.intern_pool; |
| 4264 | .R_WASM_MEMORY_ADDR_I32, | 4441 | const gpa = comp.gpa; |
| 4265 | .R_WASM_SECTION_OFFSET_I32, | 4442 | assert(comp.config.output_mode != .Obj); |
| 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 | 4443 | ||
| 4289 | /// From a given `relocation` will return the new value to be written. | 4444 | if (orig_ptr_ty != .none) { |
| 4290 | /// All values will be represented as a `u64` as all values can fit within it. | 4445 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); |
| 4291 | /// The final value must be casted to the correct size. | 4446 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; |
| 4292 | fn relocationValue(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 { | 4447 | if (explicit_alignment.compare(.gt, abi_alignment)) { |
| 4293 | const target_loc = wasm.symbolLocFinalLoc(.{ | 4448 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); |
| 4294 | .file = atom.file, | 4449 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; |
| 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 | } | 4450 | } |
| 4359 | } | 4451 | } |
| 4360 | 4452 | ||
| 4361 | // For a given `Atom` returns whether it has a tombstone value or not. | 4453 | const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index); |
| 4362 | /// This defines whether we want a specific value when a section is dead. | 4454 | if (gop.found_existing) { |
| 4363 | fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 { | 4455 | gop.value_ptr.count += 1; |
| 4364 | const atom_name = wasm.symbolLocSymbol(atom.symbolLoc()).name; | 4456 | } else { |
| 4365 | if (atom_name == wasm.custom_sections.@".debug_ranges".name or | 4457 | gop.value_ptr.* = .{ |
| 4366 | atom_name == wasm.custom_sections.@".debug_loc".name) | 4458 | // Lowering the value is delayed to avoid recursion. |
| 4367 | { | 4459 | .code = undefined, |
| 4368 | return -2; | 4460 | .count = 1, |
| 4369 | } else if (std.mem.startsWith(u8, wasm.stringSlice(atom_name), ".debug_")) { | 4461 | }; |
| 4370 | return -1; | ||
| 4371 | } else { | ||
| 4372 | return null; | ||
| 4373 | } | ||
| 4374 | } | 4462 | } |
| 4375 | }; | 4463 | return @enumFromInt(gop.index); |
| 4464 | } | ||
| 4376 | 4465 | ||
| 4377 | pub const Relocation = struct { | 4466 | pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { |
| 4378 | /// Represents the type of the `Relocation` | 4467 | const comp = wasm.base.comp; |
| 4379 | relocation_type: RelocationType, | 4468 | const gpa = comp.gpa; |
| 4380 | /// Offset of the value to rewrite relative to the relevant section's contents. | 4469 | assert(comp.config.output_mode != .Obj); |
| 4381 | /// When `offset` is zero, its position is immediately after the id and size of the section. | 4470 | const gop = try wasm.navs_obj.getOrPut(gpa, nav_index); |
| 4382 | offset: u32, | 4471 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 4383 | /// The index of the symbol used. | 4472 | // Lowering the value is delayed to avoid recursion. |
| 4384 | /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type. | 4473 | .code = undefined, |
| 4385 | index: u32, | 4474 | .relocs = undefined, |
| 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 | }; | 4475 | }; |
| 4476 | return @enumFromInt(gop.index); | ||
| 4477 | } | ||
| 4434 | 4478 | ||
| 4435 | /// Verifies the relocation type of a given `Relocation` and returns | 4479 | pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { |
| 4436 | /// true when the relocation references a function call or address to a function. | 4480 | const comp = wasm.base.comp; |
| 4437 | pub fn isFunction(self: Relocation) bool { | 4481 | const gpa = comp.gpa; |
| 4438 | return switch (self.relocation_type) { | 4482 | assert(comp.config.output_mode != .Obj); |
| 4439 | .R_WASM_FUNCTION_INDEX_LEB, | 4483 | const gop = try wasm.navs_exe.getOrPut(gpa, nav_index); |
| 4440 | .R_WASM_TABLE_INDEX_SLEB, | 4484 | if (gop.found_existing) { |
| 4441 | => true, | 4485 | gop.value_ptr.count += 1; |
| 4442 | else => false, | 4486 | } else { |
| 4487 | gop.value_ptr.* = .{ | ||
| 4488 | // Lowering the value is delayed to avoid recursion. | ||
| 4489 | .code = undefined, | ||
| 4490 | .count = 0, | ||
| 4443 | }; | 4491 | }; |
| 4444 | } | 4492 | } |
| 4493 | return @enumFromInt(gop.index); | ||
| 4494 | } | ||
| 4445 | 4495 | ||
| 4446 | pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | 4496 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4447 | _ = fmt; | 4497 | pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 { |
| 4448 | _ = options; | 4498 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4449 | try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{ | 4499 | const comp = wasm.base.comp; |
| 4450 | @tagName(self.relocation_type), | 4500 | assert(comp.config.output_mode != .Obj); |
| 4451 | self.offset, | 4501 | const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); |
| 4452 | self.index, | 4502 | return wasm.flush_buffer.data_segments.get(ds_id).?; |
| 4453 | }); | 4503 | } |
| 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. | ||
| 4461 | pub 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. | ||
| 4471 | pub const Export = struct { | ||
| 4472 | name: String, | ||
| 4473 | index: u32, | ||
| 4474 | kind: std.wasm.ExternalKind, | ||
| 4475 | }; | ||
| 4476 | |||
| 4477 | pub 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 | |||
| 4484 | pub const Alignment = @import("../InternPool.zig").Alignment; | ||
| 4485 | |||
| 4486 | pub 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 | |||
| 4524 | pub 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 | |||
| 4531 | pub 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 | |||
| 4538 | pub 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 | |||
| 4554 | pub 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 | 4504 | ||
| 4623 | pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{ | 4505 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4624 | .{ "atomics", .atomics }, | 4506 | pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 { |
| 4625 | .{ "bulk-memory", .bulk_memory }, | 4507 | assert(wasm.flush_buffer.memory_layout_finished); |
| 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 | ||
| 4642 | fn 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; | 4508 | const comp = wasm.base.comp; |
| 4646 | const gpa = comp.gpa; | 4509 | assert(comp.config.output_mode != .Obj); |
| 4647 | const symbol = &object.symtable[@intFromEnum(symbol_index)]; | 4510 | if (wasm.navs_exe.getIndex(nav_index)) |i| { |
| 4648 | const relocatable_data: Object.RelocatableData = switch (symbol.tag) { | 4511 | const navs_exe_index: NavsExeIndex = @enumFromInt(i); |
| 4649 | .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count], | 4512 | log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index }); |
| 4650 | .data => object.relocatable_data.get(.data).?[symbol.index], | 4513 | const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index }); |
| 4651 | .section => blk: { | 4514 | return wasm.flush_buffer.data_segments.get(ds_id).?; |
| 4652 | const data = object.relocatable_data.get(.custom).?; | 4515 | } |
| 4653 | for (data) |dat| { | 4516 | const zcu = comp.zcu.?; |
| 4654 | if (dat.section_index == symbol.index) { | 4517 | const ip = &zcu.intern_pool; |
| 4655 | break :blk dat; | 4518 | const nav = ip.getNav(nav_index); |
| 4519 | if (nav.getResolvedExtern(ip)) |ext| { | ||
| 4520 | if (wasm.getExistingString(ext.name.toSlice(ip))) |symbol_name| { | ||
| 4521 | if (wasm.object_data_imports.getPtr(symbol_name)) |import| { | ||
| 4522 | switch (import.resolution.unpack(wasm)) { | ||
| 4523 | .unresolved => unreachable, | ||
| 4524 | .object => |object_data_index| { | ||
| 4525 | const object_data = object_data_index.ptr(wasm); | ||
| 4526 | const ds_id: DataSegmentId = .fromObjectDataSegment(wasm, object_data.segment); | ||
| 4527 | const segment_base_addr = wasm.flush_buffer.data_segments.get(ds_id).?; | ||
| 4528 | return segment_base_addr + object_data.offset; | ||
| 4529 | }, | ||
| 4530 | .__zig_error_names => @panic("TODO"), | ||
| 4531 | .__zig_error_name_table => @panic("TODO"), | ||
| 4532 | .__heap_base => @panic("TODO"), | ||
| 4533 | .__heap_end => @panic("TODO"), | ||
| 4534 | .uav_exe => @panic("TODO"), | ||
| 4535 | .uav_obj => @panic("TODO"), | ||
| 4536 | .nav_exe => @panic("TODO"), | ||
| 4537 | .nav_obj => @panic("TODO"), | ||
| 4656 | } | 4538 | } |
| 4657 | } | 4539 | } |
| 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 | } | 4540 | } |
| 4707 | } | 4541 | } |
| 4542 | // Otherwise it's a zero bit type; any address will do. | ||
| 4543 | return 0; | ||
| 4544 | } | ||
| 4708 | 4545 | ||
| 4709 | return atom_index; | 4546 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4547 | pub fn errorNameTableAddr(wasm: *Wasm) u32 { | ||
| 4548 | assert(wasm.flush_buffer.memory_layout_finished); | ||
| 4549 | const comp = wasm.base.comp; | ||
| 4550 | assert(comp.config.output_mode != .Obj); | ||
| 4551 | return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?; | ||
| 4710 | } | 4552 | } |
| 4711 | 4553 | ||
| 4712 | fn searchRelocStart(relocs: []const Wasm.Relocation, address: u32) usize { | 4554 | fn convertZcuFnType( |
| 4713 | var min: usize = 0; | 4555 | comp: *Compilation, |
| 4714 | var max: usize = relocs.len; | 4556 | cc: std.builtin.CallingConvention, |
| 4715 | while (min < max) { | 4557 | params: []const InternPool.Index, |
| 4716 | const index = (min + max) / 2; | 4558 | return_type: Zcu.Type, |
| 4717 | const curr = relocs[index]; | 4559 | target: *const std.Target, |
| 4718 | if (curr.offset < address) { | 4560 | params_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype), |
| 4719 | min = index + 1; | 4561 | returns_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype), |
| 4562 | ) Allocator.Error!void { | ||
| 4563 | params_buffer.clearRetainingCapacity(); | ||
| 4564 | returns_buffer.clearRetainingCapacity(); | ||
| 4565 | |||
| 4566 | const gpa = comp.gpa; | ||
| 4567 | const zcu = comp.zcu.?; | ||
| 4568 | |||
| 4569 | if (CodeGen.firstParamSRet(cc, return_type, zcu, target)) { | ||
| 4570 | try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle | ||
| 4571 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { | ||
| 4572 | if (cc == .wasm_watc) { | ||
| 4573 | const res_classes = abi.classifyType(return_type, zcu); | ||
| 4574 | assert(res_classes[0] == .direct and res_classes[1] == .none); | ||
| 4575 | const scalar_type = abi.scalarType(return_type, zcu); | ||
| 4576 | try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); | ||
| 4720 | } else { | 4577 | } else { |
| 4721 | max = index; | 4578 | try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target)); |
| 4722 | } | 4579 | } |
| 4580 | } else if (return_type.isError(zcu)) { | ||
| 4581 | try returns_buffer.append(gpa, .i32); | ||
| 4723 | } | 4582 | } |
| 4724 | return min; | ||
| 4725 | } | ||
| 4726 | 4583 | ||
| 4727 | fn searchRelocEnd(relocs: []const Wasm.Relocation, address: u32) usize { | 4584 | // param types |
| 4728 | for (relocs, 0..relocs.len) |reloc, index| { | 4585 | for (params) |param_type_ip| { |
| 4729 | if (reloc.offset > address) { | 4586 | const param_type = Zcu.Type.fromInterned(param_type_ip); |
| 4730 | return index; | 4587 | if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; |
| 4588 | |||
| 4589 | switch (cc) { | ||
| 4590 | .wasm_watc => { | ||
| 4591 | const param_classes = abi.classifyType(param_type, zcu); | ||
| 4592 | if (param_classes[1] == .none) { | ||
| 4593 | if (param_classes[0] == .direct) { | ||
| 4594 | const scalar_type = abi.scalarType(param_type, zcu); | ||
| 4595 | try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); | ||
| 4596 | } else { | ||
| 4597 | try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)); | ||
| 4598 | } | ||
| 4599 | } else { | ||
| 4600 | // i128/f128 | ||
| 4601 | try params_buffer.append(gpa, .i64); | ||
| 4602 | try params_buffer.append(gpa, .i64); | ||
| 4603 | } | ||
| 4604 | }, | ||
| 4605 | else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), | ||
| 4731 | } | 4606 | } |
| 4732 | } | 4607 | } |
| 4733 | return relocs.len; | ||
| 4734 | } | 4608 | } |
| 4735 | 4609 | ||
| 4736 | pub fn internString(wasm: *Wasm, bytes: []const u8) error{OutOfMemory}!String { | 4610 | pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool { |
| 4737 | const gpa = wasm.base.comp.gpa; | 4611 | const s = optional_name.slice(wasm) orelse return false; |
| 4738 | const gop = try wasm.string_table.getOrPutContextAdapted( | 4612 | return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss."); |
| 4739 | gpa, | 4613 | } |
| 4740 | @as([]const u8, bytes), | ||
| 4741 | @as(String.TableIndexAdapter, .{ .bytes = wasm.string_bytes.items }), | ||
| 4742 | @as(String.TableContext, .{ .bytes = wasm.string_bytes.items }), | ||
| 4743 | ); | ||
| 4744 | if (gop.found_existing) return gop.key_ptr.*; | ||
| 4745 | |||
| 4746 | try wasm.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1); | ||
| 4747 | const new_off: String = @enumFromInt(wasm.string_bytes.items.len); | ||
| 4748 | 4614 | ||
| 4749 | wasm.string_bytes.appendSliceAssumeCapacity(bytes); | 4615 | /// After this function is called, there may be additional entries in |
| 4750 | wasm.string_bytes.appendAssumeCapacity(0); | 4616 | /// `Wasm.uavs_obj`, `Wasm.uavs_exe`, `Wasm.navs_obj`, and `Wasm.navs_exe` |
| 4617 | /// which have uninitialized code and relocations. This function is | ||
| 4618 | /// non-recursive, so callers must coordinate additional calls to populate | ||
| 4619 | /// those entries. | ||
| 4620 | fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !ZcuDataObj { | ||
| 4621 | const code_start: u32 = @intCast(wasm.string_bytes.items.len); | ||
| 4622 | const relocs_start: u32 = @intCast(wasm.out_relocs.len); | ||
| 4623 | const uav_fixups_start: u32 = @intCast(wasm.uav_fixups.items.len); | ||
| 4624 | const nav_fixups_start: u32 = @intCast(wasm.nav_fixups.items.len); | ||
| 4625 | const func_table_fixups_start: u32 = @intCast(wasm.func_table_fixups.items.len); | ||
| 4626 | wasm.string_bytes_lock.lock(); | ||
| 4627 | |||
| 4628 | try codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &wasm.string_bytes, .none); | ||
| 4629 | |||
| 4630 | const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start); | ||
| 4631 | const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start); | ||
| 4632 | const any_fixups = | ||
| 4633 | uav_fixups_start != wasm.uav_fixups.items.len or | ||
| 4634 | nav_fixups_start != wasm.nav_fixups.items.len or | ||
| 4635 | func_table_fixups_start != wasm.func_table_fixups.items.len; | ||
| 4636 | wasm.string_bytes_lock.unlock(); | ||
| 4637 | |||
| 4638 | const naive_code: DataPayload = .{ | ||
| 4639 | .off = @enumFromInt(code_start), | ||
| 4640 | .len = code_len, | ||
| 4641 | }; | ||
| 4751 | 4642 | ||
| 4752 | gop.key_ptr.* = new_off; | 4643 | // Only nonzero init values need to take up space in the output. |
| 4644 | // If any fixups are present, we still need the string bytes allocated since | ||
| 4645 | // that is the staging area for the fixups. | ||
| 4646 | const code: DataPayload = if (!any_fixups and std.mem.allEqual(u8, naive_code.slice(wasm), 0)) c: { | ||
| 4647 | wasm.string_bytes.shrinkRetainingCapacity(code_start); | ||
| 4648 | // Indicate empty by making off and len the same value, however, still | ||
| 4649 | // transmit the data size by using the size as that value. | ||
| 4650 | break :c .{ | ||
| 4651 | .off = .none, | ||
| 4652 | .len = naive_code.len, | ||
| 4653 | }; | ||
| 4654 | } else c: { | ||
| 4655 | wasm.any_passive_inits = wasm.any_passive_inits or wasm.base.comp.config.import_memory; | ||
| 4656 | break :c naive_code; | ||
| 4657 | }; | ||
| 4753 | 4658 | ||
| 4754 | return new_off; | 4659 | return .{ |
| 4660 | .code = code, | ||
| 4661 | .relocs = .{ | ||
| 4662 | .off = relocs_start, | ||
| 4663 | .len = relocs_len, | ||
| 4664 | }, | ||
| 4665 | }; | ||
| 4755 | } | 4666 | } |
| 4756 | 4667 | ||
| 4757 | pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String { | 4668 | fn pointerAlignment(wasm: *const Wasm) Alignment { |
| 4758 | return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{ | 4669 | const target = &wasm.base.comp.root_mod.resolved_target.result; |
| 4759 | .bytes = wasm.string_bytes.items, | 4670 | return switch (target.cpu.arch) { |
| 4760 | })); | 4671 | .wasm32 => .@"4", |
| 4672 | .wasm64 => .@"8", | ||
| 4673 | else => unreachable, | ||
| 4674 | }; | ||
| 4761 | } | 4675 | } |
| 4762 | 4676 | ||
| 4763 | pub fn stringSlice(wasm: *const Wasm, index: String) [:0]const u8 { | 4677 | fn pointerSize(wasm: *const Wasm) u32 { |
| 4764 | const slice = wasm.string_bytes.items[@intFromEnum(index)..]; | 4678 | const target = &wasm.base.comp.root_mod.resolved_target.result; |
| 4765 | return slice[0..mem.indexOfScalar(u8, slice, 0).? :0]; | 4679 | return switch (target.cpu.arch) { |
| 4680 | .wasm32 => 4, | ||
| 4681 | .wasm64 => 8, | ||
| 4682 | else => unreachable, | ||
| 4683 | }; | ||
| 4766 | } | 4684 | } |
| 4767 | 4685 | ||
| 4768 | pub fn optionalStringSlice(wasm: *const Wasm, index: OptionalString) ?[:0]const u8 { | 4686 | fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex { |
| 4769 | return stringSlice(wasm, index.unwrap() orelse return null); | 4687 | const gop = wasm.imports.getOrPutAssumeCapacity(nav_index); |
| 4688 | gop.value_ptr.* = {}; | ||
| 4689 | return @enumFromInt(gop.index); | ||
| 4770 | } | 4690 | } |
| 4771 | 4691 | ||
| 4772 | pub fn castToString(wasm: *const Wasm, index: u32) String { | 4692 | fn resolveFunctionSynthetic( |
| 4773 | assert(index == 0 or wasm.string_bytes.items[index - 1] == 0); | 4693 | wasm: *Wasm, |
| 4774 | return @enumFromInt(index); | 4694 | import: *FunctionImport, |
| 4695 | res: FunctionImport.Resolution, | ||
| 4696 | params: []const std.wasm.Valtype, | ||
| 4697 | returns: []const std.wasm.Valtype, | ||
| 4698 | ) link.File.FlushError!void { | ||
| 4699 | import.resolution = res; | ||
| 4700 | wasm.functions.putAssumeCapacity(res, {}); | ||
| 4701 | // This is not only used for type-checking but also ensures the function | ||
| 4702 | // type index is interned so that it is guaranteed to exist during `flush`. | ||
| 4703 | const correct_func_type = try addFuncType(wasm, .{ | ||
| 4704 | .params = try internValtypeList(wasm, params), | ||
| 4705 | .returns = try internValtypeList(wasm, returns), | ||
| 4706 | }); | ||
| 4707 | if (import.type != correct_func_type) { | ||
| 4708 | const diags = &wasm.base.comp.link_diags; | ||
| 4709 | return import.source_location.fail(diags, "synthetic function {s} {} imported with incorrect signature {}", .{ | ||
| 4710 | @tagName(res), correct_func_type.fmt(wasm), import.type.fmt(wasm), | ||
| 4711 | }); | ||
| 4712 | } | ||
| 4775 | } | 4713 | } |
| 4776 | 4714 | ||
| 4777 | fn segmentPtr(wasm: *const Wasm, index: Segment.Index) *Segment { | 4715 | pub fn addFunction( |
| 4778 | return &wasm.segments.items[@intFromEnum(index)]; | 4716 | wasm: *Wasm, |
| 4717 | resolution: FunctionImport.Resolution, | ||
| 4718 | params: []const std.wasm.Valtype, | ||
| 4719 | returns: []const std.wasm.Valtype, | ||
| 4720 | ) Allocator.Error!void { | ||
| 4721 | wasm.functions.putAssumeCapacity(resolution, {}); | ||
| 4722 | _ = try wasm.addFuncType(.{ | ||
| 4723 | .params = try wasm.internValtypeList(params), | ||
| 4724 | .returns = try wasm.internValtypeList(returns), | ||
| 4725 | }); | ||
| 4779 | } | 4726 | } |
src/link/Wasm/Archive.zig+14-3| ... | @@ -142,8 +142,18 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive { | ... | @@ -142,8 +142,18 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive { |
| 142 | 142 | ||
| 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. |
| 145 | pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, path: Path) !Object { | 145 | pub fn parseObject( |
| 146 | const header = mem.bytesAsValue(Header, file_contents[0..@sizeOf(Header)]); | 146 | archive: Archive, |
| 147 | wasm: *Wasm, | ||
| 148 | file_contents: []const u8, | ||
| 149 | object_offset: u32, | ||
| 150 | path: Path, | ||
| 151 | host_name: Wasm.OptionalString, | ||
| 152 | scratch_space: *Object.ScratchSpace, | ||
| 153 | must_link: bool, | ||
| 154 | gc_sections: bool, | ||
| 155 | ) !Object { | ||
| 156 | const header = mem.bytesAsValue(Header, file_contents[object_offset..][0..@sizeOf(Header)]); | ||
| 147 | if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter; | 157 | if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter; |
| 148 | 158 | ||
| 149 | const name_or_index = try header.nameOrIndex(); | 159 | const name_or_index = try header.nameOrIndex(); |
| ... | @@ -157,8 +167,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat | ... | @@ -157,8 +167,9 @@ pub fn parseObject(archive: Archive, wasm: *Wasm, file_contents: []const u8, pat |
| 157 | }; | 167 | }; |
| 158 | 168 | ||
| 159 | const object_file_size = try header.parsedSize(); | 169 | const object_file_size = try header.parsedSize(); |
| 170 | const contents = file_contents[object_offset + @sizeOf(Header) ..][0..object_file_size]; | ||
| 160 | 171 | ||
| 161 | return Object.create(wasm, file_contents[@sizeOf(Header)..][0..object_file_size], path, object_name); | 172 | return Object.parse(wasm, contents, path, object_name, host_name, scratch_space, must_link, gc_sections); |
| 162 | } | 173 | } |
| 163 | 174 | ||
| 164 | const Archive = @This(); | 175 | const Archive = @This(); |
src/link/Wasm/Flush.zig created+1975| ... | @@ -0,0 +1,1975 @@ | ||
| 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 | |||
| 4 | const Flush = @This(); | ||
| 5 | const Wasm = @import("../Wasm.zig"); | ||
| 6 | const Object = @import("Object.zig"); | ||
| 7 | const Zcu = @import("../../Zcu.zig"); | ||
| 8 | const Alignment = Wasm.Alignment; | ||
| 9 | const String = Wasm.String; | ||
| 10 | const Relocation = Wasm.Relocation; | ||
| 11 | const InternPool = @import("../../InternPool.zig"); | ||
| 12 | |||
| 13 | const build_options = @import("build_options"); | ||
| 14 | |||
| 15 | const std = @import("std"); | ||
| 16 | const Allocator = std.mem.Allocator; | ||
| 17 | const mem = std.mem; | ||
| 18 | const leb = std.leb; | ||
| 19 | const log = std.log.scoped(.link); | ||
| 20 | const 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 virtual address. | ||
| 25 | data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegmentId, 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. | ||
| 28 | /// Value is the virtual memory address of the end of the segment. | ||
| 29 | data_segment_groups: std.ArrayListUnmanaged(DataSegmentGroup) = .empty, | ||
| 30 | |||
| 31 | binary_bytes: std.ArrayListUnmanaged(u8) = .empty, | ||
| 32 | missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty, | ||
| 33 | function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty, | ||
| 34 | global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty, | ||
| 35 | data_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.DataImportId) = .empty, | ||
| 36 | |||
| 37 | indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex, void) = .empty, | ||
| 38 | |||
| 39 | /// A subset of the full interned function type list created only during flush. | ||
| 40 | func_types: std.AutoArrayHashMapUnmanaged(Wasm.FunctionType.Index, void) = .empty, | ||
| 41 | |||
| 42 | /// For debug purposes only. | ||
| 43 | memory_layout_finished: bool = false, | ||
| 44 | |||
| 45 | /// Index into `func_types`. | ||
| 46 | pub const FuncTypeIndex = enum(u32) { | ||
| 47 | _, | ||
| 48 | |||
| 49 | pub fn fromTypeIndex(i: Wasm.FunctionType.Index, f: *const Flush) FuncTypeIndex { | ||
| 50 | return @enumFromInt(f.func_types.getIndex(i).?); | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | /// Index into `indirect_function_table`. | ||
| 55 | const IndirectFunctionTableIndex = enum(u32) { | ||
| 56 | _, | ||
| 57 | |||
| 58 | fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: Wasm.ObjectFunctionIndex) IndirectFunctionTableIndex { | ||
| 59 | return fromOutputFunctionIndex(&wasm.flush_buffer, .fromObjectFunctionHandlingWeak(wasm, index)); | ||
| 60 | } | ||
| 61 | |||
| 62 | fn fromSymbolName(wasm: *const Wasm, name: String) IndirectFunctionTableIndex { | ||
| 63 | return fromOutputFunctionIndex(&wasm.flush_buffer, .fromSymbolName(wasm, name)); | ||
| 64 | } | ||
| 65 | |||
| 66 | fn fromOutputFunctionIndex(f: *const Flush, i: Wasm.OutputFunctionIndex) IndirectFunctionTableIndex { | ||
| 67 | return @enumFromInt(f.indirect_function_table.getIndex(i).?); | ||
| 68 | } | ||
| 69 | |||
| 70 | fn fromZcuIndirectFunctionSetIndex(i: Wasm.ZcuIndirectFunctionSetIndex) IndirectFunctionTableIndex { | ||
| 71 | // These are the same since those are added to the table first. | ||
| 72 | return @enumFromInt(@intFromEnum(i)); | ||
| 73 | } | ||
| 74 | |||
| 75 | fn toAbi(i: IndirectFunctionTableIndex) u32 { | ||
| 76 | return @intFromEnum(i) + 1; | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | |||
| 80 | const DataSegmentGroup = struct { | ||
| 81 | first_segment: Wasm.DataSegmentId, | ||
| 82 | end_addr: u32, | ||
| 83 | }; | ||
| 84 | |||
| 85 | pub fn clear(f: *Flush) void { | ||
| 86 | f.data_segments.clearRetainingCapacity(); | ||
| 87 | f.data_segment_groups.clearRetainingCapacity(); | ||
| 88 | f.binary_bytes.clearRetainingCapacity(); | ||
| 89 | f.indirect_function_table.clearRetainingCapacity(); | ||
| 90 | f.func_types.clearRetainingCapacity(); | ||
| 91 | f.memory_layout_finished = false; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn deinit(f: *Flush, gpa: Allocator) void { | ||
| 95 | f.data_segments.deinit(gpa); | ||
| 96 | f.data_segment_groups.deinit(gpa); | ||
| 97 | f.binary_bytes.deinit(gpa); | ||
| 98 | f.missing_exports.deinit(gpa); | ||
| 99 | f.function_imports.deinit(gpa); | ||
| 100 | f.global_imports.deinit(gpa); | ||
| 101 | f.data_imports.deinit(gpa); | ||
| 102 | f.indirect_function_table.deinit(gpa); | ||
| 103 | f.func_types.deinit(gpa); | ||
| 104 | f.* = undefined; | ||
| 105 | } | ||
| 106 | |||
| 107 | pub fn finish(f: *Flush, wasm: *Wasm) !void { | ||
| 108 | const comp = wasm.base.comp; | ||
| 109 | const shared_memory = comp.config.shared_memory; | ||
| 110 | const diags = &comp.link_diags; | ||
| 111 | const gpa = comp.gpa; | ||
| 112 | const import_memory = comp.config.import_memory; | ||
| 113 | const export_memory = comp.config.export_memory; | ||
| 114 | const target = &comp.root_mod.resolved_target.result; | ||
| 115 | const is64 = switch (target.cpu.arch) { | ||
| 116 | .wasm32 => false, | ||
| 117 | .wasm64 => true, | ||
| 118 | else => unreachable, | ||
| 119 | }; | ||
| 120 | const is_obj = comp.config.output_mode == .Obj; | ||
| 121 | const allow_undefined = is_obj or wasm.import_symbols; | ||
| 122 | |||
| 123 | const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none; | ||
| 124 | |||
| 125 | if (comp.zcu) |zcu| { | ||
| 126 | const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed! | ||
| 127 | |||
| 128 | // Detect any intrinsics that were called; they need to have dependencies on the symbols marked. | ||
| 129 | // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized. | ||
| 130 | for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) { | ||
| 131 | .call_intrinsic => { | ||
| 132 | const symbol_name = try wasm.internString(@tagName(data.intrinsic)); | ||
| 133 | const i: Wasm.FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(symbol_name) orelse { | ||
| 134 | return diags.fail("missing compiler runtime intrinsic '{s}' (undefined linker symbol)", .{ | ||
| 135 | @tagName(data.intrinsic), | ||
| 136 | }); | ||
| 137 | }); | ||
| 138 | try wasm.markFunctionImport(symbol_name, i.value(wasm), i); | ||
| 139 | }, | ||
| 140 | .call_tag_name => { | ||
| 141 | assert(ip.indexToKey(data.ip_index) == .enum_type); | ||
| 142 | const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index); | ||
| 143 | if (!gop.found_existing) { | ||
| 144 | wasm.tag_name_table_ref_count += 1; | ||
| 145 | const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu); | ||
| 146 | gop.value_ptr.* = .{ .tag_name = .{ | ||
| 147 | .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}), | ||
| 148 | .type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target), | ||
| 149 | .table_index = @intCast(wasm.tag_name_offs.items.len), | ||
| 150 | } }; | ||
| 151 | try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {}); | ||
| 152 | const tag_names = ip.loadEnumType(data.ip_index).names; | ||
| 153 | for (tag_names.get(ip)) |tag_name| { | ||
| 154 | const slice = tag_name.toSlice(ip); | ||
| 155 | try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len)); | ||
| 156 | try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | }, | ||
| 160 | else => continue, | ||
| 161 | }; | ||
| 162 | |||
| 163 | { | ||
| 164 | var i = wasm.function_imports_len_prelink; | ||
| 165 | while (i < f.function_imports.entries.len) { | ||
| 166 | const symbol_name = f.function_imports.keys()[i]; | ||
| 167 | if (wasm.object_function_imports.getIndex(symbol_name)) |import_index_usize| { | ||
| 168 | const import_index: Wasm.FunctionImport.Index = @enumFromInt(import_index_usize); | ||
| 169 | try wasm.markFunctionImport(symbol_name, import_index.value(wasm), import_index); | ||
| 170 | f.function_imports.swapRemoveAt(i); | ||
| 171 | continue; | ||
| 172 | } | ||
| 173 | i += 1; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | { | ||
| 178 | var i = wasm.data_imports_len_prelink; | ||
| 179 | while (i < f.data_imports.entries.len) { | ||
| 180 | const symbol_name = f.data_imports.keys()[i]; | ||
| 181 | if (wasm.object_data_imports.getIndex(symbol_name)) |import_index_usize| { | ||
| 182 | const import_index: Wasm.ObjectDataImport.Index = @enumFromInt(import_index_usize); | ||
| 183 | try wasm.markDataImport(symbol_name, import_index.value(wasm), import_index); | ||
| 184 | f.data_imports.swapRemoveAt(i); | ||
| 185 | continue; | ||
| 186 | } | ||
| 187 | i += 1; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | if (wasm.error_name_table_ref_count > 0) { | ||
| 192 | // Ensure Zcu error name structures are populated. | ||
| 193 | const full_error_names = ip.global_error_set.getNamesFromMainThread(); | ||
| 194 | try wasm.error_name_offs.ensureTotalCapacity(gpa, full_error_names.len + 1); | ||
| 195 | if (wasm.error_name_offs.items.len == 0) { | ||
| 196 | // Dummy entry at index 0 to avoid a sub instruction at `@errorName` sites. | ||
| 197 | wasm.error_name_offs.appendAssumeCapacity(0); | ||
| 198 | } | ||
| 199 | const new_error_names = full_error_names[wasm.error_name_offs.items.len - 1 ..]; | ||
| 200 | for (new_error_names) |error_name| { | ||
| 201 | wasm.error_name_offs.appendAssumeCapacity(@intCast(wasm.error_name_bytes.items.len)); | ||
| 202 | const s: [:0]const u8 = error_name.toSlice(ip); | ||
| 203 | try wasm.error_name_bytes.appendSlice(gpa, s[0 .. s.len + 1]); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | for (wasm.nav_exports.keys(), wasm.nav_exports.values()) |*nav_export, export_index| { | ||
| 208 | if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) { | ||
| 209 | log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index }); | ||
| 210 | const function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?; | ||
| 211 | const explicit = f.missing_exports.swapRemove(nav_export.name); | ||
| 212 | const is_hidden = !explicit and switch (export_index.ptr(zcu).opts.visibility) { | ||
| 213 | .hidden => true, | ||
| 214 | .default, .protected => false, | ||
| 215 | }; | ||
| 216 | if (is_hidden) { | ||
| 217 | try wasm.hidden_function_exports.put(gpa, nav_export.name, function_index); | ||
| 218 | } else { | ||
| 219 | try wasm.function_exports.put(gpa, nav_export.name, function_index); | ||
| 220 | } | ||
| 221 | _ = f.function_imports.swapRemove(nav_export.name); | ||
| 222 | |||
| 223 | if (nav_export.name.toOptional() == entry_name) | ||
| 224 | wasm.entry_resolution = .fromIpNav(wasm, nav_export.nav_index); | ||
| 225 | } else { | ||
| 226 | // This is a data export because Zcu currently has no way to | ||
| 227 | // export wasm globals. | ||
| 228 | _ = f.missing_exports.swapRemove(nav_export.name); | ||
| 229 | _ = f.data_imports.swapRemove(nav_export.name); | ||
| 230 | if (!is_obj) { | ||
| 231 | diags.addError("unable to export data symbol '{s}'; not emitting a relocatable", .{ | ||
| 232 | nav_export.name.slice(wasm), | ||
| 233 | }); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | for (f.missing_exports.keys()) |exp_name| { | ||
| 239 | diags.addError("manually specified export name '{s}' undefined", .{exp_name.slice(wasm)}); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | if (entry_name.unwrap()) |name| { | ||
| 244 | if (wasm.entry_resolution == .unresolved) { | ||
| 245 | var err = try diags.addErrorWithNotes(1); | ||
| 246 | try err.addMsg("entry symbol '{s}' missing", .{name.slice(wasm)}); | ||
| 247 | err.addNote("'-fno-entry' suppresses this error", .{}); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | if (!allow_undefined) { | ||
| 252 | for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| { | ||
| 253 | if (function_import_id.undefinedAllowed(wasm)) continue; | ||
| 254 | const src_loc = function_import_id.sourceLocation(wasm); | ||
| 255 | src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)}); | ||
| 256 | } | ||
| 257 | for (f.global_imports.keys(), f.global_imports.values()) |name, global_import_id| { | ||
| 258 | const src_loc = global_import_id.sourceLocation(wasm); | ||
| 259 | src_loc.addError(wasm, "undefined global: {s}", .{name.slice(wasm)}); | ||
| 260 | } | ||
| 261 | for (wasm.table_imports.keys(), wasm.table_imports.values()) |name, table_import_id| { | ||
| 262 | const src_loc = table_import_id.value(wasm).source_location; | ||
| 263 | src_loc.addError(wasm, "undefined table: {s}", .{name.slice(wasm)}); | ||
| 264 | } | ||
| 265 | for (f.data_imports.keys(), f.data_imports.values()) |name, data_import_id| { | ||
| 266 | const src_loc = data_import_id.sourceLocation(wasm); | ||
| 267 | src_loc.addError(wasm, "undefined data: {s}", .{name.slice(wasm)}); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | if (diags.hasErrors()) return error.LinkFailure; | ||
| 272 | |||
| 273 | // Merge indirect function tables. | ||
| 274 | try f.indirect_function_table.ensureUnusedCapacity(gpa, wasm.zcu_indirect_function_set.entries.len + | ||
| 275 | wasm.object_indirect_function_import_set.entries.len + wasm.object_indirect_function_set.entries.len); | ||
| 276 | // This one goes first so the indexes can be stable for MIR lowering. | ||
| 277 | for (wasm.zcu_indirect_function_set.keys()) |nav_index| | ||
| 278 | f.indirect_function_table.putAssumeCapacity(.fromIpNav(wasm, nav_index), {}); | ||
| 279 | for (wasm.object_indirect_function_import_set.keys()) |symbol_name| | ||
| 280 | f.indirect_function_table.putAssumeCapacity(.fromSymbolName(wasm, symbol_name), {}); | ||
| 281 | for (wasm.object_indirect_function_set.keys()) |object_function_index| | ||
| 282 | f.indirect_function_table.putAssumeCapacity(.fromObjectFunction(wasm, object_function_index), {}); | ||
| 283 | |||
| 284 | if (wasm.object_init_funcs.items.len > 0) { | ||
| 285 | // Zig has no constructors so these are only for object file inputs. | ||
| 286 | mem.sortUnstable(Wasm.InitFunc, wasm.object_init_funcs.items, {}, Wasm.InitFunc.lessThan); | ||
| 287 | try wasm.functions.put(gpa, .__wasm_call_ctors, {}); | ||
| 288 | } | ||
| 289 | |||
| 290 | // Merge and order the data segments. Depends on garbage collection so that | ||
| 291 | // unused segments can be omitted. | ||
| 292 | try f.data_segments.ensureUnusedCapacity(gpa, wasm.data_segments.entries.len + | ||
| 293 | wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len + | ||
| 294 | wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 4); | ||
| 295 | if (is_obj) assert(wasm.uavs_exe.entries.len == 0); | ||
| 296 | if (is_obj) assert(wasm.navs_exe.entries.len == 0); | ||
| 297 | if (!is_obj) assert(wasm.uavs_obj.entries.len == 0); | ||
| 298 | if (!is_obj) assert(wasm.navs_obj.entries.len == 0); | ||
| 299 | for (0..wasm.uavs_obj.entries.len) |uavs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{ | ||
| 300 | .uav_obj = @enumFromInt(uavs_index), | ||
| 301 | }), @as(u32, undefined)); | ||
| 302 | for (0..wasm.navs_obj.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{ | ||
| 303 | .nav_obj = @enumFromInt(navs_index), | ||
| 304 | }), @as(u32, undefined)); | ||
| 305 | for (0..wasm.uavs_exe.entries.len) |uavs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{ | ||
| 306 | .uav_exe = @enumFromInt(uavs_index), | ||
| 307 | }), @as(u32, undefined)); | ||
| 308 | for (0..wasm.navs_exe.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{ | ||
| 309 | .nav_exe = @enumFromInt(navs_index), | ||
| 310 | }), @as(u32, undefined)); | ||
| 311 | if (wasm.error_name_table_ref_count > 0) { | ||
| 312 | f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined)); | ||
| 313 | f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined)); | ||
| 314 | } | ||
| 315 | if (wasm.tag_name_table_ref_count > 0) { | ||
| 316 | f.data_segments.putAssumeCapacity(.__zig_tag_names, @as(u32, undefined)); | ||
| 317 | f.data_segments.putAssumeCapacity(.__zig_tag_name_table, @as(u32, undefined)); | ||
| 318 | } | ||
| 319 | for (wasm.data_segments.keys()) |data_id| f.data_segments.putAssumeCapacity(data_id, @as(u32, undefined)); | ||
| 320 | |||
| 321 | try wasm.functions.ensureUnusedCapacity(gpa, 3); | ||
| 322 | |||
| 323 | // Passive segments are used to avoid memory being reinitialized on each | ||
| 324 | // thread's instantiation. These passive segments are initialized and | ||
| 325 | // dropped in __wasm_init_memory, which is registered as the start function | ||
| 326 | // We also initialize bss segments (using memory.fill) as part of this | ||
| 327 | // function. | ||
| 328 | if (wasm.any_passive_inits) { | ||
| 329 | try wasm.addFunction(.__wasm_init_memory, &.{}, &.{}); | ||
| 330 | } | ||
| 331 | |||
| 332 | try wasm.tables.ensureUnusedCapacity(gpa, 1); | ||
| 333 | |||
| 334 | if (f.indirect_function_table.entries.len > 0) { | ||
| 335 | wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); | ||
| 336 | } | ||
| 337 | |||
| 338 | // Sort order: | ||
| 339 | // 0. Segment category (tls, data, zero) | ||
| 340 | // 1. Segment name prefix | ||
| 341 | // 2. Segment alignment | ||
| 342 | // 3. Reference count, descending (optimize for LEB encoding) | ||
| 343 | // 4. Segment name suffix | ||
| 344 | // 5. Segment ID interpreted as an integer (for determinism) | ||
| 345 | // | ||
| 346 | // TLS segments are intended to be merged with each other, and segments | ||
| 347 | // with a common prefix name are intended to be merged with each other. | ||
| 348 | // Sorting ensures the segments intended to be merged will be adjacent. | ||
| 349 | // | ||
| 350 | // Each Zcu Nav and Cau has an independent data segment ID in this logic. | ||
| 351 | // For the purposes of sorting, they are implicitly all named ".data". | ||
| 352 | const Sort = struct { | ||
| 353 | wasm: *const Wasm, | ||
| 354 | segments: []const Wasm.DataSegmentId, | ||
| 355 | pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool { | ||
| 356 | const lhs_segment = ctx.segments[lhs]; | ||
| 357 | const rhs_segment = ctx.segments[rhs]; | ||
| 358 | const lhs_category = @intFromEnum(lhs_segment.category(ctx.wasm)); | ||
| 359 | const rhs_category = @intFromEnum(rhs_segment.category(ctx.wasm)); | ||
| 360 | switch (std.math.order(lhs_category, rhs_category)) { | ||
| 361 | .lt => return true, | ||
| 362 | .gt => return false, | ||
| 363 | .eq => {}, | ||
| 364 | } | ||
| 365 | const lhs_segment_name = lhs_segment.name(ctx.wasm); | ||
| 366 | const rhs_segment_name = rhs_segment.name(ctx.wasm); | ||
| 367 | const lhs_prefix, const lhs_suffix = splitSegmentName(lhs_segment_name); | ||
| 368 | const rhs_prefix, const rhs_suffix = splitSegmentName(rhs_segment_name); | ||
| 369 | switch (mem.order(u8, lhs_prefix, rhs_prefix)) { | ||
| 370 | .lt => return true, | ||
| 371 | .gt => return false, | ||
| 372 | .eq => {}, | ||
| 373 | } | ||
| 374 | const lhs_alignment = lhs_segment.alignment(ctx.wasm); | ||
| 375 | const rhs_alignment = rhs_segment.alignment(ctx.wasm); | ||
| 376 | switch (lhs_alignment.order(rhs_alignment)) { | ||
| 377 | .lt => return false, | ||
| 378 | .gt => return true, | ||
| 379 | .eq => {}, | ||
| 380 | } | ||
| 381 | switch (std.math.order(lhs_segment.refCount(ctx.wasm), rhs_segment.refCount(ctx.wasm))) { | ||
| 382 | .lt => return false, | ||
| 383 | .gt => return true, | ||
| 384 | .eq => {}, | ||
| 385 | } | ||
| 386 | switch (mem.order(u8, lhs_suffix, rhs_suffix)) { | ||
| 387 | .lt => return true, | ||
| 388 | .gt => return false, | ||
| 389 | .eq => {}, | ||
| 390 | } | ||
| 391 | return @intFromEnum(lhs_segment) < @intFromEnum(rhs_segment); | ||
| 392 | } | ||
| 393 | }; | ||
| 394 | f.data_segments.sortUnstable(@as(Sort, .{ | ||
| 395 | .wasm = wasm, | ||
| 396 | .segments = f.data_segments.keys(), | ||
| 397 | })); | ||
| 398 | |||
| 399 | const page_size = std.wasm.page_size; // 64kb | ||
| 400 | const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention | ||
| 401 | const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention | ||
| 402 | const pointer_alignment: Alignment = .@"4"; | ||
| 403 | // Always place the stack at the start by default unless the user specified the global-base flag. | ||
| 404 | const place_stack_first, var memory_ptr: u64 = if (wasm.global_base) |base| .{ false, base } else .{ true, 0 }; | ||
| 405 | |||
| 406 | var virtual_addrs: VirtualAddrs = .{ | ||
| 407 | .stack_pointer = undefined, | ||
| 408 | .heap_base = undefined, | ||
| 409 | .heap_end = undefined, | ||
| 410 | .tls_base = null, | ||
| 411 | .tls_align = .none, | ||
| 412 | .tls_size = null, | ||
| 413 | .init_memory_flag = null, | ||
| 414 | }; | ||
| 415 | |||
| 416 | if (place_stack_first and !is_obj) { | ||
| 417 | memory_ptr = stack_alignment.forward(memory_ptr); | ||
| 418 | memory_ptr += wasm.base.stack_size; | ||
| 419 | virtual_addrs.stack_pointer = @intCast(memory_ptr); | ||
| 420 | } | ||
| 421 | |||
| 422 | const segment_ids = f.data_segments.keys(); | ||
| 423 | const segment_vaddrs = f.data_segments.values(); | ||
| 424 | assert(f.data_segment_groups.items.len == 0); | ||
| 425 | const data_vaddr: u32 = @intCast(memory_ptr); | ||
| 426 | if (segment_ids.len > 0) { | ||
| 427 | var seen_tls: enum { before, during, after } = .before; | ||
| 428 | var category: Wasm.DataSegmentId.Category = undefined; | ||
| 429 | var first_segment: Wasm.DataSegmentId = segment_ids[0]; | ||
| 430 | for (segment_ids, segment_vaddrs, 0..) |segment_id, *segment_vaddr, i| { | ||
| 431 | const alignment = segment_id.alignment(wasm); | ||
| 432 | category = segment_id.category(wasm); | ||
| 433 | const start_addr = alignment.forward(memory_ptr); | ||
| 434 | |||
| 435 | const want_new_segment = b: { | ||
| 436 | if (is_obj) break :b false; | ||
| 437 | switch (seen_tls) { | ||
| 438 | .before => switch (category) { | ||
| 439 | .tls => { | ||
| 440 | virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(start_addr); | ||
| 441 | virtual_addrs.tls_align = alignment; | ||
| 442 | seen_tls = .during; | ||
| 443 | break :b f.data_segment_groups.items.len > 0; | ||
| 444 | }, | ||
| 445 | else => {}, | ||
| 446 | }, | ||
| 447 | .during => switch (category) { | ||
| 448 | .tls => { | ||
| 449 | virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment); | ||
| 450 | virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?); | ||
| 451 | break :b false; | ||
| 452 | }, | ||
| 453 | else => { | ||
| 454 | seen_tls = .after; | ||
| 455 | break :b true; | ||
| 456 | }, | ||
| 457 | }, | ||
| 458 | .after => {}, | ||
| 459 | } | ||
| 460 | break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category); | ||
| 461 | }; | ||
| 462 | if (want_new_segment) { | ||
| 463 | log.debug("new segment group at 0x{x} {} {s} {}", .{ start_addr, segment_id, segment_id.name(wasm), category }); | ||
| 464 | try f.data_segment_groups.append(gpa, .{ | ||
| 465 | .end_addr = @intCast(memory_ptr), | ||
| 466 | .first_segment = first_segment, | ||
| 467 | }); | ||
| 468 | first_segment = segment_id; | ||
| 469 | } | ||
| 470 | |||
| 471 | const size = segment_id.size(wasm); | ||
| 472 | segment_vaddr.* = @intCast(start_addr); | ||
| 473 | log.debug("0x{x} {d} {s}", .{ start_addr, @intFromEnum(segment_id), segment_id.name(wasm) }); | ||
| 474 | memory_ptr = start_addr + size; | ||
| 475 | } | ||
| 476 | if (category != .zero) try f.data_segment_groups.append(gpa, .{ | ||
| 477 | .first_segment = first_segment, | ||
| 478 | .end_addr = @intCast(memory_ptr), | ||
| 479 | }); | ||
| 480 | if (category == .tls and seen_tls == .during) { | ||
| 481 | virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | if (shared_memory and wasm.any_passive_inits) { | ||
| 486 | memory_ptr = pointer_alignment.forward(memory_ptr); | ||
| 487 | virtual_addrs.init_memory_flag = @intCast(memory_ptr); | ||
| 488 | memory_ptr += 4; | ||
| 489 | } | ||
| 490 | |||
| 491 | if (!place_stack_first and !is_obj) { | ||
| 492 | memory_ptr = stack_alignment.forward(memory_ptr); | ||
| 493 | memory_ptr += wasm.base.stack_size; | ||
| 494 | virtual_addrs.stack_pointer = @intCast(memory_ptr); | ||
| 495 | } | ||
| 496 | |||
| 497 | memory_ptr = heap_alignment.forward(memory_ptr); | ||
| 498 | virtual_addrs.heap_base = @intCast(memory_ptr); | ||
| 499 | |||
| 500 | if (wasm.initial_memory) |initial_memory| { | ||
| 501 | if (!mem.isAlignedGeneric(u64, initial_memory, page_size)) { | ||
| 502 | diags.addError("initial memory value {d} is not {d}-byte aligned", .{ initial_memory, page_size }); | ||
| 503 | } | ||
| 504 | if (memory_ptr > initial_memory) { | ||
| 505 | diags.addError("initial memory value {d} insufficient; minimum {d}", .{ initial_memory, memory_ptr }); | ||
| 506 | } | ||
| 507 | if (initial_memory > std.math.maxInt(u32)) { | ||
| 508 | diags.addError("initial memory value {d} exceeds 32-bit address space", .{initial_memory}); | ||
| 509 | } | ||
| 510 | if (diags.hasErrors()) return error.LinkFailure; | ||
| 511 | memory_ptr = initial_memory; | ||
| 512 | } else { | ||
| 513 | memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size); | ||
| 514 | } | ||
| 515 | virtual_addrs.heap_end = @intCast(memory_ptr); | ||
| 516 | |||
| 517 | // In case we do not import memory, but define it ourselves, set the | ||
| 518 | // minimum amount of pages on the memory section. | ||
| 519 | wasm.memories.limits.min = @intCast(memory_ptr / page_size); | ||
| 520 | log.debug("total memory pages: {d}", .{wasm.memories.limits.min}); | ||
| 521 | |||
| 522 | if (wasm.max_memory) |max_memory| { | ||
| 523 | if (!mem.isAlignedGeneric(u64, max_memory, page_size)) { | ||
| 524 | diags.addError("maximum memory value {d} is not {d}-byte aligned", .{ max_memory, page_size }); | ||
| 525 | } | ||
| 526 | if (memory_ptr > max_memory) { | ||
| 527 | diags.addError("maximum memory value {d} insufficient; minimum {d}", .{ max_memory, memory_ptr }); | ||
| 528 | } | ||
| 529 | if (max_memory > std.math.maxInt(u32)) { | ||
| 530 | diags.addError("maximum memory value {d} exceeds 32-bit address space", .{max_memory}); | ||
| 531 | } | ||
| 532 | if (diags.hasErrors()) return error.LinkFailure; | ||
| 533 | wasm.memories.limits.max = @intCast(max_memory / page_size); | ||
| 534 | wasm.memories.limits.flags.has_max = true; | ||
| 535 | if (shared_memory) wasm.memories.limits.flags.is_shared = true; | ||
| 536 | log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max}); | ||
| 537 | } | ||
| 538 | f.memory_layout_finished = true; | ||
| 539 | |||
| 540 | // When we have TLS GOT entries and shared memory is enabled, we must | ||
| 541 | // perform runtime relocations or else we don't create the function. | ||
| 542 | if (shared_memory and virtual_addrs.tls_base != null) { | ||
| 543 | // This logic that checks `any_tls_relocs` is missing the part where it | ||
| 544 | // also notices threadlocal globals from Zcu code. | ||
| 545 | if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{}); | ||
| 546 | try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{}); | ||
| 547 | try wasm.globals.ensureUnusedCapacity(gpa, 3); | ||
| 548 | wasm.globals.putAssumeCapacity(.__tls_base, {}); | ||
| 549 | wasm.globals.putAssumeCapacity(.__tls_size, {}); | ||
| 550 | wasm.globals.putAssumeCapacity(.__tls_align, {}); | ||
| 551 | } | ||
| 552 | |||
| 553 | var section_index: u32 = 0; | ||
| 554 | // Index of the code section. Used to tell relocation table where the section lives. | ||
| 555 | var code_section_index: ?u32 = null; | ||
| 556 | // Index of the data section. Used to tell relocation table where the section lives. | ||
| 557 | var data_section_index: ?u32 = null; | ||
| 558 | |||
| 559 | const binary_bytes = &f.binary_bytes; | ||
| 560 | assert(binary_bytes.items.len == 0); | ||
| 561 | |||
| 562 | try binary_bytes.appendSlice(gpa, &std.wasm.magic ++ &std.wasm.version); | ||
| 563 | assert(binary_bytes.items.len == 8); | ||
| 564 | |||
| 565 | const binary_writer = binary_bytes.writer(gpa); | ||
| 566 | |||
| 567 | // Type section. | ||
| 568 | for (f.function_imports.values()) |id| { | ||
| 569 | try f.func_types.put(gpa, id.functionType(wasm), {}); | ||
| 570 | } | ||
| 571 | for (wasm.functions.keys()) |function| { | ||
| 572 | try f.func_types.put(gpa, function.typeIndex(wasm), {}); | ||
| 573 | } | ||
| 574 | if (f.func_types.entries.len != 0) { | ||
| 575 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 576 | for (f.func_types.keys()) |func_type_index| { | ||
| 577 | const func_type = func_type_index.ptr(wasm); | ||
| 578 | try leb.writeUleb128(binary_writer, std.wasm.function_type); | ||
| 579 | const params = func_type.params.slice(wasm); | ||
| 580 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(params.len))); | ||
| 581 | for (params) |param_ty| { | ||
| 582 | try leb.writeUleb128(binary_writer, @intFromEnum(param_ty)); | ||
| 583 | } | ||
| 584 | const returns = func_type.returns.slice(wasm); | ||
| 585 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(returns.len))); | ||
| 586 | for (returns) |ret_ty| { | ||
| 587 | try leb.writeUleb128(binary_writer, @intFromEnum(ret_ty)); | ||
| 588 | } | ||
| 589 | } | ||
| 590 | replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(f.func_types.entries.len)); | ||
| 591 | section_index += 1; | ||
| 592 | } | ||
| 593 | |||
| 594 | if (!is_obj) { | ||
| 595 | // TODO: sort function_imports by ref count descending for optimal LEB encodings | ||
| 596 | // TODO: sort global_imports by ref count descending for optimal LEB encodings | ||
| 597 | // TODO: sort output functions by ref count descending for optimal LEB encodings | ||
| 598 | } | ||
| 599 | |||
| 600 | // Import section | ||
| 601 | { | ||
| 602 | var total_imports: usize = 0; | ||
| 603 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 604 | |||
| 605 | for (f.function_imports.values()) |id| { | ||
| 606 | const module_name = id.moduleName(wasm).slice(wasm).?; | ||
| 607 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); | ||
| 608 | try binary_writer.writeAll(module_name); | ||
| 609 | |||
| 610 | const name = id.importName(wasm).slice(wasm); | ||
| 611 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 612 | try binary_writer.writeAll(name); | ||
| 613 | |||
| 614 | try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function)); | ||
| 615 | const type_index: FuncTypeIndex = .fromTypeIndex(id.functionType(wasm), f); | ||
| 616 | try leb.writeUleb128(binary_writer, @intFromEnum(type_index)); | ||
| 617 | } | ||
| 618 | total_imports += f.function_imports.entries.len; | ||
| 619 | |||
| 620 | for (wasm.table_imports.values()) |id| { | ||
| 621 | const table_import = id.value(wasm); | ||
| 622 | const module_name = table_import.module_name.slice(wasm); | ||
| 623 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); | ||
| 624 | try binary_writer.writeAll(module_name); | ||
| 625 | |||
| 626 | const name = table_import.name.slice(wasm); | ||
| 627 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 628 | try binary_writer.writeAll(name); | ||
| 629 | |||
| 630 | try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table)); | ||
| 631 | try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to()))); | ||
| 632 | try emitLimits(gpa, binary_bytes, table_import.limits()); | ||
| 633 | } | ||
| 634 | total_imports += wasm.table_imports.entries.len; | ||
| 635 | |||
| 636 | if (import_memory) { | ||
| 637 | const name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory; | ||
| 638 | try emitMemoryImport(wasm, binary_bytes, name, &.{ | ||
| 639 | // TODO the import_memory option needs to specify from which module | ||
| 640 | .module_name = wasm.object_host_name.unwrap().?, | ||
| 641 | .limits_min = wasm.memories.limits.min, | ||
| 642 | .limits_max = wasm.memories.limits.max, | ||
| 643 | .limits_has_max = wasm.memories.limits.flags.has_max, | ||
| 644 | .limits_is_shared = wasm.memories.limits.flags.is_shared, | ||
| 645 | .source_location = .none, | ||
| 646 | }); | ||
| 647 | total_imports += 1; | ||
| 648 | } | ||
| 649 | |||
| 650 | for (f.global_imports.values()) |id| { | ||
| 651 | const module_name = id.moduleName(wasm).slice(wasm).?; | ||
| 652 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); | ||
| 653 | try binary_writer.writeAll(module_name); | ||
| 654 | |||
| 655 | const name = id.importName(wasm).slice(wasm); | ||
| 656 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 657 | try binary_writer.writeAll(name); | ||
| 658 | |||
| 659 | try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global)); | ||
| 660 | const global_type = id.globalType(wasm); | ||
| 661 | try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype))); | ||
| 662 | try binary_writer.writeByte(@intFromBool(global_type.mutable)); | ||
| 663 | } | ||
| 664 | total_imports += f.global_imports.entries.len; | ||
| 665 | |||
| 666 | if (total_imports > 0) { | ||
| 667 | replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports)); | ||
| 668 | section_index += 1; | ||
| 669 | } else { | ||
| 670 | binary_bytes.shrinkRetainingCapacity(header_offset); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | // Function section | ||
| 675 | if (wasm.functions.count() != 0) { | ||
| 676 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 677 | for (wasm.functions.keys()) |function| { | ||
| 678 | const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f); | ||
| 679 | try leb.writeUleb128(binary_writer, @intFromEnum(index)); | ||
| 680 | } | ||
| 681 | |||
| 682 | replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count())); | ||
| 683 | section_index += 1; | ||
| 684 | } | ||
| 685 | |||
| 686 | // Table section | ||
| 687 | if (wasm.tables.entries.len > 0) { | ||
| 688 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 689 | |||
| 690 | for (wasm.tables.keys()) |table| { | ||
| 691 | try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm)))); | ||
| 692 | try emitLimits(gpa, binary_bytes, table.limits(wasm)); | ||
| 693 | } | ||
| 694 | |||
| 695 | replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len)); | ||
| 696 | section_index += 1; | ||
| 697 | } | ||
| 698 | |||
| 699 | // Memory section. wasm currently only supports 1 linear memory segment. | ||
| 700 | if (!import_memory) { | ||
| 701 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 702 | try emitLimits(gpa, binary_bytes, wasm.memories.limits); | ||
| 703 | replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1); | ||
| 704 | section_index += 1; | ||
| 705 | } | ||
| 706 | |||
| 707 | // Global section. | ||
| 708 | const globals_len: u32 = @intCast(wasm.globals.entries.len); | ||
| 709 | if (globals_len > 0) { | ||
| 710 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 711 | |||
| 712 | for (wasm.globals.keys()) |global_resolution| { | ||
| 713 | switch (global_resolution.unpack(wasm)) { | ||
| 714 | .unresolved => unreachable, | ||
| 715 | .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base), | ||
| 716 | .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end), | ||
| 717 | .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer), | ||
| 718 | .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)), | ||
| 719 | .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?), | ||
| 720 | .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?), | ||
| 721 | .object_global => |i| { | ||
| 722 | const global = i.ptr(wasm); | ||
| 723 | try binary_bytes.appendSlice(gpa, &.{ | ||
| 724 | @intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())), | ||
| 725 | @intFromBool(global.flags.global_type.mutable), | ||
| 726 | }); | ||
| 727 | try emitExpr(wasm, binary_bytes, global.expr); | ||
| 728 | }, | ||
| 729 | .nav_exe => unreachable, // Zig source code currently cannot represent this. | ||
| 730 | .nav_obj => unreachable, // Zig source code currently cannot represent this. | ||
| 731 | } | ||
| 732 | } | ||
| 733 | |||
| 734 | replaceVecSectionHeader(binary_bytes, header_offset, .global, globals_len); | ||
| 735 | section_index += 1; | ||
| 736 | } | ||
| 737 | |||
| 738 | // Export section | ||
| 739 | { | ||
| 740 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 741 | var exports_len: usize = 0; | ||
| 742 | |||
| 743 | for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| { | ||
| 744 | const name = exp_name.slice(wasm); | ||
| 745 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 746 | try binary_bytes.appendSlice(gpa, name); | ||
| 747 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); | ||
| 748 | const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index); | ||
| 749 | try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); | ||
| 750 | } | ||
| 751 | exports_len += wasm.function_exports.entries.len; | ||
| 752 | |||
| 753 | if (wasm.export_table and f.indirect_function_table.entries.len > 0) { | ||
| 754 | const name = "__indirect_function_table"; | ||
| 755 | const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); | ||
| 756 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 757 | try binary_bytes.appendSlice(gpa, name); | ||
| 758 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table)); | ||
| 759 | try leb.writeUleb128(binary_writer, index); | ||
| 760 | exports_len += 1; | ||
| 761 | } | ||
| 762 | |||
| 763 | if (export_memory) { | ||
| 764 | const name = "memory"; | ||
| 765 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 766 | try binary_bytes.appendSlice(gpa, name); | ||
| 767 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); | ||
| 768 | try leb.writeUleb128(binary_writer, @as(u32, 0)); | ||
| 769 | exports_len += 1; | ||
| 770 | } | ||
| 771 | |||
| 772 | for (wasm.global_exports.items) |exp| { | ||
| 773 | const name = exp.name.slice(wasm); | ||
| 774 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | ||
| 775 | try binary_bytes.appendSlice(gpa, name); | ||
| 776 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global)); | ||
| 777 | try leb.writeUleb128(binary_writer, @intFromEnum(exp.global_index)); | ||
| 778 | } | ||
| 779 | exports_len += wasm.global_exports.items.len; | ||
| 780 | |||
| 781 | if (exports_len > 0) { | ||
| 782 | replaceVecSectionHeader(binary_bytes, header_offset, .@"export", @intCast(exports_len)); | ||
| 783 | section_index += 1; | ||
| 784 | } else { | ||
| 785 | binary_bytes.shrinkRetainingCapacity(header_offset); | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | // start section | ||
| 790 | if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| { | ||
| 791 | try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @enumFromInt(func_index))); | ||
| 792 | } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| { | ||
| 793 | try emitStartSection(gpa, binary_bytes, func_index); | ||
| 794 | } | ||
| 795 | |||
| 796 | // element section | ||
| 797 | if (f.indirect_function_table.entries.len > 0) { | ||
| 798 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 799 | |||
| 800 | // indirect function table elements | ||
| 801 | const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); | ||
| 802 | // passive with implicit 0-index table or set table index manually | ||
| 803 | const flags: u32 = if (table_index == 0) 0x0 else 0x02; | ||
| 804 | try leb.writeUleb128(binary_writer, flags); | ||
| 805 | if (flags == 0x02) { | ||
| 806 | try leb.writeUleb128(binary_writer, table_index); | ||
| 807 | } | ||
| 808 | // We start at index 1, so unresolved function pointers are invalid | ||
| 809 | try emitInit(binary_writer, .{ .i32_const = 1 }); | ||
| 810 | if (flags == 0x02) { | ||
| 811 | try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref | ||
| 812 | } | ||
| 813 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.entries.len))); | ||
| 814 | for (f.indirect_function_table.keys()) |func_index| { | ||
| 815 | try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); | ||
| 816 | } | ||
| 817 | |||
| 818 | replaceVecSectionHeader(binary_bytes, header_offset, .element, 1); | ||
| 819 | section_index += 1; | ||
| 820 | } | ||
| 821 | |||
| 822 | // When the shared-memory option is enabled, we *must* emit the 'data count' section. | ||
| 823 | if (f.data_segment_groups.items.len > 0) { | ||
| 824 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 825 | replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len)); | ||
| 826 | } | ||
| 827 | |||
| 828 | // Code section. | ||
| 829 | if (wasm.functions.count() != 0) { | ||
| 830 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 831 | |||
| 832 | for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) { | ||
| 833 | .unresolved => unreachable, | ||
| 834 | .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"), | ||
| 835 | .__wasm_call_ctors => { | ||
| 836 | const code_start = try reserveSize(gpa, binary_bytes); | ||
| 837 | defer replaceSize(binary_bytes, code_start); | ||
| 838 | try emitCallCtorsFunction(wasm, binary_bytes); | ||
| 839 | }, | ||
| 840 | .__wasm_init_memory => { | ||
| 841 | const code_start = try reserveSize(gpa, binary_bytes); | ||
| 842 | defer replaceSize(binary_bytes, code_start); | ||
| 843 | try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs); | ||
| 844 | }, | ||
| 845 | .__wasm_init_tls => { | ||
| 846 | const code_start = try reserveSize(gpa, binary_bytes); | ||
| 847 | defer replaceSize(binary_bytes, code_start); | ||
| 848 | try emitInitTlsFunction(wasm, binary_bytes); | ||
| 849 | }, | ||
| 850 | .object_function => |i| { | ||
| 851 | const ptr = i.ptr(wasm); | ||
| 852 | const code = ptr.code.slice(wasm); | ||
| 853 | try leb.writeUleb128(binary_writer, code.len); | ||
| 854 | const code_start = binary_bytes.items.len; | ||
| 855 | try binary_bytes.appendSlice(gpa, code); | ||
| 856 | if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); | ||
| 857 | }, | ||
| 858 | .zcu_func => |i| { | ||
| 859 | const code_start = try reserveSize(gpa, binary_bytes); | ||
| 860 | defer replaceSize(binary_bytes, code_start); | ||
| 861 | |||
| 862 | log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?}); | ||
| 863 | |||
| 864 | const zcu = comp.zcu.?; | ||
| 865 | const ip = &zcu.intern_pool; | ||
| 866 | const ip_index = i.key(wasm).*; | ||
| 867 | switch (ip.indexToKey(ip_index)) { | ||
| 868 | .enum_type => { | ||
| 869 | try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); | ||
| 870 | }, | ||
| 871 | else => try i.value(wasm).function.lower(wasm, binary_bytes), | ||
| 872 | } | ||
| 873 | }, | ||
| 874 | }; | ||
| 875 | |||
| 876 | replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len)); | ||
| 877 | code_section_index = section_index; | ||
| 878 | section_index += 1; | ||
| 879 | } | ||
| 880 | |||
| 881 | if (!is_obj) { | ||
| 882 | for (wasm.uav_fixups.items) |uav_fixup| { | ||
| 883 | const ds_id: Wasm.DataSegmentId = .pack(wasm, .{ .uav_exe = uav_fixup.uavs_exe_index }); | ||
| 884 | const vaddr = f.data_segments.get(ds_id).? + uav_fixup.addend; | ||
| 885 | if (!is64) { | ||
| 886 | mem.writeInt(u32, wasm.string_bytes.items[uav_fixup.offset..][0..4], vaddr, .little); | ||
| 887 | } else { | ||
| 888 | mem.writeInt(u64, wasm.string_bytes.items[uav_fixup.offset..][0..8], vaddr, .little); | ||
| 889 | } | ||
| 890 | } | ||
| 891 | for (wasm.nav_fixups.items) |nav_fixup| { | ||
| 892 | const ds_id: Wasm.DataSegmentId = .pack(wasm, .{ .nav_exe = nav_fixup.navs_exe_index }); | ||
| 893 | const vaddr = f.data_segments.get(ds_id).? + nav_fixup.addend; | ||
| 894 | if (!is64) { | ||
| 895 | mem.writeInt(u32, wasm.string_bytes.items[nav_fixup.offset..][0..4], vaddr, .little); | ||
| 896 | } else { | ||
| 897 | mem.writeInt(u64, wasm.string_bytes.items[nav_fixup.offset..][0..8], vaddr, .little); | ||
| 898 | } | ||
| 899 | } | ||
| 900 | for (wasm.func_table_fixups.items) |fixup| { | ||
| 901 | const table_index: IndirectFunctionTableIndex = .fromZcuIndirectFunctionSetIndex(fixup.table_index); | ||
| 902 | if (!is64) { | ||
| 903 | mem.writeInt(u32, wasm.string_bytes.items[fixup.offset..][0..4], table_index.toAbi(), .little); | ||
| 904 | } else { | ||
| 905 | mem.writeInt(u64, wasm.string_bytes.items[fixup.offset..][0..8], table_index.toAbi(), .little); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | } | ||
| 909 | |||
| 910 | // Data section. | ||
| 911 | if (f.data_segment_groups.items.len != 0) { | ||
| 912 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | ||
| 913 | |||
| 914 | var group_index: u32 = 0; | ||
| 915 | var segment_offset: u32 = 0; | ||
| 916 | var group_start_addr: u32 = data_vaddr; | ||
| 917 | var group_end_addr = f.data_segment_groups.items[group_index].end_addr; | ||
| 918 | for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| { | ||
| 919 | if (segment_vaddr >= group_end_addr) { | ||
| 920 | try binary_bytes.appendNTimes(gpa, 0, group_end_addr - group_start_addr - segment_offset); | ||
| 921 | group_index += 1; | ||
| 922 | if (group_index >= f.data_segment_groups.items.len) { | ||
| 923 | // All remaining segments are zero. | ||
| 924 | break; | ||
| 925 | } | ||
| 926 | group_start_addr = group_end_addr; | ||
| 927 | group_end_addr = f.data_segment_groups.items[group_index].end_addr; | ||
| 928 | segment_offset = 0; | ||
| 929 | } | ||
| 930 | if (segment_offset == 0) { | ||
| 931 | const group_size = group_end_addr - group_start_addr; | ||
| 932 | log.debug("emit data section group, {d} bytes", .{group_size}); | ||
| 933 | const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active; | ||
| 934 | try leb.writeUleb128(binary_writer, @intFromEnum(flags)); | ||
| 935 | // Passive segments are initialized at runtime. | ||
| 936 | if (flags != .passive) { | ||
| 937 | try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) }); | ||
| 938 | } | ||
| 939 | try leb.writeUleb128(binary_writer, group_size); | ||
| 940 | } | ||
| 941 | if (segment_id.isEmpty(wasm)) { | ||
| 942 | // It counted for virtual memory but it does not go into the binary. | ||
| 943 | continue; | ||
| 944 | } | ||
| 945 | |||
| 946 | // Padding for alignment. | ||
| 947 | const needed_offset = segment_vaddr - group_start_addr; | ||
| 948 | try binary_bytes.appendNTimes(gpa, 0, needed_offset - segment_offset); | ||
| 949 | segment_offset = needed_offset; | ||
| 950 | |||
| 951 | const code_start = binary_bytes.items.len; | ||
| 952 | append: { | ||
| 953 | const code = switch (segment_id.unpack(wasm)) { | ||
| 954 | .__heap_base => { | ||
| 955 | mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_base, .little); | ||
| 956 | break :append; | ||
| 957 | }, | ||
| 958 | .__heap_end => { | ||
| 959 | mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_end, .little); | ||
| 960 | break :append; | ||
| 961 | }, | ||
| 962 | .__zig_error_names => { | ||
| 963 | try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items); | ||
| 964 | break :append; | ||
| 965 | }, | ||
| 966 | .__zig_error_name_table => { | ||
| 967 | if (is_obj) @panic("TODO error name table reloc"); | ||
| 968 | const base = f.data_segments.get(.__zig_error_names).?; | ||
| 969 | if (!is64) { | ||
| 970 | try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32); | ||
| 971 | } else { | ||
| 972 | try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64); | ||
| 973 | } | ||
| 974 | break :append; | ||
| 975 | }, | ||
| 976 | .__zig_tag_names => { | ||
| 977 | try binary_bytes.appendSlice(gpa, wasm.tag_name_bytes.items); | ||
| 978 | break :append; | ||
| 979 | }, | ||
| 980 | .__zig_tag_name_table => { | ||
| 981 | if (is_obj) @panic("TODO tag name table reloc"); | ||
| 982 | const base = f.data_segments.get(.__zig_tag_names).?; | ||
| 983 | if (!is64) { | ||
| 984 | try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32); | ||
| 985 | } else { | ||
| 986 | try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64); | ||
| 987 | } | ||
| 988 | break :append; | ||
| 989 | }, | ||
| 990 | .object => |i| { | ||
| 991 | const ptr = i.ptr(wasm); | ||
| 992 | try binary_bytes.appendSlice(gpa, ptr.payload.slice(wasm)); | ||
| 993 | if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); | ||
| 994 | break :append; | ||
| 995 | }, | ||
| 996 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code, | ||
| 997 | }; | ||
| 998 | try binary_bytes.appendSlice(gpa, code.slice(wasm)); | ||
| 999 | } | ||
| 1000 | segment_offset += @intCast(binary_bytes.items.len - code_start); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | replaceVecSectionHeader(binary_bytes, header_offset, .data, @intCast(f.data_segment_groups.items.len)); | ||
| 1004 | data_section_index = section_index; | ||
| 1005 | section_index += 1; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | if (is_obj) { | ||
| 1009 | @panic("TODO emit link section for object file and emit modified relocations"); | ||
| 1010 | } else if (comp.config.debug_format != .strip) { | ||
| 1011 | try emitNameSection(wasm, f.data_segment_groups.items, binary_bytes); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | if (comp.config.debug_format != .strip) { | ||
| 1015 | // The build id must be computed on the main sections only, | ||
| 1016 | // so we have to do it now, before the debug sections. | ||
| 1017 | switch (wasm.base.build_id) { | ||
| 1018 | .none => {}, | ||
| 1019 | .fast => { | ||
| 1020 | var id: [16]u8 = undefined; | ||
| 1021 | std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{}); | ||
| 1022 | var uuid: [36]u8 = undefined; | ||
| 1023 | _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{ | ||
| 1024 | std.fmt.fmtSliceHexLower(id[0..4]), | ||
| 1025 | std.fmt.fmtSliceHexLower(id[4..6]), | ||
| 1026 | std.fmt.fmtSliceHexLower(id[6..8]), | ||
| 1027 | std.fmt.fmtSliceHexLower(id[8..10]), | ||
| 1028 | std.fmt.fmtSliceHexLower(id[10..]), | ||
| 1029 | }); | ||
| 1030 | try emitBuildIdSection(gpa, binary_bytes, &uuid); | ||
| 1031 | }, | ||
| 1032 | .hexstring => |hs| { | ||
| 1033 | var buffer: [32 * 2]u8 = undefined; | ||
| 1034 | const str = std.fmt.bufPrint(&buffer, "{s}", .{ | ||
| 1035 | std.fmt.fmtSliceHexLower(hs.toSlice()), | ||
| 1036 | }) catch unreachable; | ||
| 1037 | try emitBuildIdSection(gpa, binary_bytes, str); | ||
| 1038 | }, | ||
| 1039 | else => |mode| { | ||
| 1040 | var err = try diags.addErrorWithNotes(0); | ||
| 1041 | try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}); | ||
| 1042 | }, | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | var debug_bytes = std.ArrayList(u8).init(gpa); | ||
| 1046 | defer debug_bytes.deinit(); | ||
| 1047 | |||
| 1048 | try emitProducerSection(gpa, binary_bytes); | ||
| 1049 | try emitFeaturesSection(gpa, binary_bytes, target); | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | // Finally, write the entire binary into the file. | ||
| 1053 | const file = wasm.base.file.?; | ||
| 1054 | try file.pwriteAll(binary_bytes.items, 0); | ||
| 1055 | try file.setEndPos(binary_bytes.items.len); | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | const VirtualAddrs = struct { | ||
| 1059 | stack_pointer: u32, | ||
| 1060 | heap_base: u32, | ||
| 1061 | heap_end: u32, | ||
| 1062 | tls_base: ?u32, | ||
| 1063 | tls_align: Alignment, | ||
| 1064 | tls_size: ?u32, | ||
| 1065 | init_memory_flag: ?u32, | ||
| 1066 | }; | ||
| 1067 | |||
| 1068 | fn emitNameSection( | ||
| 1069 | wasm: *Wasm, | ||
| 1070 | data_segment_groups: []const DataSegmentGroup, | ||
| 1071 | binary_bytes: *std.ArrayListUnmanaged(u8), | ||
| 1072 | ) !void { | ||
| 1073 | const f = &wasm.flush_buffer; | ||
| 1074 | const comp = wasm.base.comp; | ||
| 1075 | const gpa = comp.gpa; | ||
| 1076 | |||
| 1077 | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1078 | defer writeCustomSectionHeader(binary_bytes, header_offset); | ||
| 1079 | |||
| 1080 | const name_name = "name"; | ||
| 1081 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len)); | ||
| 1082 | try binary_bytes.appendSlice(gpa, name_name); | ||
| 1083 | |||
| 1084 | { | ||
| 1085 | const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1086 | defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function)); | ||
| 1087 | |||
| 1088 | const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len); | ||
| 1089 | try leb.writeUleb128(binary_bytes.writer(gpa), total_functions); | ||
| 1090 | |||
| 1091 | for (f.function_imports.keys(), 0..) |name_index, function_index| { | ||
| 1092 | const name = name_index.slice(wasm); | ||
| 1093 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); | ||
| 1094 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); | ||
| 1095 | try binary_bytes.appendSlice(gpa, name); | ||
| 1096 | } | ||
| 1097 | for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| { | ||
| 1098 | const name = resolution.name(wasm).?; | ||
| 1099 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); | ||
| 1100 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); | ||
| 1101 | try binary_bytes.appendSlice(gpa, name); | ||
| 1102 | } | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | { | ||
| 1106 | const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1107 | defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global)); | ||
| 1108 | |||
| 1109 | const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len); | ||
| 1110 | try leb.writeUleb128(binary_bytes.writer(gpa), total_globals); | ||
| 1111 | |||
| 1112 | for (f.global_imports.keys(), 0..) |name_index, global_index| { | ||
| 1113 | const name = name_index.slice(wasm); | ||
| 1114 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); | ||
| 1115 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); | ||
| 1116 | try binary_bytes.appendSlice(gpa, name); | ||
| 1117 | } | ||
| 1118 | for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { | ||
| 1119 | const name = resolution.name(wasm).?; | ||
| 1120 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); | ||
| 1121 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); | ||
| 1122 | try binary_bytes.appendSlice(gpa, name); | ||
| 1123 | } | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | { | ||
| 1127 | const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1128 | defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment)); | ||
| 1129 | |||
| 1130 | const total_data_segments: u32 = @intCast(data_segment_groups.len); | ||
| 1131 | try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments); | ||
| 1132 | |||
| 1133 | for (data_segment_groups, 0..) |group, i| { | ||
| 1134 | const name, _ = splitSegmentName(group.first_segment.name(wasm)); | ||
| 1135 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i))); | ||
| 1136 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); | ||
| 1137 | try binary_bytes.appendSlice(gpa, name); | ||
| 1138 | } | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | fn emitFeaturesSection( | ||
| 1143 | gpa: Allocator, | ||
| 1144 | binary_bytes: *std.ArrayListUnmanaged(u8), | ||
| 1145 | target: *const std.Target, | ||
| 1146 | ) Allocator.Error!void { | ||
| 1147 | const feature_count = target.cpu.features.count(); | ||
| 1148 | if (feature_count == 0) return; | ||
| 1149 | |||
| 1150 | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1151 | defer writeCustomSectionHeader(binary_bytes, header_offset); | ||
| 1152 | |||
| 1153 | const writer = binary_bytes.writer(gpa); | ||
| 1154 | const target_features = "target_features"; | ||
| 1155 | try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len))); | ||
| 1156 | try writer.writeAll(target_features); | ||
| 1157 | |||
| 1158 | try leb.writeUleb128(writer, @as(u32, @intCast(feature_count))); | ||
| 1159 | |||
| 1160 | var safety_count = feature_count; | ||
| 1161 | for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| { | ||
| 1162 | if (!std.Target.wasm.featureSetHas(target.cpu.features, @enumFromInt(i))) continue; | ||
| 1163 | safety_count -= 1; | ||
| 1164 | |||
| 1165 | try leb.writeUleb128(writer, @as(u32, '+')); | ||
| 1166 | // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions. | ||
| 1167 | const name = feature.llvm_name.?; | ||
| 1168 | try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); | ||
| 1169 | try writer.writeAll(name); | ||
| 1170 | } | ||
| 1171 | assert(safety_count == 0); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void { | ||
| 1175 | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1176 | defer writeCustomSectionHeader(binary_bytes, header_offset); | ||
| 1177 | |||
| 1178 | const writer = binary_bytes.writer(gpa); | ||
| 1179 | const hdr_build_id = "build_id"; | ||
| 1180 | try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len))); | ||
| 1181 | try writer.writeAll(hdr_build_id); | ||
| 1182 | |||
| 1183 | try leb.writeUleb128(writer, @as(u32, 1)); | ||
| 1184 | try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len))); | ||
| 1185 | try writer.writeAll(build_id); | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void { | ||
| 1189 | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); | ||
| 1190 | defer writeCustomSectionHeader(binary_bytes, header_offset); | ||
| 1191 | |||
| 1192 | const writer = binary_bytes.writer(gpa); | ||
| 1193 | const producers = "producers"; | ||
| 1194 | try leb.writeUleb128(writer, @as(u32, @intCast(producers.len))); | ||
| 1195 | try writer.writeAll(producers); | ||
| 1196 | |||
| 1197 | try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by | ||
| 1198 | |||
| 1199 | // language field | ||
| 1200 | { | ||
| 1201 | const language = "language"; | ||
| 1202 | try leb.writeUleb128(writer, @as(u32, @intCast(language.len))); | ||
| 1203 | try writer.writeAll(language); | ||
| 1204 | |||
| 1205 | // field_value_count (TODO: Parse object files for producer sections to detect their language) | ||
| 1206 | try leb.writeUleb128(writer, @as(u32, 1)); | ||
| 1207 | |||
| 1208 | // versioned name | ||
| 1209 | { | ||
| 1210 | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" | ||
| 1211 | try writer.writeAll("Zig"); | ||
| 1212 | |||
| 1213 | try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); | ||
| 1214 | try writer.writeAll(build_options.version); | ||
| 1215 | } | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | // processed-by field | ||
| 1219 | { | ||
| 1220 | const processed_by = "processed-by"; | ||
| 1221 | try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len))); | ||
| 1222 | try writer.writeAll(processed_by); | ||
| 1223 | |||
| 1224 | // field_value_count (TODO: Parse object files for producer sections to detect other used tools) | ||
| 1225 | try leb.writeUleb128(writer, @as(u32, 1)); | ||
| 1226 | |||
| 1227 | // versioned name | ||
| 1228 | { | ||
| 1229 | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" | ||
| 1230 | try writer.writeAll("Zig"); | ||
| 1231 | |||
| 1232 | try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); | ||
| 1233 | try writer.writeAll(build_options.version); | ||
| 1234 | } | ||
| 1235 | } | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } { | ||
| 1239 | const start = @intFromBool(name.len >= 1 and name[0] == '.'); | ||
| 1240 | const pivot = mem.indexOfScalarPos(u8, name, start, '.') orelse name.len; | ||
| 1241 | return .{ name[0..pivot], name[pivot..] }; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | test splitSegmentName { | ||
| 1245 | { | ||
| 1246 | const a, const b = splitSegmentName(".data"); | ||
| 1247 | try std.testing.expectEqualStrings(".data", a); | ||
| 1248 | try std.testing.expectEqualStrings("", b); | ||
| 1249 | } | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | fn wantSegmentMerge( | ||
| 1253 | wasm: *const Wasm, | ||
| 1254 | a_id: Wasm.DataSegmentId, | ||
| 1255 | b_id: Wasm.DataSegmentId, | ||
| 1256 | b_category: Wasm.DataSegmentId.Category, | ||
| 1257 | ) bool { | ||
| 1258 | const a_category = a_id.category(wasm); | ||
| 1259 | if (a_category != b_category) return false; | ||
| 1260 | if (a_category == .tls or b_category == .tls) return false; | ||
| 1261 | if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false; | ||
| 1262 | if (b_category == .zero) return true; | ||
| 1263 | const a_name = a_id.name(wasm); | ||
| 1264 | const b_name = b_id.name(wasm); | ||
| 1265 | const a_prefix, _ = splitSegmentName(a_name); | ||
| 1266 | const b_prefix, _ = splitSegmentName(b_name); | ||
| 1267 | return mem.eql(u8, a_prefix, b_prefix); | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | /// section id + fixed leb contents size + fixed leb vector length | ||
| 1271 | const section_header_reserve_size = 1 + 5 + 5; | ||
| 1272 | const section_header_size = 5 + 1; | ||
| 1273 | |||
| 1274 | fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { | ||
| 1275 | try bytes.appendNTimes(gpa, 0, section_header_reserve_size); | ||
| 1276 | return @intCast(bytes.items.len - section_header_reserve_size); | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | fn replaceVecSectionHeader( | ||
| 1280 | bytes: *std.ArrayListUnmanaged(u8), | ||
| 1281 | offset: u32, | ||
| 1282 | section: std.wasm.Section, | ||
| 1283 | n_items: u32, | ||
| 1284 | ) void { | ||
| 1285 | const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items)); | ||
| 1286 | var buf: [section_header_reserve_size]u8 = undefined; | ||
| 1287 | var fbw = std.io.fixedBufferStream(&buf); | ||
| 1288 | const w = fbw.writer(); | ||
| 1289 | w.writeByte(@intFromEnum(section)) catch unreachable; | ||
| 1290 | leb.writeUleb128(w, size) catch unreachable; | ||
| 1291 | leb.writeUleb128(w, n_items) catch unreachable; | ||
| 1292 | bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten()); | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { | ||
| 1296 | try bytes.appendNTimes(gpa, 0, section_header_size); | ||
| 1297 | return @intCast(bytes.items.len - section_header_size); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void { | ||
| 1301 | return replaceHeader(bytes, offset, 0); // 0 = 'custom' section | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void { | ||
| 1305 | const size: u32 = @intCast(bytes.items.len - offset - section_header_size); | ||
| 1306 | var buf: [section_header_size]u8 = undefined; | ||
| 1307 | var fbw = std.io.fixedBufferStream(&buf); | ||
| 1308 | const w = fbw.writer(); | ||
| 1309 | w.writeByte(tag) catch unreachable; | ||
| 1310 | leb.writeUleb128(w, size) catch unreachable; | ||
| 1311 | bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten()); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | const max_size_encoding = 5; | ||
| 1315 | |||
| 1316 | fn reserveSize(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { | ||
| 1317 | try bytes.appendNTimes(gpa, 0, max_size_encoding); | ||
| 1318 | return @intCast(bytes.items.len - max_size_encoding); | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | fn replaceSize(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void { | ||
| 1322 | const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding); | ||
| 1323 | var buf: [max_size_encoding]u8 = undefined; | ||
| 1324 | var fbw = std.io.fixedBufferStream(&buf); | ||
| 1325 | leb.writeUleb128(fbw.writer(), size) catch unreachable; | ||
| 1326 | bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten()); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | fn emitLimits( | ||
| 1330 | gpa: Allocator, | ||
| 1331 | binary_bytes: *std.ArrayListUnmanaged(u8), | ||
| 1332 | limits: std.wasm.Limits, | ||
| 1333 | ) Allocator.Error!void { | ||
| 1334 | try binary_bytes.append(gpa, @bitCast(limits.flags)); | ||
| 1335 | try leb.writeUleb128(binary_bytes.writer(gpa), limits.min); | ||
| 1336 | if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max); | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | fn emitMemoryImport( | ||
| 1340 | wasm: *Wasm, | ||
| 1341 | binary_bytes: *std.ArrayListUnmanaged(u8), | ||
| 1342 | name_index: String, | ||
| 1343 | memory_import: *const Wasm.MemoryImport, | ||
| 1344 | ) Allocator.Error!void { | ||
| 1345 | const gpa = wasm.base.comp.gpa; | ||
| 1346 | const module_name = memory_import.module_name.slice(wasm); | ||
| 1347 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len))); | ||
| 1348 | try binary_bytes.appendSlice(gpa, module_name); | ||
| 1349 | |||
| 1350 | const name = name_index.slice(wasm); | ||
| 1351 | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); | ||
| 1352 | try binary_bytes.appendSlice(gpa, name); | ||
| 1353 | |||
| 1354 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); | ||
| 1355 | try emitLimits(gpa, binary_bytes, memory_import.limits()); | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { | ||
| 1359 | switch (init_expr) { | ||
| 1360 | .i32_const => |val| { | ||
| 1361 | try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1362 | try leb.writeIleb128(writer, val); | ||
| 1363 | }, | ||
| 1364 | .i64_const => |val| { | ||
| 1365 | try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const)); | ||
| 1366 | try leb.writeIleb128(writer, val); | ||
| 1367 | }, | ||
| 1368 | .f32_const => |val| { | ||
| 1369 | try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const)); | ||
| 1370 | try writer.writeInt(u32, @bitCast(val), .little); | ||
| 1371 | }, | ||
| 1372 | .f64_const => |val| { | ||
| 1373 | try writer.writeByte(@intFromEnum(std.wasm.Opcode.f64_const)); | ||
| 1374 | try writer.writeInt(u64, @bitCast(val), .little); | ||
| 1375 | }, | ||
| 1376 | .global_get => |val| { | ||
| 1377 | try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get)); | ||
| 1378 | try leb.writeUleb128(writer, val); | ||
| 1379 | }, | ||
| 1380 | } | ||
| 1381 | try writer.writeByte(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void { | ||
| 1385 | const gpa = wasm.base.comp.gpa; | ||
| 1386 | const slice = expr.slice(wasm); | ||
| 1387 | try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { | ||
| 1391 | const gpa = wasm.base.comp.gpa; | ||
| 1392 | const writer = binary_bytes.writer(gpa); | ||
| 1393 | try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info)); | ||
| 1394 | const segment_offset = binary_bytes.items.len; | ||
| 1395 | |||
| 1396 | try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count()))); | ||
| 1397 | for (wasm.segment_info.values()) |segment_info| { | ||
| 1398 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ | ||
| 1399 | segment_info.name, | ||
| 1400 | segment_info.alignment, | ||
| 1401 | segment_info.flags, | ||
| 1402 | }); | ||
| 1403 | try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len))); | ||
| 1404 | try writer.writeAll(segment_info.name); | ||
| 1405 | try leb.writeUleb128(writer, segment_info.alignment.toLog2Units()); | ||
| 1406 | try leb.writeUleb128(writer, segment_info.flags); | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | var buf: [5]u8 = undefined; | ||
| 1410 | leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset))); | ||
| 1411 | try binary_bytes.insertSlice(segment_offset, &buf); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | fn uleb128size(x: u32) u32 { | ||
| 1415 | var value = x; | ||
| 1416 | var size: u32 = 0; | ||
| 1417 | while (value != 0) : (size += 1) value >>= 7; | ||
| 1418 | return size; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | fn emitTagNameTable( | ||
| 1422 | gpa: Allocator, | ||
| 1423 | code: *std.ArrayListUnmanaged(u8), | ||
| 1424 | tag_name_offs: []const u32, | ||
| 1425 | tag_name_bytes: []const u8, | ||
| 1426 | base: u32, | ||
| 1427 | comptime Int: type, | ||
| 1428 | ) error{OutOfMemory}!void { | ||
| 1429 | const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8); | ||
| 1430 | try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * tag_name_offs.len); | ||
| 1431 | for (tag_name_offs) |off| { | ||
| 1432 | const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?); | ||
| 1433 | mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little); | ||
| 1434 | mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little); | ||
| 1435 | } | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.IterableSlice, wasm: *const Wasm) void { | ||
| 1439 | for ( | ||
| 1440 | relocs.slice.tags(wasm), | ||
| 1441 | relocs.slice.pointees(wasm), | ||
| 1442 | relocs.slice.offsets(wasm), | ||
| 1443 | relocs.slice.addends(wasm), | ||
| 1444 | ) |tag, pointee, offset, *addend| { | ||
| 1445 | if (offset >= relocs.end) break; | ||
| 1446 | const sliced_code = code[offset - code_offset ..]; | ||
| 1447 | switch (tag) { | ||
| 1448 | .function_index_i32 => reloc_u32_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)), | ||
| 1449 | .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)), | ||
| 1450 | .function_offset_i32 => @panic("TODO this value is not known yet"), | ||
| 1451 | .function_offset_i64 => @panic("TODO this value is not known yet"), | ||
| 1452 | .table_index_i32 => reloc_u32_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)), | ||
| 1453 | .table_index_i64 => reloc_u64_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)), | ||
| 1454 | .table_index_rel_sleb => @panic("TODO what does this reloc tag mean?"), | ||
| 1455 | .table_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"), | ||
| 1456 | .table_index_sleb => reloc_sleb_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)), | ||
| 1457 | .table_index_sleb64 => reloc_sleb64_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)), | ||
| 1458 | |||
| 1459 | .function_import_index_i32 => reloc_u32_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1460 | .function_import_index_leb => reloc_leb_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1461 | .function_import_offset_i32 => @panic("TODO this value is not known yet"), | ||
| 1462 | .function_import_offset_i64 => @panic("TODO this value is not known yet"), | ||
| 1463 | .table_import_index_i32 => reloc_u32_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1464 | .table_import_index_i64 => reloc_u64_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1465 | .table_import_index_rel_sleb => @panic("TODO what does this reloc tag mean?"), | ||
| 1466 | .table_import_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"), | ||
| 1467 | .table_import_index_sleb => reloc_sleb_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1468 | .table_import_index_sleb64 => reloc_sleb64_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1469 | |||
| 1470 | .global_index_i32 => reloc_u32_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)), | ||
| 1471 | .global_index_leb => reloc_leb_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)), | ||
| 1472 | |||
| 1473 | .global_import_index_i32 => reloc_u32_global(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1474 | .global_import_index_leb => reloc_leb_global(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1475 | |||
| 1476 | .memory_addr_i32 => reloc_u32_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1477 | .memory_addr_i64 => reloc_u64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1478 | .memory_addr_leb => reloc_leb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1479 | .memory_addr_leb64 => reloc_leb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1480 | .memory_addr_locrel_i32 => @panic("TODO implement relocation memory_addr_locrel_i32"), | ||
| 1481 | .memory_addr_rel_sleb => @panic("TODO implement relocation memory_addr_rel_sleb"), | ||
| 1482 | .memory_addr_rel_sleb64 => @panic("TODO implement relocation memory_addr_rel_sleb64"), | ||
| 1483 | .memory_addr_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1484 | .memory_addr_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1485 | .memory_addr_tls_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1486 | .memory_addr_tls_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)), | ||
| 1487 | |||
| 1488 | .memory_addr_import_i32 => reloc_u32_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)), | ||
| 1489 | .memory_addr_import_i64 => reloc_u64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)), | ||
| 1490 | .memory_addr_import_leb => reloc_leb_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)), | ||
| 1491 | .memory_addr_import_leb64 => reloc_leb64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)), | ||
| 1492 | .memory_addr_import_locrel_i32 => @panic("TODO implement relocation memory_addr_import_locrel_i32"), | ||
| 1493 | .memory_addr_import_rel_sleb => @panic("TODO implement relocation memory_addr_import_rel_sleb"), | ||
| 1494 | .memory_addr_import_rel_sleb64 => @panic("TODO implement memory_addr_import_rel_sleb64"), | ||
| 1495 | .memory_addr_import_sleb => reloc_sleb_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)), | ||
| 1496 | .memory_addr_import_sleb64 => reloc_sleb64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)), | ||
| 1497 | .memory_addr_import_tls_sleb => @panic("TODO"), | ||
| 1498 | .memory_addr_import_tls_sleb64 => @panic("TODO"), | ||
| 1499 | |||
| 1500 | .section_offset_i32 => @panic("TODO this value is not known yet"), | ||
| 1501 | |||
| 1502 | .table_number_leb => reloc_leb_table(sliced_code, .fromObjectTable(wasm, pointee.table)), | ||
| 1503 | .table_import_number_leb => reloc_leb_table(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)), | ||
| 1504 | |||
| 1505 | .type_index_leb => reloc_leb_type(sliced_code, .fromTypeIndex(pointee.type_index, &wasm.flush_buffer)), | ||
| 1506 | } | ||
| 1507 | } | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | fn reloc_u32_table_index(code: []u8, i: IndirectFunctionTableIndex) void { | ||
| 1511 | mem.writeInt(u32, code[0..4], i.toAbi(), .little); | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | fn reloc_u64_table_index(code: []u8, i: IndirectFunctionTableIndex) void { | ||
| 1515 | mem.writeInt(u64, code[0..8], i.toAbi(), .little); | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | fn reloc_sleb_table_index(code: []u8, i: IndirectFunctionTableIndex) void { | ||
| 1519 | leb.writeSignedFixed(5, code[0..5], i.toAbi()); | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | fn reloc_sleb64_table_index(code: []u8, i: IndirectFunctionTableIndex) void { | ||
| 1523 | leb.writeSignedFixed(11, code[0..11], i.toAbi()); | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void { | ||
| 1527 | mem.writeInt(u32, code[0..4], @intFromEnum(function), .little); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | fn reloc_leb_function(code: []u8, function: Wasm.OutputFunctionIndex) void { | ||
| 1531 | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function)); | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void { | ||
| 1535 | mem.writeInt(u32, code[0..4], @intFromEnum(global), .little); | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | fn reloc_leb_global(code: []u8, global: Wasm.GlobalIndex) void { | ||
| 1539 | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global)); | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | const RelocAddr = struct { | ||
| 1543 | addr: u32, | ||
| 1544 | |||
| 1545 | fn fromObjectData(wasm: *const Wasm, i: Wasm.ObjectData.Index, addend: i32) RelocAddr { | ||
| 1546 | return fromDataLoc(&wasm.flush_buffer, .fromObjectDataIndex(wasm, i), addend); | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | fn fromSymbolName(wasm: *const Wasm, name: String, addend: i32) RelocAddr { | ||
| 1550 | const flush = &wasm.flush_buffer; | ||
| 1551 | if (wasm.object_data_imports.getPtr(name)) |import| { | ||
| 1552 | return fromDataLoc(flush, import.resolution.dataLoc(wasm), addend); | ||
| 1553 | } else if (wasm.data_imports.get(name)) |id| { | ||
| 1554 | return fromDataLoc(flush, .fromDataImportId(wasm, id), addend); | ||
| 1555 | } else { | ||
| 1556 | unreachable; | ||
| 1557 | } | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | fn fromDataLoc(flush: *const Flush, data_loc: Wasm.DataLoc, addend: i32) RelocAddr { | ||
| 1561 | const base_addr: i64 = flush.data_segments.get(data_loc.segment).?; | ||
| 1562 | return .{ .addr = @intCast(base_addr + data_loc.offset + addend) }; | ||
| 1563 | } | ||
| 1564 | }; | ||
| 1565 | |||
| 1566 | fn reloc_u32_addr(code: []u8, ra: RelocAddr) void { | ||
| 1567 | mem.writeInt(u32, code[0..4], ra.addr, .little); | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | fn reloc_u64_addr(code: []u8, ra: RelocAddr) void { | ||
| 1571 | mem.writeInt(u64, code[0..8], ra.addr, .little); | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | fn reloc_leb_addr(code: []u8, ra: RelocAddr) void { | ||
| 1575 | leb.writeUnsignedFixed(5, code[0..5], ra.addr); | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | fn reloc_leb64_addr(code: []u8, ra: RelocAddr) void { | ||
| 1579 | leb.writeUnsignedFixed(11, code[0..11], ra.addr); | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | fn reloc_sleb_addr(code: []u8, ra: RelocAddr) void { | ||
| 1583 | leb.writeSignedFixed(5, code[0..5], ra.addr); | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | fn reloc_sleb64_addr(code: []u8, ra: RelocAddr) void { | ||
| 1587 | leb.writeSignedFixed(11, code[0..11], ra.addr); | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | fn reloc_leb_table(code: []u8, table: Wasm.TableIndex) void { | ||
| 1591 | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table)); | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void { | ||
| 1595 | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index)); | ||
| 1596 | } | ||
| 1597 | |||
| 1598 | fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { | ||
| 1599 | const gpa = wasm.base.comp.gpa; | ||
| 1600 | |||
| 1601 | try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1); | ||
| 1602 | appendReservedUleb32(binary_bytes, 0); // no locals | ||
| 1603 | |||
| 1604 | for (wasm.object_init_funcs.items) |init_func| { | ||
| 1605 | const func = init_func.function_index.ptr(wasm); | ||
| 1606 | if (!func.object_index.ptr(wasm).is_included) continue; | ||
| 1607 | const ty = func.type_index.ptr(wasm); | ||
| 1608 | const n_returns = ty.returns.slice(wasm).len; | ||
| 1609 | |||
| 1610 | // Call function by its function index | ||
| 1611 | try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + n_returns + 1); | ||
| 1612 | const call_index: Wasm.OutputFunctionIndex = .fromObjectFunction(wasm, init_func.function_index); | ||
| 1613 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); | ||
| 1614 | appendReservedUleb32(binary_bytes, @intFromEnum(call_index)); | ||
| 1615 | |||
| 1616 | // drop all returned values from the stack as __wasm_call_ctors has no return value | ||
| 1617 | binary_bytes.appendNTimesAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop), n_returns); | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end function body | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | fn emitInitMemoryFunction( | ||
| 1624 | wasm: *const Wasm, | ||
| 1625 | binary_bytes: *std.ArrayListUnmanaged(u8), | ||
| 1626 | virtual_addrs: *const VirtualAddrs, | ||
| 1627 | ) Allocator.Error!void { | ||
| 1628 | const comp = wasm.base.comp; | ||
| 1629 | const gpa = comp.gpa; | ||
| 1630 | const shared_memory = comp.config.shared_memory; | ||
| 1631 | |||
| 1632 | // Passive segments are used to avoid memory being reinitialized on each | ||
| 1633 | // thread's instantiation. These passive segments are initialized and | ||
| 1634 | // dropped in __wasm_init_memory, which is registered as the start function | ||
| 1635 | // We also initialize bss segments (using memory.fill) as part of this | ||
| 1636 | // function. | ||
| 1637 | assert(wasm.any_passive_inits); | ||
| 1638 | |||
| 1639 | try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1); | ||
| 1640 | appendReservedUleb32(binary_bytes, 0); // no locals | ||
| 1641 | |||
| 1642 | if (virtual_addrs.init_memory_flag) |flag_address| { | ||
| 1643 | assert(shared_memory); | ||
| 1644 | try binary_bytes.ensureUnusedCapacity(gpa, 2 * 3 + 6 * 3 + 1 + 6 * 3 + 1 + 5 * 4 + 1 + 1); | ||
| 1645 | // destination blocks | ||
| 1646 | // based on values we jump to corresponding label | ||
| 1647 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $drop | ||
| 1648 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); | ||
| 1649 | |||
| 1650 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $wait | ||
| 1651 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); | ||
| 1652 | |||
| 1653 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $init | ||
| 1654 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); | ||
| 1655 | |||
| 1656 | // atomically check | ||
| 1657 | appendReservedI32Const(binary_bytes, flag_address); | ||
| 1658 | appendReservedI32Const(binary_bytes, 0); | ||
| 1659 | appendReservedI32Const(binary_bytes, 1); | ||
| 1660 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); | ||
| 1661 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_rmw_cmpxchg)); | ||
| 1662 | appendReservedUleb32(binary_bytes, 2); // alignment | ||
| 1663 | appendReservedUleb32(binary_bytes, 0); // offset | ||
| 1664 | |||
| 1665 | // based on the value from the atomic check, jump to the label. | ||
| 1666 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table)); | ||
| 1667 | appendReservedUleb32(binary_bytes, 2); // length of the table (we have 3 blocks but because of the mandatory default the length is 2). | ||
| 1668 | appendReservedUleb32(binary_bytes, 0); // $init | ||
| 1669 | appendReservedUleb32(binary_bytes, 1); // $wait | ||
| 1670 | appendReservedUleb32(binary_bytes, 2); // $drop | ||
| 1671 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | const segment_groups = wasm.flush_buffer.data_segment_groups.items; | ||
| 1675 | var prev_end: u32 = 0; | ||
| 1676 | for (segment_groups, 0..) |group, segment_index| { | ||
| 1677 | defer prev_end = group.end_addr; | ||
| 1678 | const segment = group.first_segment; | ||
| 1679 | if (!segment.isPassive(wasm)) continue; | ||
| 1680 | |||
| 1681 | const start_addr: u32 = @intCast(segment.alignment(wasm).forward(prev_end)); | ||
| 1682 | const segment_size: u32 = group.end_addr - start_addr; | ||
| 1683 | |||
| 1684 | try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 5 + 6 + 6 + 1 + 6 * 2 + 1 + 1); | ||
| 1685 | |||
| 1686 | // For passive BSS segments we can simply issue a memory.fill(0). For | ||
| 1687 | // non-BSS segments we do a memory.init. Both instructions take as | ||
| 1688 | // their first argument the destination address. | ||
| 1689 | appendReservedI32Const(binary_bytes, start_addr); | ||
| 1690 | |||
| 1691 | if (shared_memory and segment.isTls(wasm)) { | ||
| 1692 | // When we initialize the TLS segment we also set the `__tls_base` | ||
| 1693 | // global. This allows the runtime to use this static copy of the | ||
| 1694 | // TLS data for the first/main thread. | ||
| 1695 | appendReservedI32Const(binary_bytes, start_addr); | ||
| 1696 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); | ||
| 1697 | appendReservedUleb32(binary_bytes, virtual_addrs.tls_base.?); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | appendReservedI32Const(binary_bytes, 0); | ||
| 1701 | appendReservedI32Const(binary_bytes, segment_size); | ||
| 1702 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); | ||
| 1703 | if (segment.isBss(wasm)) { | ||
| 1704 | // fill bss segment with zeroes | ||
| 1705 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_fill)); | ||
| 1706 | } else { | ||
| 1707 | // initialize the segment | ||
| 1708 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init)); | ||
| 1709 | appendReservedUleb32(binary_bytes, @intCast(segment_index)); | ||
| 1710 | } | ||
| 1711 | binary_bytes.appendAssumeCapacity(0); // memory index immediate | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | if (virtual_addrs.init_memory_flag) |flag_address| { | ||
| 1715 | assert(shared_memory); | ||
| 1716 | try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 3 * 5 + 6 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 5 + 1 + 6 * 2 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 1); | ||
| 1717 | // we set the init memory flag to value '2' | ||
| 1718 | appendReservedI32Const(binary_bytes, flag_address); | ||
| 1719 | appendReservedI32Const(binary_bytes, 2); | ||
| 1720 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); | ||
| 1721 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_store)); | ||
| 1722 | appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment | ||
| 1723 | appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset | ||
| 1724 | |||
| 1725 | // notify any waiters for segment initialization completion | ||
| 1726 | appendReservedI32Const(binary_bytes, flag_address); | ||
| 1727 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1728 | leb.writeIleb128(binary_bytes.fixedWriter(), @as(i32, -1)) catch unreachable; // number of waiters | ||
| 1729 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); | ||
| 1730 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify)); | ||
| 1731 | appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment | ||
| 1732 | appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset | ||
| 1733 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop)); | ||
| 1734 | |||
| 1735 | // branch and drop segments | ||
| 1736 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br)); | ||
| 1737 | appendReservedUleb32(binary_bytes, @as(u32, 1)); | ||
| 1738 | |||
| 1739 | // wait for thread to initialize memory segments | ||
| 1740 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $wait | ||
| 1741 | appendReservedI32Const(binary_bytes, flag_address); | ||
| 1742 | appendReservedI32Const(binary_bytes, 1); // expected flag value | ||
| 1743 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); | ||
| 1744 | leb.writeIleb128(binary_bytes.fixedWriter(), @as(i64, -1)) catch unreachable; // timeout | ||
| 1745 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); | ||
| 1746 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32)); | ||
| 1747 | appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment | ||
| 1748 | appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset | ||
| 1749 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop)); | ||
| 1750 | |||
| 1751 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $drop | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | for (segment_groups, 0..) |group, segment_index| { | ||
| 1755 | const segment = group.first_segment; | ||
| 1756 | if (!segment.isPassive(wasm)) continue; | ||
| 1757 | if (segment.isBss(wasm)) continue; | ||
| 1758 | // The TLS region should not be dropped since its is needed | ||
| 1759 | // during the initialization of each thread (__wasm_init_tls). | ||
| 1760 | if (shared_memory and segment.isTls(wasm)) continue; | ||
| 1761 | |||
| 1762 | try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + 5 + 1); | ||
| 1763 | |||
| 1764 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); | ||
| 1765 | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.data_drop)); | ||
| 1766 | appendReservedUleb32(binary_bytes, @intCast(segment_index)); | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | // End of the function body | ||
| 1770 | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1771 | } | ||
| 1772 | |||
| 1773 | fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { | ||
| 1774 | const comp = wasm.base.comp; | ||
| 1775 | const gpa = comp.gpa; | ||
| 1776 | |||
| 1777 | assert(comp.config.shared_memory); | ||
| 1778 | |||
| 1779 | try bytes.ensureUnusedCapacity(gpa, 5 * 10 + 8); | ||
| 1780 | |||
| 1781 | appendReservedUleb32(bytes, 0); // no locals | ||
| 1782 | |||
| 1783 | // If there's a TLS segment, initialize it during runtime using the bulk-memory feature | ||
| 1784 | // TLS segment is always the first one due to how we sort the data segments. | ||
| 1785 | const data_segments = wasm.flush_buffer.data_segments.keys(); | ||
| 1786 | if (data_segments.len > 0 and data_segments[0].isTls(wasm)) { | ||
| 1787 | const start_addr = wasm.flush_buffer.data_segments.values()[0]; | ||
| 1788 | const end_addr = wasm.flush_buffer.data_segment_groups.items[0].end_addr; | ||
| 1789 | const group_size = end_addr - start_addr; | ||
| 1790 | const data_segment_index = 0; | ||
| 1791 | |||
| 1792 | const param_local: u32 = 0; | ||
| 1793 | |||
| 1794 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1795 | appendReservedUleb32(bytes, param_local); | ||
| 1796 | |||
| 1797 | const tls_base_global_index: Wasm.GlobalIndex = @enumFromInt(wasm.globals.getIndex(.__tls_base).?); | ||
| 1798 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); | ||
| 1799 | appendReservedUleb32(bytes, @intFromEnum(tls_base_global_index)); | ||
| 1800 | |||
| 1801 | // load stack values for the bulk-memory operation | ||
| 1802 | { | ||
| 1803 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1804 | appendReservedUleb32(bytes, param_local); | ||
| 1805 | |||
| 1806 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1807 | appendReservedUleb32(bytes, 0); //segment offset | ||
| 1808 | |||
| 1809 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1810 | appendReservedUleb32(bytes, group_size); //segment offset | ||
| 1811 | } | ||
| 1812 | |||
| 1813 | // perform the bulk-memory operation to initialize the data segment | ||
| 1814 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); | ||
| 1815 | appendReservedUleb32(bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init)); | ||
| 1816 | // segment immediate | ||
| 1817 | appendReservedUleb32(bytes, data_segment_index); | ||
| 1818 | // memory index immediate (always 0) | ||
| 1819 | appendReservedUleb32(bytes, 0); | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | // If we have to perform any TLS relocations, call the corresponding function | ||
| 1823 | // which performs all runtime TLS relocations. This is a synthetic function, | ||
| 1824 | // generated by the linker. | ||
| 1825 | if (wasm.functions.getIndex(.__wasm_apply_global_tls_relocs)) |function_index| { | ||
| 1826 | const output_function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex(wasm, @enumFromInt(function_index)); | ||
| 1827 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); | ||
| 1828 | appendReservedUleb32(bytes, @intFromEnum(output_function_index)); | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void { | ||
| 1835 | const header_offset = try reserveVecSectionHeader(gpa, bytes); | ||
| 1836 | replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | fn emitTagNameFunction( | ||
| 1840 | wasm: *Wasm, | ||
| 1841 | code: *std.ArrayListUnmanaged(u8), | ||
| 1842 | table_base_addr: u32, | ||
| 1843 | table_index: u32, | ||
| 1844 | enum_type_ip: InternPool.Index, | ||
| 1845 | ) !void { | ||
| 1846 | const comp = wasm.base.comp; | ||
| 1847 | const gpa = comp.gpa; | ||
| 1848 | const diags = &comp.link_diags; | ||
| 1849 | const zcu = comp.zcu.?; | ||
| 1850 | const ip = &zcu.intern_pool; | ||
| 1851 | const enum_type = ip.loadEnumType(enum_type_ip); | ||
| 1852 | const tag_values = enum_type.values.get(ip); | ||
| 1853 | |||
| 1854 | try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6); | ||
| 1855 | appendReservedUleb32(code, 0); // no locals | ||
| 1856 | |||
| 1857 | const slice_abi_size = 8; | ||
| 1858 | const encoded_alignment = @ctz(@as(u32, 4)); | ||
| 1859 | if (tag_values.len == 0) { | ||
| 1860 | // Then it's auto-numbered and therefore a direct table lookup. | ||
| 1861 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1862 | appendReservedUleb32(code, 0); | ||
| 1863 | |||
| 1864 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1865 | appendReservedUleb32(code, 1); | ||
| 1866 | |||
| 1867 | appendReservedI32Const(code, slice_abi_size); | ||
| 1868 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_mul)); | ||
| 1869 | |||
| 1870 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load)); | ||
| 1871 | appendReservedUleb32(code, encoded_alignment); | ||
| 1872 | appendReservedUleb32(code, table_base_addr + table_index * 8); | ||
| 1873 | |||
| 1874 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); | ||
| 1875 | appendReservedUleb32(code, encoded_alignment); | ||
| 1876 | appendReservedUleb32(code, 0); | ||
| 1877 | } else { | ||
| 1878 | const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu); | ||
| 1879 | const outer_block_type: std.wasm.BlockType = switch (int_info.bits) { | ||
| 1880 | 0...32 => .i32, | ||
| 1881 | 33...64 => .i64, | ||
| 1882 | else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}), | ||
| 1883 | }; | ||
| 1884 | |||
| 1885 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1886 | appendReservedUleb32(code, 0); | ||
| 1887 | |||
| 1888 | // Outer block that computes table offset. | ||
| 1889 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); | ||
| 1890 | code.appendAssumeCapacity(@intFromEnum(outer_block_type)); | ||
| 1891 | |||
| 1892 | for (tag_values, 0..) |tag_value, tag_index| { | ||
| 1893 | // block for this if case | ||
| 1894 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); | ||
| 1895 | code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); | ||
| 1896 | |||
| 1897 | // Tag value whose name should be returned. | ||
| 1898 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); | ||
| 1899 | appendReservedUleb32(code, 1); | ||
| 1900 | |||
| 1901 | const val: Zcu.Value = .fromInterned(tag_value); | ||
| 1902 | switch (outer_block_type) { | ||
| 1903 | .i32 => { | ||
| 1904 | const x: u32 = switch (int_info.signedness) { | ||
| 1905 | .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))), | ||
| 1906 | .unsigned => @intCast(val.toUnsignedInt(zcu)), | ||
| 1907 | }; | ||
| 1908 | appendReservedI32Const(code, x); | ||
| 1909 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne)); | ||
| 1910 | }, | ||
| 1911 | .i64 => { | ||
| 1912 | const x: u64 = switch (int_info.signedness) { | ||
| 1913 | .signed => @bitCast(val.toSignedInt(zcu)), | ||
| 1914 | .unsigned => val.toUnsignedInt(zcu), | ||
| 1915 | }; | ||
| 1916 | appendReservedI64Const(code, x); | ||
| 1917 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne)); | ||
| 1918 | }, | ||
| 1919 | else => unreachable, | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | // if they're not equal, break out of current branch | ||
| 1923 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if)); | ||
| 1924 | appendReservedUleb32(code, 0); | ||
| 1925 | |||
| 1926 | // Put the table offset of the result on the stack. | ||
| 1927 | appendReservedI32Const(code, @intCast(tag_index * slice_abi_size)); | ||
| 1928 | |||
| 1929 | // break outside blocks | ||
| 1930 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br)); | ||
| 1931 | appendReservedUleb32(code, 1); | ||
| 1932 | |||
| 1933 | // end the block for this case | ||
| 1934 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1935 | } | ||
| 1936 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable")); | ||
| 1937 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1938 | |||
| 1939 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load)); | ||
| 1940 | appendReservedUleb32(code, encoded_alignment); | ||
| 1941 | appendReservedUleb32(code, table_base_addr + table_index * 8); | ||
| 1942 | |||
| 1943 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); | ||
| 1944 | appendReservedUleb32(code, encoded_alignment); | ||
| 1945 | appendReservedUleb32(code, 0); | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | // End of the function body | ||
| 1949 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. | ||
| 1953 | fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { | ||
| 1954 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1955 | leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable; | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | /// Writes an unsigned 64-bit integer as a LEB128-encoded 'i64.const' value. | ||
| 1959 | fn appendReservedI64Const(bytes: *std.ArrayListUnmanaged(u8), val: u64) void { | ||
| 1960 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); | ||
| 1961 | leb.writeIleb128(bytes.fixedWriter(), @as(i64, @bitCast(val))) catch unreachable; | ||
| 1962 | } | ||
| 1963 | |||
| 1964 | fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { | ||
| 1965 | leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable; | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8, val: u32) Allocator.Error!void { | ||
| 1969 | try bytes.ensureUnusedCapacity(gpa, 9); | ||
| 1970 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32)); | ||
| 1971 | bytes.appendAssumeCapacity(mutable); | ||
| 1972 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1973 | appendReservedUleb32(bytes, val); | ||
| 1974 | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); | ||
| 1975 | } | ||
src/link/Wasm/Object.zig+1416-737| ... | @@ -1,841 +1,1520 @@ | ... | @@ -1,841 +1,1520 @@ |
| 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. | ||
| 4 | const Object = @This(); | 1 | const Object = @This(); |
| 5 | 2 | ||
| 6 | const Wasm = @import("../Wasm.zig"); | 3 | const Wasm = @import("../Wasm.zig"); |
| 7 | const Atom = Wasm.Atom; | ||
| 8 | const Alignment = Wasm.Alignment; | 4 | const Alignment = Wasm.Alignment; |
| 9 | const Symbol = @import("Symbol.zig"); | ||
| 10 | 5 | ||
| 11 | const std = @import("std"); | 6 | const std = @import("std"); |
| 12 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 13 | const leb = std.leb; | ||
| 14 | const meta = std.meta; | ||
| 15 | const Path = std.Build.Cache.Path; | 8 | const Path = std.Build.Cache.Path; |
| 16 | |||
| 17 | const log = std.log.scoped(.object); | 9 | const log = std.log.scoped(.object); |
| 10 | const assert = std.debug.assert; | ||
| 18 | 11 | ||
| 19 | /// Wasm spec version used for this `Object` | 12 | /// Wasm spec version used for this `Object` |
| 20 | version: u32 = 0, | 13 | version: 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. |
| 23 | path: Path, | 16 | path: Path, |
| 24 | /// For error reporting purposes only. | 17 | /// For error reporting purposes only. |
| 25 | /// If this represents an object in an archive, it's the basename of the | 18 | /// 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. |
| 27 | archive_member_name: ?[]const u8, | 20 | archive_member_name: Wasm.OptionalString, |
| 28 | /// Parsed type section | ||
| 29 | func_types: []const std.wasm.Type = &.{}, | ||
| 30 | /// A list of all imports for this module | ||
| 31 | imports: []const Wasm.Import = &.{}, | ||
| 32 | /// Parsed function section | ||
| 33 | functions: []const std.wasm.Func = &.{}, | ||
| 34 | /// Parsed table section | ||
| 35 | tables: []const std.wasm.Table = &.{}, | ||
| 36 | /// Parsed memory section | ||
| 37 | memories: []const std.wasm.Memory = &.{}, | ||
| 38 | /// Parsed global section | ||
| 39 | globals: []const std.wasm.Global = &.{}, | ||
| 40 | /// Parsed export section | ||
| 41 | exports: []const Wasm.Export = &.{}, | ||
| 42 | /// Parsed element section | ||
| 43 | elements: []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 startup | 22 | /// 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. |
| 47 | start: ?u32 = null, | 24 | start_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 another | 26 | /// (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. |
| 51 | features: []const Wasm.Feature = &.{}, | 28 | features: Wasm.Feature.Set, |
| 52 | /// A table that maps the relocations we must perform where the key represents | 29 | /// Points into `Wasm.object_functions` |
| 53 | /// the section that the list of relocations applies to. | 30 | functions: RelativeSlice, |
| 54 | relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty, | 31 | /// Points into `Wasm.object_function_imports` |
| 55 | /// Table of symbols belonging to this Object file | 32 | function_imports: RelativeSlice, |
| 56 | symtable: []Symbol = &.{}, | 33 | /// Points into `Wasm.object_global_imports` |
| 57 | /// Extra metadata about the linking section, such as alignment of segments and their name | 34 | global_imports: RelativeSlice, |
| 58 | segment_info: []const Wasm.NamedSegment = &.{}, | 35 | /// Points into `Wasm.object_table_imports` |
| 59 | /// A sequence of function initializers that must be called on startup | 36 | table_imports: RelativeSlice, |
| 60 | init_funcs: []const Wasm.InitFunc = &.{}, | 37 | // Points into `Wasm.object_data_imports` |
| 61 | /// Comdat information | 38 | data_imports: RelativeSlice, |
| 62 | comdat_info: []const Wasm.Comdat = &.{}, | 39 | /// Points into Wasm object_custom_segments |
| 63 | /// Represents non-synthetic sections that can essentially be mem-cpy'd into place | 40 | custom_segments: RelativeSlice, |
| 64 | /// after performing relocations. | 41 | /// Points into Wasm object_init_funcs |
| 65 | relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty, | 42 | init_funcs: RelativeSlice, |
| 66 | /// Amount of functions in the `import` sections. | 43 | /// Points into Wasm object_comdats |
| 67 | imported_functions_count: u32 = 0, | 44 | comdats: RelativeSlice, |
| 68 | /// Amount of globals in the `import` section. | 45 | /// Guaranteed to be non-null when functions has nonzero length. |
| 69 | imported_globals_count: u32 = 0, | 46 | code_section_index: ?Wasm.ObjectSectionIndex, |
| 70 | /// Amount of tables in the `import` section. | 47 | /// Guaranteed to be non-null when globals has nonzero length. |
| 71 | imported_tables_count: u32 = 0, | 48 | global_section_index: ?Wasm.ObjectSectionIndex, |
| 72 | 49 | /// Guaranteed to be non-null when data segments has nonzero length. | |
| 73 | /// Represents a single item within a section (depending on its `type`) | 50 | data_section_index: ?Wasm.ObjectSectionIndex, |
| 74 | pub const RelocatableData = struct { | 51 | is_included: bool, |
| 75 | /// The type of the relocatable data | 52 | |
| 76 | type: Tag, | 53 | pub const RelativeSlice = struct { |
| 77 | /// Pointer to the data of the segment, where its length is written to `size` | 54 | off: u32, |
| 78 | data: [*]u8, | 55 | len: u32, |
| 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 | }; | 56 | }; |
| 119 | 57 | ||
| 120 | /// Initializes a new `Object` from a wasm object file. | 58 | pub const SegmentInfo = struct { |
| 121 | /// This also parses and verifies the object file. | 59 | name: Wasm.String, |
| 122 | /// When a max size is given, will only parse up to the given size, | 60 | flags: Flags, |
| 123 | /// else will read until the end of the file. | 61 | |
| 124 | pub fn create( | 62 | /// Matches the ABI. |
| 125 | wasm: *Wasm, | 63 | pub const Flags = packed struct(u32) { |
| 126 | file_contents: []const u8, | 64 | /// Signals that the segment contains only null terminated strings allowing |
| 127 | path: Path, | 65 | /// the linker to perform merging. |
| 128 | archive_member_name: ?[]const u8, | 66 | strings: bool, |
| 129 | ) !Object { | 67 | /// The segment contains thread-local data. This means that a unique copy |
| 130 | const gpa = wasm.base.comp.gpa; | 68 | /// of this segment will be created for each thread. |
| 131 | var object: Object = .{ | 69 | tls: bool, |
| 132 | .path = path, | 70 | /// If the object file is included in the final link, the segment should be |
| 133 | .archive_member_name = archive_member_name, | 71 | /// retained in the final output regardless of whether it is used by the |
| 72 | /// program. | ||
| 73 | retain: bool, | ||
| 74 | alignment: Alignment, | ||
| 75 | |||
| 76 | _: u23 = 0, | ||
| 134 | }; | 77 | }; |
| 78 | }; | ||
| 135 | 79 | ||
| 136 | var parser: Parser = .{ | 80 | pub const FunctionImport = struct { |
| 137 | .object = &object, | 81 | module_name: Wasm.String, |
| 138 | .wasm = wasm, | 82 | name: Wasm.String, |
| 139 | .reader = std.io.fixedBufferStream(file_contents), | 83 | function_index: ScratchSpace.FuncTypeIndex, |
| 140 | }; | 84 | }; |
| 141 | try parser.parseObject(gpa); | ||
| 142 | 85 | ||
| 143 | return object; | 86 | pub const GlobalImport = struct { |
| 144 | } | 87 | module_name: Wasm.String, |
| 88 | name: Wasm.String, | ||
| 89 | valtype: std.wasm.Valtype, | ||
| 90 | mutable: bool, | ||
| 91 | }; | ||
| 145 | 92 | ||
| 146 | /// Frees all memory of `Object` at once. The given `Allocator` must be | 93 | pub const TableImport = struct { |
| 147 | /// the same allocator that was used when `init` was called. | 94 | module_name: Wasm.String, |
| 148 | pub fn deinit(object: *Object, gpa: Allocator) void { | 95 | name: Wasm.String, |
| 149 | for (object.func_types) |func_ty| { | 96 | limits_min: u32, |
| 150 | gpa.free(func_ty.params); | 97 | limits_max: u32, |
| 151 | gpa.free(func_ty.returns); | 98 | limits_has_max: bool, |
| 152 | } | 99 | limits_is_shared: bool, |
| 153 | gpa.free(object.func_types); | 100 | ref_type: std.wasm.RefType, |
| 154 | gpa.free(object.functions); | 101 | }; |
| 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 | } | ||
| 188 | 102 | ||
| 189 | /// Finds the import within the list of imports from a given kind and index of that kind. | 103 | pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx }; |
| 190 | /// Asserts the import exists | ||
| 191 | pub fn findImport(object: *const Object, sym: Symbol) Wasm.Import { | ||
| 192 | var i: u32 = 0; | ||
| 193 | return for (object.imports) |import| { | ||
| 194 | if (std.meta.activeTag(import.kind) == sym.tag.externalType()) { | ||
| 195 | if (i == sym.index) return import; | ||
| 196 | i += 1; | ||
| 197 | } | ||
| 198 | } else unreachable; // Only existing imports are allowed to be found | ||
| 199 | } | ||
| 200 | 104 | ||
| 201 | /// Checks if the object file is an MVP version. | 105 | pub const SubsectionType = enum(u8) { |
| 202 | /// When that's the case, we check if there's an import table definition with its name | 106 | segment_info = 5, |
| 203 | /// set to '__indirect_function_table". When that's also the case, | 107 | init_funcs = 6, |
| 204 | /// we initialize a new table symbol that corresponds to that import and return that symbol. | 108 | comdat_info = 7, |
| 205 | /// | 109 | symbol_table = 8, |
| 206 | /// When the object file is *NOT* MVP, we return `null`. | 110 | }; |
| 207 | fn checkLegacyIndirectFunctionTable(object: *Object, wasm: *const Wasm) !?Symbol { | ||
| 208 | const diags = &wasm.base.comp.link_diags; | ||
| 209 | 111 | ||
| 210 | var table_count: usize = 0; | 112 | /// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md |
| 211 | for (object.symtable) |sym| { | 113 | pub const RelocationType = enum(u8) { |
| 212 | if (sym.tag == .table) table_count += 1; | 114 | function_index_leb = 0, |
| 213 | } | 115 | table_index_sleb = 1, |
| 116 | table_index_i32 = 2, | ||
| 117 | memory_addr_leb = 3, | ||
| 118 | memory_addr_sleb = 4, | ||
| 119 | memory_addr_i32 = 5, | ||
| 120 | type_index_leb = 6, | ||
| 121 | global_index_leb = 7, | ||
| 122 | function_offset_i32 = 8, | ||
| 123 | section_offset_i32 = 9, | ||
| 124 | event_index_leb = 10, | ||
| 125 | memory_addr_rel_sleb = 11, | ||
| 126 | table_index_rel_sleb = 12, | ||
| 127 | global_index_i32 = 13, | ||
| 128 | memory_addr_leb64 = 14, | ||
| 129 | memory_addr_sleb64 = 15, | ||
| 130 | memory_addr_i64 = 16, | ||
| 131 | memory_addr_rel_sleb64 = 17, | ||
| 132 | table_index_sleb64 = 18, | ||
| 133 | table_index_i64 = 19, | ||
| 134 | table_number_leb = 20, | ||
| 135 | memory_addr_tls_sleb = 21, | ||
| 136 | function_offset_i64 = 22, | ||
| 137 | memory_addr_locrel_i32 = 23, | ||
| 138 | table_index_rel_sleb64 = 24, | ||
| 139 | memory_addr_tls_sleb64 = 25, | ||
| 140 | function_index_i32 = 26, | ||
| 141 | }; | ||
| 214 | 142 | ||
| 215 | // For each import table, we also have a symbol so this is not a legacy object file | 143 | pub const Symbol = struct { |
| 216 | if (object.imported_tables_count == table_count) return null; | 144 | flags: Wasm.SymbolFlags, |
| 145 | name: Wasm.OptionalString, | ||
| 146 | pointee: Pointee, | ||
| 147 | |||
| 148 | /// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection | ||
| 149 | const Tag = enum(u8) { | ||
| 150 | function, | ||
| 151 | data, | ||
| 152 | global, | ||
| 153 | section, | ||
| 154 | event, | ||
| 155 | table, | ||
| 156 | }; | ||
| 217 | 157 | ||
| 218 | if (table_count != 0) { | 158 | const Pointee = union(enum) { |
| 219 | return diags.failParse(object.path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{ | 159 | function: Wasm.ObjectFunctionIndex, |
| 220 | object.imported_tables_count, | 160 | function_import: ScratchSpace.FuncImportIndex, |
| 221 | table_count, | 161 | data: Wasm.ObjectData.Index, |
| 222 | }); | 162 | data_import: void, |
| 223 | } | 163 | global: Wasm.ObjectGlobalIndex, |
| 164 | global_import: ScratchSpace.GlobalImportIndex, | ||
| 165 | section: Wasm.ObjectSectionIndex, | ||
| 166 | table: Wasm.ObjectTableIndex, | ||
| 167 | table_import: ScratchSpace.TableImportIndex, | ||
| 168 | }; | ||
| 169 | }; | ||
| 224 | 170 | ||
| 225 | // MVP object files cannot have any table definitions, only imports (for the indirect function table). | 171 | pub const ScratchSpace = struct { |
| 226 | if (object.tables.len > 0) { | 172 | func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty, |
| 227 | return diags.failParse(object.path, "unexpected table definition without representing table symbols.", .{}); | 173 | func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty, |
| 228 | } | 174 | func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty, |
| 175 | global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty, | ||
| 176 | table_imports: std.ArrayListUnmanaged(TableImport) = .empty, | ||
| 177 | symbol_table: std.ArrayListUnmanaged(Symbol) = .empty, | ||
| 178 | segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty, | ||
| 179 | exports: std.ArrayListUnmanaged(Export) = .empty, | ||
| 180 | |||
| 181 | const Export = struct { | ||
| 182 | name: Wasm.String, | ||
| 183 | pointee: Pointee, | ||
| 184 | |||
| 185 | const Pointee = union(std.wasm.ExternalKind) { | ||
| 186 | function: Wasm.ObjectFunctionIndex, | ||
| 187 | table: Wasm.ObjectTableIndex, | ||
| 188 | memory: Wasm.ObjectMemory.Index, | ||
| 189 | global: Wasm.ObjectGlobalIndex, | ||
| 190 | }; | ||
| 191 | }; | ||
| 229 | 192 | ||
| 230 | if (object.imported_tables_count != 1) { | 193 | /// Index into `func_imports`. |
| 231 | return diags.failParse(object.path, "found more than one table import, but no representing table symbols", .{}); | 194 | const FuncImportIndex = enum(u32) { |
| 232 | } | 195 | _, |
| 233 | 196 | ||
| 234 | const table_import: Wasm.Import = for (object.imports) |imp| { | 197 | fn ptr(index: FuncImportIndex, ss: *const ScratchSpace) *FunctionImport { |
| 235 | if (imp.kind == .table) { | 198 | return &ss.func_imports.items[@intFromEnum(index)]; |
| 236 | break imp; | ||
| 237 | } | 199 | } |
| 238 | } else unreachable; | 200 | }; |
| 239 | 201 | ||
| 240 | if (table_import.name != wasm.preloaded_strings.__indirect_function_table) { | 202 | /// Index into `global_imports`. |
| 241 | return diags.failParse(object.path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{ | 203 | const GlobalImportIndex = enum(u32) { |
| 242 | wasm.stringSlice(table_import.name), | 204 | _, |
| 243 | }); | ||
| 244 | } | ||
| 245 | 205 | ||
| 246 | var table_symbol: Symbol = .{ | 206 | fn ptr(index: GlobalImportIndex, ss: *const ScratchSpace) *GlobalImport { |
| 247 | .flags = 0, | 207 | return &ss.global_imports.items[@intFromEnum(index)]; |
| 248 | .name = table_import.name, | 208 | } |
| 249 | .tag = .table, | ||
| 250 | .index = 0, | ||
| 251 | .virtual_address = undefined, | ||
| 252 | }; | 209 | }; |
| 253 | table_symbol.setFlag(.WASM_SYM_UNDEFINED); | ||
| 254 | table_symbol.setFlag(.WASM_SYM_NO_STRIP); | ||
| 255 | return table_symbol; | ||
| 256 | } | ||
| 257 | 210 | ||
| 258 | const Parser = struct { | 211 | /// Index into `table_imports`. |
| 259 | reader: std.io.FixedBufferStream([]const u8), | 212 | const TableImportIndex = enum(u32) { |
| 260 | /// Object file we're building | 213 | _, |
| 261 | object: *Object, | 214 | |
| 262 | /// Mutable so that the string table can be modified. | 215 | fn ptr(index: TableImportIndex, ss: *const ScratchSpace) *TableImport { |
| 263 | wasm: *Wasm, | 216 | return &ss.table_imports.items[@intFromEnum(index)]; |
| 217 | } | ||
| 218 | }; | ||
| 264 | 219 | ||
| 265 | fn parseObject(parser: *Parser, gpa: Allocator) anyerror!void { | 220 | /// Index into `func_types`. |
| 266 | const wasm = parser.wasm; | 221 | const FuncTypeIndex = enum(u32) { |
| 222 | _, | ||
| 267 | 223 | ||
| 268 | { | 224 | fn ptr(index: FuncTypeIndex, ss: *const ScratchSpace) *Wasm.FunctionType.Index { |
| 269 | var magic_bytes: [4]u8 = undefined; | 225 | return &ss.func_types.items[@intFromEnum(index)]; |
| 270 | try parser.reader.reader().readNoEof(&magic_bytes); | ||
| 271 | if (!std.mem.eql(u8, &magic_bytes, &std.wasm.magic)) return error.BadObjectMagic; | ||
| 272 | } | 226 | } |
| 227 | }; | ||
| 273 | 228 | ||
| 274 | const version = try parser.reader.reader().readInt(u32, .little); | 229 | pub fn deinit(ss: *ScratchSpace, gpa: Allocator) void { |
| 275 | parser.object.version = version; | 230 | ss.exports.deinit(gpa); |
| 276 | 231 | ss.func_types.deinit(gpa); | |
| 277 | var saw_linking_section = false; | 232 | ss.func_type_indexes.deinit(gpa); |
| 278 | 233 | ss.func_imports.deinit(gpa); | |
| 279 | var section_index: u32 = 0; | 234 | ss.global_imports.deinit(gpa); |
| 280 | while (parser.reader.reader().readByte()) |byte| : (section_index += 1) { | 235 | ss.table_imports.deinit(gpa); |
| 281 | const len = try readLeb(u32, parser.reader.reader()); | 236 | ss.symbol_table.deinit(gpa); |
| 282 | var limited_reader = std.io.limitedReader(parser.reader.reader(), len); | 237 | ss.segment_info.deinit(gpa); |
| 283 | const reader = limited_reader.reader(); | 238 | ss.* = undefined; |
| 284 | switch (@as(std.wasm.Section, @enumFromInt(byte))) { | 239 | } |
| 285 | .custom => { | ||
| 286 | const name_len = try readLeb(u32, reader); | ||
| 287 | const name = try gpa.alloc(u8, name_len); | ||
| 288 | defer gpa.free(name); | ||
| 289 | try reader.readNoEof(name); | ||
| 290 | |||
| 291 | if (std.mem.eql(u8, name, "linking")) { | ||
| 292 | saw_linking_section = true; | ||
| 293 | try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left))); | ||
| 294 | } else if (std.mem.startsWith(u8, name, "reloc")) { | ||
| 295 | try parser.parseRelocations(gpa); | ||
| 296 | } else if (std.mem.eql(u8, name, "target_features")) { | ||
| 297 | try parser.parseFeatures(gpa); | ||
| 298 | } else if (std.mem.startsWith(u8, name, ".debug")) { | ||
| 299 | const gop = try parser.object.relocatable_data.getOrPut(gpa, .custom); | ||
| 300 | var relocatable_data: std.ArrayListUnmanaged(RelocatableData) = .empty; | ||
| 301 | defer relocatable_data.deinit(gpa); | ||
| 302 | if (!gop.found_existing) { | ||
| 303 | gop.value_ptr.* = &.{}; | ||
| 304 | } else { | ||
| 305 | relocatable_data = std.ArrayListUnmanaged(RelocatableData).fromOwnedSlice(gop.value_ptr.*); | ||
| 306 | } | ||
| 307 | const debug_size = @as(u32, @intCast(reader.context.bytes_left)); | ||
| 308 | const debug_content = try gpa.alloc(u8, debug_size); | ||
| 309 | errdefer gpa.free(debug_content); | ||
| 310 | try reader.readNoEof(debug_content); | ||
| 311 | |||
| 312 | try relocatable_data.append(gpa, .{ | ||
| 313 | .type = .custom, | ||
| 314 | .data = debug_content.ptr, | ||
| 315 | .size = debug_size, | ||
| 316 | .index = @intFromEnum(try wasm.internString(name)), | ||
| 317 | .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset | ||
| 318 | .section_index = section_index, | ||
| 319 | }); | ||
| 320 | gop.value_ptr.* = try relocatable_data.toOwnedSlice(gpa); | ||
| 321 | } else { | ||
| 322 | try reader.skipBytes(reader.context.bytes_left, .{}); | ||
| 323 | } | ||
| 324 | }, | ||
| 325 | .type => { | ||
| 326 | for (try readVec(&parser.object.func_types, reader, gpa)) |*type_val| { | ||
| 327 | if ((try reader.readByte()) != std.wasm.function_type) return error.ExpectedFuncType; | ||
| 328 | 240 | ||
| 329 | for (try readVec(&type_val.params, reader, gpa)) |*param| { | 241 | fn clear(ss: *ScratchSpace) void { |
| 330 | param.* = try readEnum(std.wasm.Valtype, reader); | 242 | ss.exports.clearRetainingCapacity(); |
| 331 | } | 243 | ss.func_types.clearRetainingCapacity(); |
| 244 | ss.func_type_indexes.clearRetainingCapacity(); | ||
| 245 | ss.func_imports.clearRetainingCapacity(); | ||
| 246 | ss.global_imports.clearRetainingCapacity(); | ||
| 247 | ss.table_imports.clearRetainingCapacity(); | ||
| 248 | ss.symbol_table.clearRetainingCapacity(); | ||
| 249 | ss.segment_info.clearRetainingCapacity(); | ||
| 250 | } | ||
| 251 | }; | ||
| 332 | 252 | ||
| 333 | for (try readVec(&type_val.returns, reader, gpa)) |*result| { | 253 | pub fn parse( |
| 334 | result.* = try readEnum(std.wasm.Valtype, reader); | 254 | wasm: *Wasm, |
| 255 | bytes: []const u8, | ||
| 256 | path: Path, | ||
| 257 | archive_member_name: ?[]const u8, | ||
| 258 | host_name: Wasm.OptionalString, | ||
| 259 | ss: *ScratchSpace, | ||
| 260 | must_link: bool, | ||
| 261 | gc_sections: bool, | ||
| 262 | ) anyerror!Object { | ||
| 263 | const comp = wasm.base.comp; | ||
| 264 | const gpa = comp.gpa; | ||
| 265 | const diags = &comp.link_diags; | ||
| 266 | |||
| 267 | var pos: usize = 0; | ||
| 268 | |||
| 269 | if (!std.mem.eql(u8, bytes[0..std.wasm.magic.len], &std.wasm.magic)) return error.BadObjectMagic; | ||
| 270 | pos += std.wasm.magic.len; | ||
| 271 | |||
| 272 | const version = std.mem.readInt(u32, bytes[pos..][0..4], .little); | ||
| 273 | pos += 4; | ||
| 274 | |||
| 275 | const data_segment_start: u32 = @intCast(wasm.object_data_segments.items.len); | ||
| 276 | const custom_segment_start: u32 = @intCast(wasm.object_custom_segments.entries.len); | ||
| 277 | const functions_start: u32 = @intCast(wasm.object_functions.items.len); | ||
| 278 | const tables_start: u32 = @intCast(wasm.object_tables.items.len); | ||
| 279 | const memories_start: u32 = @intCast(wasm.object_memories.items.len); | ||
| 280 | const globals_start: u32 = @intCast(wasm.object_globals.items.len); | ||
| 281 | const init_funcs_start: u32 = @intCast(wasm.object_init_funcs.items.len); | ||
| 282 | const comdats_start: u32 = @intCast(wasm.object_comdats.items.len); | ||
| 283 | const function_imports_start: u32 = @intCast(wasm.object_function_imports.entries.len); | ||
| 284 | const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len); | ||
| 285 | const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len); | ||
| 286 | const data_imports_start: u32 = @intCast(wasm.object_data_imports.entries.len); | ||
| 287 | const local_section_index_base = wasm.object_total_sections; | ||
| 288 | const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len); | ||
| 289 | const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm); | ||
| 290 | |||
| 291 | ss.clear(); | ||
| 292 | |||
| 293 | var start_function: Wasm.OptionalObjectFunctionIndex = .none; | ||
| 294 | var opt_features: ?Wasm.Feature.Set = null; | ||
| 295 | var saw_linking_section = false; | ||
| 296 | var has_tls = false; | ||
| 297 | var table_import_symbol_count: usize = 0; | ||
| 298 | var code_section_index: ?Wasm.ObjectSectionIndex = null; | ||
| 299 | var global_section_index: ?Wasm.ObjectSectionIndex = null; | ||
| 300 | var data_section_index: ?Wasm.ObjectSectionIndex = null; | ||
| 301 | while (pos < bytes.len) : (wasm.object_total_sections += 1) { | ||
| 302 | const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections); | ||
| 303 | |||
| 304 | const section_tag: std.wasm.Section = @enumFromInt(bytes[pos]); | ||
| 305 | pos += 1; | ||
| 306 | |||
| 307 | const len, pos = readLeb(u32, bytes, pos); | ||
| 308 | const section_end = pos + len; | ||
| 309 | switch (section_tag) { | ||
| 310 | .custom => { | ||
| 311 | const section_name, pos = readBytes(bytes, pos); | ||
| 312 | if (std.mem.eql(u8, section_name, "linking")) { | ||
| 313 | saw_linking_section = true; | ||
| 314 | const section_version, pos = readLeb(u32, bytes, pos); | ||
| 315 | log.debug("link meta data version: {d}", .{section_version}); | ||
| 316 | if (section_version != 2) return error.UnsupportedVersion; | ||
| 317 | while (pos < section_end) { | ||
| 318 | const sub_type, pos = readLeb(u8, bytes, pos); | ||
| 319 | log.debug("found subsection: {s}", .{@tagName(@as(SubsectionType, @enumFromInt(sub_type)))}); | ||
| 320 | const payload_len, pos = readLeb(u32, bytes, pos); | ||
| 321 | if (payload_len == 0) break; | ||
| 322 | |||
| 323 | const count, pos = readLeb(u32, bytes, pos); | ||
| 324 | |||
| 325 | switch (@as(SubsectionType, @enumFromInt(sub_type))) { | ||
| 326 | .segment_info => { | ||
| 327 | for (try ss.segment_info.addManyAsSlice(gpa, count)) |*segment| { | ||
| 328 | const name, pos = readBytes(bytes, pos); | ||
| 329 | const alignment, pos = readLeb(u32, bytes, pos); | ||
| 330 | const flags_u32, pos = readLeb(u32, bytes, pos); | ||
| 331 | const flags: SegmentInfo.Flags = @bitCast(flags_u32); | ||
| 332 | const tls = flags.tls or | ||
| 333 | // Supports legacy object files that specified | ||
| 334 | // being TLS by the name instead of the TLS flag. | ||
| 335 | std.mem.startsWith(u8, name, ".tdata") or | ||
| 336 | std.mem.startsWith(u8, name, ".tbss"); | ||
| 337 | has_tls = has_tls or tls; | ||
| 338 | segment.* = .{ | ||
| 339 | .name = try wasm.internString(name), | ||
| 340 | .flags = .{ | ||
| 341 | .strings = flags.strings, | ||
| 342 | .tls = tls, | ||
| 343 | .alignment = @enumFromInt(alignment), | ||
| 344 | .retain = flags.retain, | ||
| 345 | }, | ||
| 346 | }; | ||
| 347 | } | ||
| 348 | }, | ||
| 349 | .init_funcs => { | ||
| 350 | for (try wasm.object_init_funcs.addManyAsSlice(gpa, count)) |*func| { | ||
| 351 | const priority, pos = readLeb(u32, bytes, pos); | ||
| 352 | const symbol_index, pos = readLeb(u32, bytes, pos); | ||
| 353 | if (symbol_index > ss.symbol_table.items.len) | ||
| 354 | return diags.failParse(path, "init_funcs before symbol table", .{}); | ||
| 355 | const sym = &ss.symbol_table.items[symbol_index]; | ||
| 356 | if (sym.pointee != .function) { | ||
| 357 | return diags.failParse(path, "init_func symbol '{s}' not a function", .{ | ||
| 358 | sym.name.slice(wasm).?, | ||
| 359 | }); | ||
| 360 | } else if (sym.flags.undefined) { | ||
| 361 | return diags.failParse(path, "init_func symbol '{s}' is an import", .{ | ||
| 362 | sym.name.slice(wasm).?, | ||
| 363 | }); | ||
| 364 | } | ||
| 365 | func.* = .{ | ||
| 366 | .priority = priority, | ||
| 367 | .function_index = sym.pointee.function, | ||
| 368 | }; | ||
| 369 | } | ||
| 370 | }, | ||
| 371 | .comdat_info => { | ||
| 372 | for (try wasm.object_comdats.addManyAsSlice(gpa, count)) |*comdat| { | ||
| 373 | const name, pos = readBytes(bytes, pos); | ||
| 374 | const flags, pos = readLeb(u32, bytes, pos); | ||
| 375 | if (flags != 0) return error.UnexpectedComdatFlags; | ||
| 376 | const symbol_count, pos = readLeb(u32, bytes, pos); | ||
| 377 | const start_off: u32 = @intCast(wasm.object_comdat_symbols.len); | ||
| 378 | try wasm.object_comdat_symbols.ensureUnusedCapacity(gpa, symbol_count); | ||
| 379 | for (0..symbol_count) |_| { | ||
| 380 | const kind, pos = readEnum(Wasm.Comdat.Symbol.Type, bytes, pos); | ||
| 381 | const index, pos = readLeb(u32, bytes, pos); | ||
| 382 | if (true) @panic("TODO rebase index depending on kind"); | ||
| 383 | wasm.object_comdat_symbols.appendAssumeCapacity(.{ | ||
| 384 | .kind = kind, | ||
| 385 | .index = index, | ||
| 386 | }); | ||
| 387 | } | ||
| 388 | comdat.* = .{ | ||
| 389 | .name = try wasm.internString(name), | ||
| 390 | .flags = flags, | ||
| 391 | .symbols = .{ | ||
| 392 | .off = start_off, | ||
| 393 | .len = @intCast(wasm.object_comdat_symbols.len - start_off), | ||
| 394 | }, | ||
| 395 | }; | ||
| 396 | } | ||
| 397 | }, | ||
| 398 | .symbol_table => { | ||
| 399 | for (try ss.symbol_table.addManyAsSlice(gpa, count)) |*symbol| { | ||
| 400 | const tag, pos = readEnum(Symbol.Tag, bytes, pos); | ||
| 401 | const flags, pos = readLeb(u32, bytes, pos); | ||
| 402 | symbol.* = .{ | ||
| 403 | .flags = @bitCast(flags), | ||
| 404 | .name = .none, | ||
| 405 | .pointee = undefined, | ||
| 406 | }; | ||
| 407 | symbol.flags.initZigSpecific(must_link, gc_sections); | ||
| 408 | |||
| 409 | switch (tag) { | ||
| 410 | .data => { | ||
| 411 | const name, pos = readBytes(bytes, pos); | ||
| 412 | const interned_name = try wasm.internString(name); | ||
| 413 | symbol.name = interned_name.toOptional(); | ||
| 414 | if (symbol.flags.undefined) { | ||
| 415 | symbol.pointee = .data_import; | ||
| 416 | } else { | ||
| 417 | const segment_index, pos = readLeb(u32, bytes, pos); | ||
| 418 | const segment_offset, pos = readLeb(u32, bytes, pos); | ||
| 419 | const size, pos = readLeb(u32, bytes, pos); | ||
| 420 | try wasm.object_datas.append(gpa, .{ | ||
| 421 | .segment = @enumFromInt(data_segment_start + segment_index), | ||
| 422 | .offset = segment_offset, | ||
| 423 | .size = size, | ||
| 424 | .name = interned_name, | ||
| 425 | .flags = symbol.flags, | ||
| 426 | }); | ||
| 427 | symbol.pointee = .{ | ||
| 428 | .data = @enumFromInt(wasm.object_datas.items.len - 1), | ||
| 429 | }; | ||
| 430 | } | ||
| 431 | }, | ||
| 432 | .section => { | ||
| 433 | const local_section, pos = readLeb(u32, bytes, pos); | ||
| 434 | const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section); | ||
| 435 | symbol.pointee = .{ .section = section }; | ||
| 436 | }, | ||
| 437 | |||
| 438 | .function => { | ||
| 439 | const local_index, pos = readLeb(u32, bytes, pos); | ||
| 440 | if (symbol.flags.undefined) { | ||
| 441 | const function_import: ScratchSpace.FuncImportIndex = @enumFromInt(local_index); | ||
| 442 | symbol.pointee = .{ .function_import = function_import }; | ||
| 443 | if (symbol.flags.explicit_name) { | ||
| 444 | const name, pos = readBytes(bytes, pos); | ||
| 445 | symbol.name = (try wasm.internString(name)).toOptional(); | ||
| 446 | } else { | ||
| 447 | symbol.name = function_import.ptr(ss).name.toOptional(); | ||
| 448 | } | ||
| 449 | } else { | ||
| 450 | symbol.pointee = .{ .function = @enumFromInt(functions_start + (local_index - ss.func_imports.items.len)) }; | ||
| 451 | const name, pos = readBytes(bytes, pos); | ||
| 452 | symbol.name = (try wasm.internString(name)).toOptional(); | ||
| 453 | } | ||
| 454 | }, | ||
| 455 | .global => { | ||
| 456 | const local_index, pos = readLeb(u32, bytes, pos); | ||
| 457 | if (symbol.flags.undefined) { | ||
| 458 | const global_import: ScratchSpace.GlobalImportIndex = @enumFromInt(local_index); | ||
| 459 | symbol.pointee = .{ .global_import = global_import }; | ||
| 460 | if (symbol.flags.explicit_name) { | ||
| 461 | const name, pos = readBytes(bytes, pos); | ||
| 462 | symbol.name = (try wasm.internString(name)).toOptional(); | ||
| 463 | } else { | ||
| 464 | symbol.name = global_import.ptr(ss).name.toOptional(); | ||
| 465 | } | ||
| 466 | } else { | ||
| 467 | symbol.pointee = .{ .global = @enumFromInt(globals_start + (local_index - ss.global_imports.items.len)) }; | ||
| 468 | const name, pos = readBytes(bytes, pos); | ||
| 469 | symbol.name = (try wasm.internString(name)).toOptional(); | ||
| 470 | } | ||
| 471 | }, | ||
| 472 | .table => { | ||
| 473 | const local_index, pos = readLeb(u32, bytes, pos); | ||
| 474 | if (symbol.flags.undefined) { | ||
| 475 | table_import_symbol_count += 1; | ||
| 476 | const table_import: ScratchSpace.TableImportIndex = @enumFromInt(local_index); | ||
| 477 | symbol.pointee = .{ .table_import = table_import }; | ||
| 478 | if (symbol.flags.explicit_name) { | ||
| 479 | const name, pos = readBytes(bytes, pos); | ||
| 480 | symbol.name = (try wasm.internString(name)).toOptional(); | ||
| 481 | } else { | ||
| 482 | symbol.name = table_import.ptr(ss).name.toOptional(); | ||
| 483 | } | ||
| 484 | } else { | ||
| 485 | symbol.pointee = .{ .table = @enumFromInt(tables_start + (local_index - ss.table_imports.items.len)) }; | ||
| 486 | const name, pos = readBytes(bytes, pos); | ||
| 487 | symbol.name = (try wasm.internString(name)).toOptional(); | ||
| 488 | } | ||
| 489 | }, | ||
| 490 | else => { | ||
| 491 | log.debug("unrecognized symbol type tag: {x}", .{@intFromEnum(tag)}); | ||
| 492 | return error.UnrecognizedSymbolType; | ||
| 493 | }, | ||
| 494 | } | ||
| 495 | } | ||
| 496 | }, | ||
| 335 | } | 497 | } |
| 336 | } | 498 | } |
| 337 | try assertEnd(reader); | 499 | } else if (std.mem.startsWith(u8, section_name, "reloc.")) { |
| 338 | }, | 500 | // 'The "reloc." custom sections must come after the "linking" custom section' |
| 339 | .import => { | 501 | if (!saw_linking_section) return error.RelocBeforeLinkingSection; |
| 340 | for (try readVec(&parser.object.imports, reader, gpa)) |*import| { | 502 | |
| 341 | const module_len = try readLeb(u32, reader); | 503 | // "Relocation sections start with an identifier specifying |
| 342 | const module_name = try gpa.alloc(u8, module_len); | 504 | // which section they apply to, and must be sequenced in |
| 343 | defer gpa.free(module_name); | 505 | // the module after that section." |
| 344 | try reader.readNoEof(module_name); | 506 | // "Relocation sections can only target code, data and custom sections." |
| 345 | 507 | const local_section, pos = readLeb(u32, bytes, pos); | |
| 346 | const name_len = try readLeb(u32, reader); | 508 | const count, pos = readLeb(u32, bytes, pos); |
| 347 | const name = try gpa.alloc(u8, name_len); | 509 | const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section); |
| 348 | defer gpa.free(name); | 510 | |
| 349 | try reader.readNoEof(name); | 511 | log.debug("found {d} relocations for section={d}", .{ count, section }); |
| 350 | 512 | ||
| 351 | const kind = try readEnum(std.wasm.ExternalKind, reader); | 513 | var prev_offset: u32 = 0; |
| 352 | const kind_value: std.wasm.Import.Kind = switch (kind) { | 514 | try wasm.object_relocations.ensureUnusedCapacity(gpa, count); |
| 353 | .function => val: { | 515 | for (0..count) |_| { |
| 354 | parser.object.imported_functions_count += 1; | 516 | const tag: RelocationType = @enumFromInt(bytes[pos]); |
| 355 | break :val .{ .function = try readLeb(u32, reader) }; | 517 | pos += 1; |
| 518 | const offset, pos = readLeb(u32, bytes, pos); | ||
| 519 | const index, pos = readLeb(u32, bytes, pos); | ||
| 520 | |||
| 521 | if (offset < prev_offset) | ||
| 522 | return diags.failParse(path, "relocation entries not sorted by offset", .{}); | ||
| 523 | prev_offset = offset; | ||
| 524 | |||
| 525 | const sym = &ss.symbol_table.items[index]; | ||
| 526 | |||
| 527 | switch (tag) { | ||
| 528 | .memory_addr_leb, | ||
| 529 | .memory_addr_sleb, | ||
| 530 | .memory_addr_i32, | ||
| 531 | .memory_addr_rel_sleb, | ||
| 532 | .memory_addr_leb64, | ||
| 533 | .memory_addr_sleb64, | ||
| 534 | .memory_addr_i64, | ||
| 535 | .memory_addr_rel_sleb64, | ||
| 536 | .memory_addr_tls_sleb, | ||
| 537 | .memory_addr_locrel_i32, | ||
| 538 | .memory_addr_tls_sleb64, | ||
| 539 | => { | ||
| 540 | const addend: i32, pos = readLeb(i32, bytes, pos); | ||
| 541 | wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { | ||
| 542 | .data => |data| .{ | ||
| 543 | .tag = .fromType(tag), | ||
| 544 | .offset = offset, | ||
| 545 | .pointee = .{ .data = data }, | ||
| 546 | .addend = addend, | ||
| 547 | }, | ||
| 548 | .data_import => .{ | ||
| 549 | .tag = .fromTypeImport(tag), | ||
| 550 | .offset = offset, | ||
| 551 | .pointee = .{ .symbol_name = sym.name.unwrap().? }, | ||
| 552 | .addend = addend, | ||
| 553 | }, | ||
| 554 | else => unreachable, | ||
| 555 | }); | ||
| 556 | }, | ||
| 557 | .function_offset_i32, .function_offset_i64 => { | ||
| 558 | const addend: i32, pos = readLeb(i32, bytes, pos); | ||
| 559 | wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { | ||
| 560 | .function => .{ | ||
| 561 | .tag = .fromType(tag), | ||
| 562 | .offset = offset, | ||
| 563 | .pointee = .{ .function = sym.pointee.function }, | ||
| 564 | .addend = addend, | ||
| 565 | }, | ||
| 566 | .function_import => .{ | ||
| 567 | .tag = .fromTypeImport(tag), | ||
| 568 | .offset = offset, | ||
| 569 | .pointee = .{ .symbol_name = sym.name.unwrap().? }, | ||
| 570 | .addend = addend, | ||
| 571 | }, | ||
| 572 | else => unreachable, | ||
| 573 | }); | ||
| 356 | }, | 574 | }, |
| 357 | .memory => .{ .memory = try readLimits(reader) }, | 575 | .section_offset_i32 => { |
| 358 | .global => val: { | 576 | const addend: i32, pos = readLeb(i32, bytes, pos); |
| 359 | parser.object.imported_globals_count += 1; | 577 | wasm.object_relocations.appendAssumeCapacity(.{ |
| 360 | break :val .{ .global = .{ | 578 | .tag = .section_offset_i32, |
| 361 | .valtype = try readEnum(std.wasm.Valtype, reader), | 579 | .offset = offset, |
| 362 | .mutable = (try reader.readByte()) == 0x01, | 580 | .pointee = .{ .section = sym.pointee.section }, |
| 363 | } }; | 581 | .addend = addend, |
| 582 | }); | ||
| 364 | }, | 583 | }, |
| 365 | .table => val: { | 584 | .type_index_leb => { |
| 366 | parser.object.imported_tables_count += 1; | 585 | wasm.object_relocations.appendAssumeCapacity(.{ |
| 367 | break :val .{ .table = .{ | 586 | .tag = .type_index_leb, |
| 368 | .reftype = try readEnum(std.wasm.RefType, reader), | 587 | .offset = offset, |
| 369 | .limits = try readLimits(reader), | 588 | .pointee = .{ .type_index = ss.func_types.items[index] }, |
| 370 | } }; | 589 | .addend = undefined, |
| 590 | }); | ||
| 591 | }, | ||
| 592 | .function_index_leb, | ||
| 593 | .function_index_i32, | ||
| 594 | .table_index_sleb, | ||
| 595 | .table_index_i32, | ||
| 596 | .table_index_sleb64, | ||
| 597 | .table_index_i64, | ||
| 598 | .table_index_rel_sleb, | ||
| 599 | .table_index_rel_sleb64, | ||
| 600 | => { | ||
| 601 | wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { | ||
| 602 | .function => .{ | ||
| 603 | .tag = .fromType(tag), | ||
| 604 | .offset = offset, | ||
| 605 | .pointee = .{ .function = sym.pointee.function }, | ||
| 606 | .addend = undefined, | ||
| 607 | }, | ||
| 608 | .function_import => .{ | ||
| 609 | .tag = .fromTypeImport(tag), | ||
| 610 | .offset = offset, | ||
| 611 | .pointee = .{ .symbol_name = sym.name.unwrap().? }, | ||
| 612 | .addend = undefined, | ||
| 613 | }, | ||
| 614 | else => unreachable, | ||
| 615 | }); | ||
| 616 | }, | ||
| 617 | .global_index_leb, .global_index_i32 => { | ||
| 618 | wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { | ||
| 619 | .global => .{ | ||
| 620 | .tag = .fromType(tag), | ||
| 621 | .offset = offset, | ||
| 622 | .pointee = .{ .global = sym.pointee.global }, | ||
| 623 | .addend = undefined, | ||
| 624 | }, | ||
| 625 | .global_import => .{ | ||
| 626 | .tag = .fromTypeImport(tag), | ||
| 627 | .offset = offset, | ||
| 628 | .pointee = .{ .symbol_name = sym.name.unwrap().? }, | ||
| 629 | .addend = undefined, | ||
| 630 | }, | ||
| 631 | else => unreachable, | ||
| 632 | }); | ||
| 371 | }, | 633 | }, |
| 372 | }; | ||
| 373 | 634 | ||
| 374 | import.* = .{ | 635 | .table_number_leb => { |
| 375 | .module_name = try wasm.internString(module_name), | 636 | wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { |
| 376 | .name = try wasm.internString(name), | 637 | .table => .{ |
| 377 | .kind = kind_value, | 638 | .tag = .fromType(tag), |
| 378 | }; | 639 | .offset = offset, |
| 379 | } | 640 | .pointee = .{ .table = sym.pointee.table }, |
| 380 | try assertEnd(reader); | 641 | .addend = undefined, |
| 381 | }, | 642 | }, |
| 382 | .function => { | 643 | .table_import => .{ |
| 383 | for (try readVec(&parser.object.functions, reader, gpa)) |*func| { | 644 | .tag = .fromTypeImport(tag), |
| 384 | func.* = .{ .type_index = try readLeb(u32, reader) }; | 645 | .offset = offset, |
| 385 | } | 646 | .pointee = .{ .symbol_name = sym.name.unwrap().? }, |
| 386 | try assertEnd(reader); | 647 | .addend = undefined, |
| 387 | }, | 648 | }, |
| 388 | .table => { | 649 | else => unreachable, |
| 389 | for (try readVec(&parser.object.tables, reader, gpa)) |*table| { | 650 | }); |
| 390 | table.* = .{ | 651 | }, |
| 391 | .reftype = try readEnum(std.wasm.RefType, reader), | 652 | .event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}), |
| 392 | .limits = try readLimits(reader), | 653 | } |
| 393 | }; | ||
| 394 | } | 654 | } |
| 395 | try assertEnd(reader); | 655 | |
| 396 | }, | 656 | try wasm.object_relocations_table.putNoClobber(gpa, section, .{ |
| 397 | .memory => { | 657 | .off = @intCast(wasm.object_relocations.len - count), |
| 398 | for (try readVec(&parser.object.memories, reader, gpa)) |*memory| { | 658 | .len = count, |
| 399 | memory.* = .{ .limits = try readLimits(reader) }; | 659 | }); |
| 660 | } else if (std.mem.eql(u8, section_name, "target_features")) { | ||
| 661 | opt_features, pos = try parseFeatures(wasm, bytes, pos, path); | ||
| 662 | } else if (std.mem.startsWith(u8, section_name, ".debug")) { | ||
| 663 | const debug_content = bytes[pos..section_end]; | ||
| 664 | pos = section_end; | ||
| 665 | |||
| 666 | const data_off: u32 = @intCast(wasm.string_bytes.items.len); | ||
| 667 | try wasm.string_bytes.appendSlice(gpa, debug_content); | ||
| 668 | |||
| 669 | try wasm.object_custom_segments.put(gpa, section_index, .{ | ||
| 670 | .payload = .{ | ||
| 671 | .off = @enumFromInt(data_off), | ||
| 672 | .len = @intCast(debug_content.len), | ||
| 673 | }, | ||
| 674 | .flags = .{}, | ||
| 675 | .section_name = try wasm.internString(section_name), | ||
| 676 | }); | ||
| 677 | } else { | ||
| 678 | pos = section_end; | ||
| 679 | } | ||
| 680 | }, | ||
| 681 | .type => { | ||
| 682 | const func_types_len, pos = readLeb(u32, bytes, pos); | ||
| 683 | for (try ss.func_types.addManyAsSlice(gpa, func_types_len)) |*func_type| { | ||
| 684 | if (bytes[pos] != std.wasm.function_type) return error.ExpectedFuncType; | ||
| 685 | pos += 1; | ||
| 686 | |||
| 687 | const params, pos = readBytes(bytes, pos); | ||
| 688 | const returns, pos = readBytes(bytes, pos); | ||
| 689 | func_type.* = try wasm.addFuncType(.{ | ||
| 690 | .params = .fromString(try wasm.internString(params)), | ||
| 691 | .returns = .fromString(try wasm.internString(returns)), | ||
| 692 | }); | ||
| 693 | } | ||
| 694 | }, | ||
| 695 | .import => { | ||
| 696 | const imports_len, pos = readLeb(u32, bytes, pos); | ||
| 697 | for (0..imports_len) |_| { | ||
| 698 | const module_name, pos = readBytes(bytes, pos); | ||
| 699 | const name, pos = readBytes(bytes, pos); | ||
| 700 | const kind, pos = readEnum(std.wasm.ExternalKind, bytes, pos); | ||
| 701 | const interned_module_name = try wasm.internString(module_name); | ||
| 702 | const interned_name = try wasm.internString(name); | ||
| 703 | switch (kind) { | ||
| 704 | .function => { | ||
| 705 | const function, pos = readLeb(u32, bytes, pos); | ||
| 706 | try ss.func_imports.append(gpa, .{ | ||
| 707 | .module_name = interned_module_name, | ||
| 708 | .name = interned_name, | ||
| 709 | .function_index = @enumFromInt(function), | ||
| 710 | }); | ||
| 711 | }, | ||
| 712 | .memory => { | ||
| 713 | const limits, pos = readLimits(bytes, pos); | ||
| 714 | const gop = try wasm.object_memory_imports.getOrPut(gpa, interned_name); | ||
| 715 | if (gop.found_existing) { | ||
| 716 | if (gop.value_ptr.module_name != interned_module_name) { | ||
| 717 | var err = try diags.addErrorWithNotes(2); | ||
| 718 | try err.addMsg("memory '{s}' mismatching module names", .{name}); | ||
| 719 | gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{ | ||
| 720 | gop.value_ptr.module_name.slice(wasm), | ||
| 721 | }); | ||
| 722 | source_location.addNote(&err, "module '{s}' here", .{module_name}); | ||
| 723 | } | ||
| 724 | // TODO error for mismatching flags | ||
| 725 | gop.value_ptr.limits_min = @min(gop.value_ptr.limits_min, limits.min); | ||
| 726 | gop.value_ptr.limits_max = @max(gop.value_ptr.limits_max, limits.max); | ||
| 727 | } else { | ||
| 728 | gop.value_ptr.* = .{ | ||
| 729 | .module_name = interned_module_name, | ||
| 730 | .limits_min = limits.min, | ||
| 731 | .limits_max = limits.max, | ||
| 732 | .limits_has_max = limits.flags.has_max, | ||
| 733 | .limits_is_shared = limits.flags.is_shared, | ||
| 734 | .source_location = source_location, | ||
| 735 | }; | ||
| 736 | } | ||
| 737 | }, | ||
| 738 | .global => { | ||
| 739 | const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos); | ||
| 740 | const mutable = bytes[pos] == 0x01; | ||
| 741 | pos += 1; | ||
| 742 | try ss.global_imports.append(gpa, .{ | ||
| 743 | .name = interned_name, | ||
| 744 | .valtype = valtype, | ||
| 745 | .mutable = mutable, | ||
| 746 | .module_name = interned_module_name, | ||
| 747 | }); | ||
| 748 | }, | ||
| 749 | .table => { | ||
| 750 | const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos); | ||
| 751 | const limits, pos = readLimits(bytes, pos); | ||
| 752 | try ss.table_imports.append(gpa, .{ | ||
| 753 | .name = interned_name, | ||
| 754 | .module_name = interned_module_name, | ||
| 755 | .limits_min = limits.min, | ||
| 756 | .limits_max = limits.max, | ||
| 757 | .limits_has_max = limits.flags.has_max, | ||
| 758 | .limits_is_shared = limits.flags.is_shared, | ||
| 759 | .ref_type = ref_type, | ||
| 760 | }); | ||
| 761 | }, | ||
| 400 | } | 762 | } |
| 401 | try assertEnd(reader); | 763 | } |
| 402 | }, | 764 | }, |
| 403 | .global => { | 765 | .function => { |
| 404 | for (try readVec(&parser.object.globals, reader, gpa)) |*global| { | 766 | const functions_len, pos = readLeb(u32, bytes, pos); |
| 405 | global.* = .{ | 767 | for (try ss.func_type_indexes.addManyAsSlice(gpa, functions_len)) |*func_type_index| { |
| 768 | const i, pos = readLeb(u32, bytes, pos); | ||
| 769 | func_type_index.* = @enumFromInt(i); | ||
| 770 | } | ||
| 771 | }, | ||
| 772 | .table => { | ||
| 773 | const tables_len, pos = readLeb(u32, bytes, pos); | ||
| 774 | for (try wasm.object_tables.addManyAsSlice(gpa, tables_len)) |*table| { | ||
| 775 | const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos); | ||
| 776 | const limits, pos = readLimits(bytes, pos); | ||
| 777 | table.* = .{ | ||
| 778 | .name = .none, | ||
| 779 | .module_name = .none, | ||
| 780 | .flags = .{ | ||
| 781 | .ref_type = .from(ref_type), | ||
| 782 | .limits_has_max = limits.flags.has_max, | ||
| 783 | .limits_is_shared = limits.flags.is_shared, | ||
| 784 | }, | ||
| 785 | .limits_min = limits.min, | ||
| 786 | .limits_max = limits.max, | ||
| 787 | }; | ||
| 788 | } | ||
| 789 | }, | ||
| 790 | .memory => { | ||
| 791 | const memories_len, pos = readLeb(u32, bytes, pos); | ||
| 792 | for (try wasm.object_memories.addManyAsSlice(gpa, memories_len)) |*memory| { | ||
| 793 | const limits, pos = readLimits(bytes, pos); | ||
| 794 | memory.* = .{ | ||
| 795 | .name = .none, | ||
| 796 | .flags = .{ | ||
| 797 | .limits_has_max = limits.flags.has_max, | ||
| 798 | .limits_is_shared = limits.flags.is_shared, | ||
| 799 | }, | ||
| 800 | .limits_min = limits.min, | ||
| 801 | .limits_max = limits.max, | ||
| 802 | }; | ||
| 803 | } | ||
| 804 | }, | ||
| 805 | .global => { | ||
| 806 | if (global_section_index != null) | ||
| 807 | return diags.failParse(path, "object has more than one global section", .{}); | ||
| 808 | global_section_index = section_index; | ||
| 809 | |||
| 810 | const section_start = pos; | ||
| 811 | const globals_len, pos = readLeb(u32, bytes, pos); | ||
| 812 | for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| { | ||
| 813 | const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos); | ||
| 814 | const mutable = bytes[pos] == 0x01; | ||
| 815 | pos += 1; | ||
| 816 | const init_start = pos; | ||
| 817 | const expr, pos = try readInit(wasm, bytes, pos); | ||
| 818 | global.* = .{ | ||
| 819 | .name = .none, | ||
| 820 | .flags = .{ | ||
| 406 | .global_type = .{ | 821 | .global_type = .{ |
| 407 | .valtype = try readEnum(std.wasm.Valtype, reader), | 822 | .valtype = .from(valtype), |
| 408 | .mutable = (try reader.readByte()) == 0x01, | 823 | .mutable = mutable, |
| 409 | }, | 824 | }, |
| 410 | .init = try readInit(reader), | 825 | }, |
| 411 | }; | 826 | .expr = expr, |
| 412 | } | 827 | .object_index = object_index, |
| 413 | try assertEnd(reader); | 828 | .offset = @intCast(init_start - section_start), |
| 414 | }, | 829 | .size = @intCast(pos - init_start), |
| 415 | .@"export" => { | 830 | }; |
| 416 | for (try readVec(&parser.object.exports, reader, gpa)) |*exp| { | 831 | } |
| 417 | const name_len = try readLeb(u32, reader); | 832 | }, |
| 418 | const name = try gpa.alloc(u8, name_len); | 833 | .@"export" => { |
| 419 | defer gpa.free(name); | 834 | const exports_len, pos = readLeb(u32, bytes, pos); |
| 420 | try reader.readNoEof(name); | 835 | // Read into scratch space, and then later add this data as if |
| 421 | exp.* = .{ | 836 | // it were extra symbol table entries, but allow merging with |
| 422 | .name = try wasm.internString(name), | 837 | // existing symbol table data if the name matches. |
| 423 | .kind = try readEnum(std.wasm.ExternalKind, reader), | 838 | for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| { |
| 424 | .index = try readLeb(u32, reader), | 839 | const name, pos = readBytes(bytes, pos); |
| 425 | }; | 840 | const kind: std.wasm.ExternalKind = @enumFromInt(bytes[pos]); |
| 841 | pos += 1; | ||
| 842 | const index, pos = readLeb(u32, bytes, pos); | ||
| 843 | exp.* = .{ | ||
| 844 | .name = try wasm.internString(name), | ||
| 845 | .pointee = switch (kind) { | ||
| 846 | .function => .{ .function = @enumFromInt(functions_start + (index - ss.func_imports.items.len)) }, | ||
| 847 | .table => .{ .table = @enumFromInt(tables_start + (index - ss.table_imports.items.len)) }, | ||
| 848 | .memory => .{ .memory = @enumFromInt(memories_start + index) }, | ||
| 849 | .global => .{ .global = @enumFromInt(globals_start + (index - ss.global_imports.items.len)) }, | ||
| 850 | }, | ||
| 851 | }; | ||
| 852 | } | ||
| 853 | }, | ||
| 854 | .start => { | ||
| 855 | const index, pos = readLeb(u32, bytes, pos); | ||
| 856 | start_function = @enumFromInt(functions_start + index); | ||
| 857 | }, | ||
| 858 | .element => { | ||
| 859 | log.warn("unimplemented: element section in {} {?s}", .{ path, archive_member_name }); | ||
| 860 | pos = section_end; | ||
| 861 | }, | ||
| 862 | .code => { | ||
| 863 | if (code_section_index != null) | ||
| 864 | return diags.failParse(path, "object has more than one code section", .{}); | ||
| 865 | code_section_index = section_index; | ||
| 866 | |||
| 867 | const start = pos; | ||
| 868 | const count, pos = readLeb(u32, bytes, pos); | ||
| 869 | for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| { | ||
| 870 | const code_len, pos = readLeb(u32, bytes, pos); | ||
| 871 | const offset: u32 = @intCast(pos - start); | ||
| 872 | const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..code_len]); | ||
| 873 | pos += code_len; | ||
| 874 | elem.* = .{ | ||
| 875 | .flags = .{}, // populated from symbol table | ||
| 876 | .name = .none, // populated from symbol table | ||
| 877 | .type_index = undefined, // populated from func_types | ||
| 878 | .code = payload, | ||
| 879 | .offset = offset, | ||
| 880 | .object_index = object_index, | ||
| 881 | }; | ||
| 882 | } | ||
| 883 | }, | ||
| 884 | .data => { | ||
| 885 | if (data_section_index != null) | ||
| 886 | return diags.failParse(path, "object has more than one data section", .{}); | ||
| 887 | data_section_index = section_index; | ||
| 888 | |||
| 889 | const section_start = pos; | ||
| 890 | const count, pos = readLeb(u32, bytes, pos); | ||
| 891 | for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| { | ||
| 892 | const flags, pos = readEnum(DataSegmentFlags, bytes, pos); | ||
| 893 | if (flags == .active_memidx) { | ||
| 894 | const memidx, pos = readLeb(u32, bytes, pos); | ||
| 895 | if (memidx != 0) return diags.failParse(path, "data section uses mem index {d}", .{memidx}); | ||
| 426 | } | 896 | } |
| 427 | try assertEnd(reader); | 897 | //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos }; |
| 428 | }, | 898 | if (flags != .passive) pos = try skipInit(bytes, pos); |
| 429 | .start => { | 899 | const data_len, pos = readLeb(u32, bytes, pos); |
| 430 | parser.object.start = try readLeb(u32, reader); | 900 | const segment_start = pos; |
| 431 | try assertEnd(reader); | 901 | const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]); |
| 432 | }, | 902 | pos += data_len; |
| 433 | .element => { | 903 | elem.* = .{ |
| 434 | for (try readVec(&parser.object.elements, reader, gpa)) |*elem| { | 904 | .payload = payload, |
| 435 | elem.table_index = try readLeb(u32, reader); | 905 | .name = .none, // Populated from segment_info |
| 436 | elem.offset = try readInit(reader); | 906 | .flags = .{ |
| 907 | .is_passive = flags == .passive, | ||
| 908 | }, // Remainder populated from segment_info | ||
| 909 | .offset = @intCast(segment_start - section_start), | ||
| 910 | .object_index = object_index, | ||
| 911 | }; | ||
| 912 | } | ||
| 913 | }, | ||
| 914 | else => pos = section_end, | ||
| 915 | } | ||
| 916 | if (pos != section_end) return error.MalformedSection; | ||
| 917 | } | ||
| 918 | if (!saw_linking_section) return error.MissingLinkingSection; | ||
| 437 | 919 | ||
| 438 | for (try readVec(&elem.func_indexes, reader, gpa)) |*idx| { | 920 | const target_features = comp.root_mod.resolved_target.result.cpu.features; |
| 439 | idx.* = try readLeb(u32, reader); | 921 | |
| 440 | } | 922 | if (has_tls) { |
| 441 | } | 923 | if (!std.Target.wasm.featureSetHas(target_features, .atomics)) |
| 442 | try assertEnd(reader); | 924 | return diags.failParse(path, "object has TLS segment but target CPU feature atomics is disabled", .{}); |
| 925 | if (!std.Target.wasm.featureSetHas(target_features, .bulk_memory)) | ||
| 926 | return diags.failParse(path, "object has TLS segment but target CPU feature bulk_memory is disabled", .{}); | ||
| 927 | } | ||
| 928 | |||
| 929 | const features = opt_features orelse return error.MissingFeatures; | ||
| 930 | for (features.slice(wasm)) |feat| { | ||
| 931 | log.debug("feature: {s}{s}", .{ @tagName(feat.prefix), @tagName(feat.tag) }); | ||
| 932 | switch (feat.prefix) { | ||
| 933 | .invalid => unreachable, | ||
| 934 | .@"-" => switch (feat.tag) { | ||
| 935 | .@"shared-mem" => if (comp.config.shared_memory) { | ||
| 936 | return diags.failParse(path, "object forbids shared-mem but compilation enables it", .{}); | ||
| 443 | }, | 937 | }, |
| 444 | .code => { | 938 | else => { |
| 445 | const start = reader.context.bytes_left; | 939 | const f = feat.tag.toCpuFeature().?; |
| 446 | var index: u32 = 0; | 940 | if (std.Target.wasm.featureSetHas(target_features, f)) { |
| 447 | const count = try readLeb(u32, reader); | 941 | return diags.failParse( |
| 448 | const imported_function_count = parser.object.imported_functions_count; | 942 | path, |
| 449 | var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); | 943 | "object forbids {s} but specified target features include {s}", |
| 450 | defer relocatable_data.deinit(); | 944 | .{ @tagName(feat.tag), @tagName(f) }, |
| 451 | while (index < count) : (index += 1) { | 945 | ); |
| 452 | const code_len = try readLeb(u32, reader); | ||
| 453 | const offset = @as(u32, @intCast(start - reader.context.bytes_left)); | ||
| 454 | const data = try gpa.alloc(u8, code_len); | ||
| 455 | errdefer gpa.free(data); | ||
| 456 | try reader.readNoEof(data); | ||
| 457 | relocatable_data.appendAssumeCapacity(.{ | ||
| 458 | .type = .code, | ||
| 459 | .data = data.ptr, | ||
| 460 | .size = code_len, | ||
| 461 | .index = imported_function_count + index, | ||
| 462 | .offset = offset, | ||
| 463 | .section_index = section_index, | ||
| 464 | }); | ||
| 465 | } | 946 | } |
| 466 | try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice()); | ||
| 467 | }, | 947 | }, |
| 468 | .data => { | 948 | }, |
| 469 | const start = reader.context.bytes_left; | 949 | .@"+", .@"=" => switch (feat.tag) { |
| 470 | var index: u32 = 0; | 950 | .@"shared-mem" => if (!comp.config.shared_memory) { |
| 471 | const count = try readLeb(u32, reader); | 951 | return diags.failParse(path, "object requires shared-mem but compilation disables it", .{}); |
| 472 | var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); | 952 | }, |
| 473 | defer relocatable_data.deinit(); | 953 | else => { |
| 474 | while (index < count) : (index += 1) { | 954 | const f = feat.tag.toCpuFeature().?; |
| 475 | const flags = try readLeb(u32, reader); | 955 | if (!std.Target.wasm.featureSetHas(target_features, f)) { |
| 476 | const data_offset = try readInit(reader); | 956 | return diags.failParse( |
| 477 | _ = flags; // TODO: Do we need to check flags to detect passive/active memory? | 957 | path, |
| 478 | _ = data_offset; | 958 | "object requires {s} but specified target features exclude {s}", |
| 479 | const data_len = try readLeb(u32, reader); | 959 | .{ @tagName(feat.tag), @tagName(f) }, |
| 480 | const offset = @as(u32, @intCast(start - reader.context.bytes_left)); | 960 | ); |
| 481 | const data = try gpa.alloc(u8, data_len); | ||
| 482 | errdefer gpa.free(data); | ||
| 483 | try reader.readNoEof(data); | ||
| 484 | relocatable_data.appendAssumeCapacity(.{ | ||
| 485 | .type = .data, | ||
| 486 | .data = data.ptr, | ||
| 487 | .size = data_len, | ||
| 488 | .index = index, | ||
| 489 | .offset = offset, | ||
| 490 | .section_index = section_index, | ||
| 491 | }); | ||
| 492 | } | 961 | } |
| 493 | try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice()); | ||
| 494 | }, | 962 | }, |
| 495 | else => try parser.reader.reader().skipBytes(len, .{}), | 963 | }, |
| 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 | }); | ||
| 542 | |||
| 543 | for (relocations) |*relocation| { | ||
| 544 | const rel_type = try reader.readByte(); | ||
| 545 | const rel_type_enum = std.meta.intToEnum(Wasm.Relocation.RelocationType, rel_type) catch return error.MalformedSection; | ||
| 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 | } | 964 | } |
| 559 | |||
| 560 | try parser.object.relocations.putNoClobber(gpa, section, relocations); | ||
| 561 | } | 965 | } |
| 562 | 966 | ||
| 563 | /// Parses the "linking" custom section. Versions that are not | 967 | // Apply function type information. |
| 564 | /// supported will be an error. `payload_size` is required to be able | 968 | for (ss.func_type_indexes.items, wasm.object_functions.items[functions_start..]) |func_type, *func| { |
| 565 | /// to calculate the subsections we need to parse, as that data is not | 969 | func.type_index = func_type.ptr(ss).*; |
| 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 | } | 970 | } |
| 579 | 971 | ||
| 580 | /// Parses a `spec.Subsection`. | 972 | // Apply symbol table information. |
| 581 | /// The `reader` param for this is to provide a `LimitedReader`, which allows | 973 | for (ss.symbol_table.items) |symbol| switch (symbol.pointee) { |
| 582 | /// us to only read until a max length. | 974 | .function_import => |index| { |
| 583 | /// | 975 | const ptr = index.ptr(ss); |
| 584 | /// `parser` is used to provide access to other sections that may be needed, | 976 | const name = symbol.name.unwrap() orelse ptr.name; |
| 585 | /// such as access to the `import` section to find the name of a symbol. | 977 | if (symbol.flags.binding == .local) { |
| 586 | fn parseSubsection(parser: *Parser, gpa: Allocator, reader: anytype) !void { | 978 | diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); |
| 587 | const wasm = parser.wasm; | 979 | continue; |
| 588 | const sub_type = try leb.readUleb128(u8, reader); | 980 | } |
| 589 | log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))}); | 981 | const gop = try wasm.object_function_imports.getOrPut(gpa, name); |
| 590 | const payload_len = try leb.readUleb128(u32, reader); | 982 | const fn_ty_index = ptr.function_index.ptr(ss).*; |
| 591 | if (payload_len == 0) return; | 983 | if (gop.found_existing) { |
| 592 | 984 | if (gop.value_ptr.type != fn_ty_index) { | |
| 593 | var limited = std.io.limitedReader(reader, payload_len); | 985 | var err = try diags.addErrorWithNotes(2); |
| 594 | const limited_reader = limited.reader(); | 986 | try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)}); |
| 595 | 987 | gop.value_ptr.source_location.addNote(&err, "imported as {} here", .{ | |
| 596 | // every subsection contains a 'count' field | 988 | gop.value_ptr.type.fmt(wasm), |
| 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 | }); | 989 | }); |
| 618 | 990 | source_location.addNote(&err, "imported as {} here", .{fn_ty_index.fmt(wasm)}); | |
| 619 | // support legacy object files that specified being TLS by the name instead of the TLS flag. | 991 | continue; |
| 620 | if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) { | 992 | } |
| 621 | // set the flag so we can simply check for the flag in the rest of the linker. | 993 | if (gop.value_ptr.module_name != ptr.module_name.toOptional()) { |
| 622 | segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS); | 994 | var err = try diags.addErrorWithNotes(2); |
| 995 | try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)}); | ||
| 996 | if (gop.value_ptr.module_name.slice(wasm)) |module_name| { | ||
| 997 | gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{module_name}); | ||
| 998 | } else { | ||
| 999 | gop.value_ptr.source_location.addNote(&err, "no module here", .{}); | ||
| 623 | } | 1000 | } |
| 1001 | source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)}); | ||
| 1002 | continue; | ||
| 624 | } | 1003 | } |
| 625 | parser.object.segment_info = segments; | 1004 | if (gop.value_ptr.name != ptr.name) { |
| 626 | }, | 1005 | var err = try diags.addErrorWithNotes(2); |
| 627 | .WASM_INIT_FUNCS => { | 1006 | try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)}); |
| 628 | const funcs = try gpa.alloc(Wasm.InitFunc, count); | 1007 | gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)}); |
| 629 | errdefer gpa.free(funcs); | 1008 | source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)}); |
| 630 | for (funcs) |*func| { | 1009 | continue; |
| 631 | func.* = .{ | ||
| 632 | .priority = try leb.readUleb128(u32, reader), | ||
| 633 | .symbol_index = try leb.readUleb128(u32, reader), | ||
| 634 | }; | ||
| 635 | log.debug("Found function - prio: {d}, index: {d}", .{ func.priority, func.symbol_index }); | ||
| 636 | } | 1010 | } |
| 637 | parser.object.init_funcs = funcs; | 1011 | } else { |
| 638 | }, | 1012 | gop.value_ptr.* = .{ |
| 639 | .WASM_COMDAT_INFO => { | 1013 | .flags = symbol.flags, |
| 640 | const comdats = try gpa.alloc(Wasm.Comdat, count); | 1014 | .module_name = ptr.module_name.toOptional(), |
| 641 | errdefer gpa.free(comdats); | 1015 | .name = ptr.name, |
| 642 | for (comdats) |*comdat| { | 1016 | .source_location = source_location, |
| 643 | const name_len = try leb.readUleb128(u32, reader); | 1017 | .resolution = .unresolved, |
| 644 | const name = try gpa.alloc(u8, name_len); | 1018 | .type = fn_ty_index, |
| 645 | errdefer gpa.free(name); | 1019 | }; |
| 646 | try reader.readNoEof(name); | 1020 | } |
| 647 | 1021 | }, | |
| 648 | const flags = try leb.readUleb128(u32, reader); | 1022 | .global_import => |index| { |
| 649 | if (flags != 0) { | 1023 | const ptr = index.ptr(ss); |
| 650 | return error.UnexpectedValue; | 1024 | const name = symbol.name.unwrap() orelse ptr.name; |
| 651 | } | 1025 | if (symbol.flags.binding == .local) { |
| 652 | 1026 | diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); | |
| 653 | const symbol_count = try leb.readUleb128(u32, reader); | 1027 | continue; |
| 654 | const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count); | 1028 | } |
| 655 | errdefer gpa.free(symbols); | 1029 | const gop = try wasm.object_global_imports.getOrPut(gpa, name); |
| 656 | for (symbols) |*symbol| { | 1030 | if (gop.found_existing) { |
| 657 | symbol.* = .{ | 1031 | const existing_ty = gop.value_ptr.type(); |
| 658 | .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))), | 1032 | if (ptr.valtype != existing_ty.valtype) { |
| 659 | .index = try leb.readUleb128(u32, reader), | 1033 | var err = try diags.addErrorWithNotes(2); |
| 660 | }; | 1034 | try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)}); |
| 1035 | gop.value_ptr.source_location.addNote(&err, "type {s} here", .{@tagName(existing_ty.valtype)}); | ||
| 1036 | source_location.addNote(&err, "type {s} here", .{@tagName(ptr.valtype)}); | ||
| 1037 | continue; | ||
| 1038 | } | ||
| 1039 | if (ptr.mutable != existing_ty.mutable) { | ||
| 1040 | var err = try diags.addErrorWithNotes(2); | ||
| 1041 | try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)}); | ||
| 1042 | gop.value_ptr.source_location.addNote(&err, "{s} here", .{ | ||
| 1043 | if (existing_ty.mutable) "mutable" else "not mutable", | ||
| 1044 | }); | ||
| 1045 | source_location.addNote(&err, "{s} here", .{ | ||
| 1046 | if (ptr.mutable) "mutable" else "not mutable", | ||
| 1047 | }); | ||
| 1048 | continue; | ||
| 1049 | } | ||
| 1050 | if (gop.value_ptr.module_name != ptr.module_name.toOptional()) { | ||
| 1051 | var err = try diags.addErrorWithNotes(2); | ||
| 1052 | try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)}); | ||
| 1053 | if (gop.value_ptr.module_name.slice(wasm)) |module_name| { | ||
| 1054 | gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{module_name}); | ||
| 1055 | } else { | ||
| 1056 | gop.value_ptr.source_location.addNote(&err, "no module here", .{}); | ||
| 661 | } | 1057 | } |
| 662 | 1058 | source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)}); | |
| 663 | comdat.* = .{ | 1059 | continue; |
| 664 | .name = name, | ||
| 665 | .flags = flags, | ||
| 666 | .symbols = symbols, | ||
| 667 | }; | ||
| 668 | } | 1060 | } |
| 669 | 1061 | if (gop.value_ptr.name != ptr.name) { | |
| 670 | parser.object.comdat_info = comdats; | 1062 | var err = try diags.addErrorWithNotes(2); |
| 671 | }, | 1063 | try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)}); |
| 672 | .WASM_SYMBOL_TABLE => { | 1064 | gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)}); |
| 673 | var symbols = try std.ArrayList(Symbol).initCapacity(gpa, count); | 1065 | source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)}); |
| 674 | 1066 | continue; | |
| 675 | var i: usize = 0; | 1067 | } |
| 676 | while (i < count) : (i += 1) { | 1068 | } else { |
| 677 | const symbol = symbols.addOneAssumeCapacity(); | 1069 | gop.value_ptr.* = .{ |
| 678 | symbol.* = try parser.parseSymbol(gpa, reader); | 1070 | .flags = symbol.flags, |
| 679 | log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{ | 1071 | .module_name = ptr.module_name.toOptional(), |
| 680 | @tagName(symbol.tag), | 1072 | .name = ptr.name, |
| 681 | wasm.stringSlice(symbol.name), | 1073 | .source_location = source_location, |
| 682 | symbol.flags, | 1074 | .resolution = .unresolved, |
| 1075 | }; | ||
| 1076 | gop.value_ptr.flags.global_type = .{ | ||
| 1077 | .valtype = .from(ptr.valtype), | ||
| 1078 | .mutable = ptr.mutable, | ||
| 1079 | }; | ||
| 1080 | } | ||
| 1081 | }, | ||
| 1082 | .table_import => |index| { | ||
| 1083 | const ptr = index.ptr(ss); | ||
| 1084 | const name = symbol.name.unwrap() orelse ptr.name; | ||
| 1085 | if (symbol.flags.binding == .local) { | ||
| 1086 | diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); | ||
| 1087 | continue; | ||
| 1088 | } | ||
| 1089 | const gop = try wasm.object_table_imports.getOrPut(gpa, name); | ||
| 1090 | if (gop.found_existing) { | ||
| 1091 | const existing_reftype = gop.value_ptr.flags.ref_type.to(); | ||
| 1092 | if (ptr.ref_type != existing_reftype) { | ||
| 1093 | var err = try diags.addErrorWithNotes(2); | ||
| 1094 | try err.addMsg("symbol '{s}' mismatching table reftypes", .{name.slice(wasm)}); | ||
| 1095 | gop.value_ptr.source_location.addNote(&err, "{s} here", .{@tagName(existing_reftype)}); | ||
| 1096 | source_location.addNote(&err, "{s} here", .{@tagName(ptr.ref_type)}); | ||
| 1097 | continue; | ||
| 1098 | } | ||
| 1099 | if (gop.value_ptr.module_name != ptr.module_name) { | ||
| 1100 | var err = try diags.addErrorWithNotes(2); | ||
| 1101 | try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)}); | ||
| 1102 | gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{ | ||
| 1103 | gop.value_ptr.module_name.slice(wasm), | ||
| 683 | }); | 1104 | }); |
| 1105 | source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)}); | ||
| 1106 | continue; | ||
| 684 | } | 1107 | } |
| 685 | 1108 | if (gop.value_ptr.name != ptr.name) { | |
| 686 | // we found all symbols, check for indirect function table | 1109 | var err = try diags.addErrorWithNotes(2); |
| 687 | // in case of an MVP object file | 1110 | try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)}); |
| 688 | if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm)) |symbol| { | 1111 | gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)}); |
| 689 | try symbols.append(symbol); | 1112 | source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)}); |
| 690 | log.debug("Found legacy indirect function table. Created symbol", .{}); | 1113 | continue; |
| 691 | } | 1114 | } |
| 692 | 1115 | if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong; | |
| 693 | // Not all debug sections may be represented by a symbol, for those sections | 1116 | if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false; |
| 694 | // we manually create a symbol. | 1117 | if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true; |
| 695 | if (parser.object.relocatable_data.get(.custom)) |custom_sections| { | 1118 | } else { |
| 696 | for (custom_sections) |*data| { | 1119 | gop.value_ptr.* = .{ |
| 697 | if (!data.represented) { | 1120 | .flags = symbol.flags, |
| 698 | const name = wasm.castToString(data.index); | 1121 | .module_name = ptr.module_name, |
| 699 | try symbols.append(.{ | 1122 | .name = ptr.name, |
| 700 | .name = name, | 1123 | .source_location = source_location, |
| 701 | .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | 1124 | .resolution = .unresolved, |
| 702 | .tag = .section, | 1125 | .limits_min = ptr.limits_min, |
| 703 | .virtual_address = 0, | 1126 | .limits_max = ptr.limits_max, |
| 704 | .index = data.section_index, | 1127 | }; |
| 705 | }); | 1128 | gop.value_ptr.flags.limits_has_max = ptr.limits_has_max; |
| 706 | data.represented = true; | 1129 | gop.value_ptr.flags.limits_is_shared = ptr.limits_is_shared; |
| 707 | log.debug("Created synthetic custom section symbol for '{s}'", .{ | 1130 | gop.value_ptr.flags.ref_type = .from(ptr.ref_type); |
| 708 | wasm.stringSlice(name), | 1131 | } |
| 709 | }); | 1132 | }, |
| 710 | } | 1133 | .data_import => { |
| 711 | } | 1134 | const name = symbol.name.unwrap().?; |
| 1135 | if (symbol.flags.binding == .local) { | ||
| 1136 | diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); | ||
| 1137 | continue; | ||
| 1138 | } | ||
| 1139 | const gop = try wasm.object_data_imports.getOrPut(gpa, name); | ||
| 1140 | if (!gop.found_existing) gop.value_ptr.* = .{ | ||
| 1141 | .flags = symbol.flags, | ||
| 1142 | .source_location = source_location, | ||
| 1143 | .resolution = .unresolved, | ||
| 1144 | }; | ||
| 1145 | }, | ||
| 1146 | .function => |index| { | ||
| 1147 | assert(!symbol.flags.undefined); | ||
| 1148 | const ptr = index.ptr(wasm); | ||
| 1149 | ptr.name = symbol.name; | ||
| 1150 | ptr.flags = symbol.flags; | ||
| 1151 | if (symbol.flags.binding == .local) continue; // No participation in symbol resolution. | ||
| 1152 | const name = symbol.name.unwrap().?; | ||
| 1153 | const gop = try wasm.object_function_imports.getOrPut(gpa, name); | ||
| 1154 | if (gop.found_existing) { | ||
| 1155 | if (gop.value_ptr.type != ptr.type_index) { | ||
| 1156 | var err = try diags.addErrorWithNotes(2); | ||
| 1157 | try err.addMsg("function signature mismatch: {s}", .{name.slice(wasm)}); | ||
| 1158 | gop.value_ptr.source_location.addNote(&err, "exported as {} here", .{ | ||
| 1159 | ptr.type_index.fmt(wasm), | ||
| 1160 | }); | ||
| 1161 | const word = if (gop.value_ptr.resolution == .unresolved) "imported" else "exported"; | ||
| 1162 | source_location.addNote(&err, "{s} as {} here", .{ word, gop.value_ptr.type.fmt(wasm) }); | ||
| 1163 | continue; | ||
| 712 | } | 1164 | } |
| 1165 | if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) { | ||
| 1166 | // Intentional: if they're both weak, take the last one. | ||
| 1167 | gop.value_ptr.source_location = source_location; | ||
| 1168 | gop.value_ptr.module_name = host_name; | ||
| 1169 | gop.value_ptr.resolution = .fromObjectFunction(wasm, index); | ||
| 1170 | gop.value_ptr.flags = symbol.flags; | ||
| 1171 | continue; | ||
| 1172 | } | ||
| 1173 | if (ptr.flags.binding == .weak) { | ||
| 1174 | // Keep the existing one. | ||
| 1175 | continue; | ||
| 1176 | } | ||
| 1177 | var err = try diags.addErrorWithNotes(2); | ||
| 1178 | try err.addMsg("symbol collision: {s}", .{name.slice(wasm)}); | ||
| 1179 | gop.value_ptr.source_location.addNote(&err, "exported as {} here", .{ptr.type_index.fmt(wasm)}); | ||
| 1180 | source_location.addNote(&err, "exported as {} here", .{gop.value_ptr.type.fmt(wasm)}); | ||
| 1181 | continue; | ||
| 1182 | } else { | ||
| 1183 | gop.value_ptr.* = .{ | ||
| 1184 | .flags = symbol.flags, | ||
| 1185 | .module_name = host_name, | ||
| 1186 | .name = name, | ||
| 1187 | .source_location = source_location, | ||
| 1188 | .resolution = .fromObjectFunction(wasm, index), | ||
| 1189 | .type = ptr.type_index, | ||
| 1190 | }; | ||
| 1191 | } | ||
| 1192 | }, | ||
| 1193 | .global => |index| { | ||
| 1194 | assert(!symbol.flags.undefined); | ||
| 1195 | const ptr = index.ptr(wasm); | ||
| 1196 | ptr.name = symbol.name; | ||
| 1197 | ptr.flags = symbol.flags; | ||
| 1198 | if (symbol.flags.binding == .local) continue; // No participation in symbol resolution. | ||
| 1199 | const name = symbol.name.unwrap().?; | ||
| 1200 | const new_ty = ptr.type(); | ||
| 1201 | const gop = try wasm.object_global_imports.getOrPut(gpa, name); | ||
| 1202 | if (gop.found_existing) { | ||
| 1203 | const existing_ty = gop.value_ptr.type(); | ||
| 1204 | if (new_ty.valtype != existing_ty.valtype) { | ||
| 1205 | var err = try diags.addErrorWithNotes(2); | ||
| 1206 | try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)}); | ||
| 1207 | gop.value_ptr.source_location.addNote(&err, "type {s} here", .{@tagName(existing_ty.valtype)}); | ||
| 1208 | source_location.addNote(&err, "type {s} here", .{@tagName(new_ty.valtype)}); | ||
| 1209 | continue; | ||
| 1210 | } | ||
| 1211 | if (new_ty.mutable != existing_ty.mutable) { | ||
| 1212 | var err = try diags.addErrorWithNotes(2); | ||
| 1213 | try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)}); | ||
| 1214 | gop.value_ptr.source_location.addNote(&err, "{s} here", .{ | ||
| 1215 | if (existing_ty.mutable) "mutable" else "not mutable", | ||
| 1216 | }); | ||
| 1217 | source_location.addNote(&err, "{s} here", .{ | ||
| 1218 | if (new_ty.mutable) "mutable" else "not mutable", | ||
| 1219 | }); | ||
| 1220 | continue; | ||
| 1221 | } | ||
| 1222 | if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) { | ||
| 1223 | // Intentional: if they're both weak, take the last one. | ||
| 1224 | gop.value_ptr.source_location = source_location; | ||
| 1225 | gop.value_ptr.module_name = host_name; | ||
| 1226 | gop.value_ptr.resolution = .fromObjectGlobal(wasm, index); | ||
| 1227 | gop.value_ptr.flags = symbol.flags; | ||
| 1228 | continue; | ||
| 1229 | } | ||
| 1230 | if (ptr.flags.binding == .weak) { | ||
| 1231 | // Keep the existing one. | ||
| 1232 | continue; | ||
| 1233 | } | ||
| 1234 | var err = try diags.addErrorWithNotes(2); | ||
| 1235 | try err.addMsg("symbol collision: {s}", .{name.slice(wasm)}); | ||
| 1236 | gop.value_ptr.source_location.addNote(&err, "exported as {s} here", .{@tagName(existing_ty.valtype)}); | ||
| 1237 | source_location.addNote(&err, "exported as {s} here", .{@tagName(new_ty.valtype)}); | ||
| 1238 | continue; | ||
| 1239 | } else { | ||
| 1240 | gop.value_ptr.* = .{ | ||
| 1241 | .flags = symbol.flags, | ||
| 1242 | .module_name = .none, | ||
| 1243 | .name = name, | ||
| 1244 | .source_location = source_location, | ||
| 1245 | .resolution = .fromObjectGlobal(wasm, index), | ||
| 1246 | }; | ||
| 1247 | gop.value_ptr.flags.global_type = .{ | ||
| 1248 | .valtype = .from(new_ty.valtype), | ||
| 1249 | .mutable = new_ty.mutable, | ||
| 1250 | }; | ||
| 1251 | } | ||
| 1252 | }, | ||
| 1253 | .table => |i| { | ||
| 1254 | assert(!symbol.flags.undefined); | ||
| 1255 | const ptr = i.ptr(wasm); | ||
| 1256 | ptr.name = symbol.name; | ||
| 1257 | ptr.flags = symbol.flags; | ||
| 1258 | }, | ||
| 1259 | .data => |index| { | ||
| 1260 | assert(!symbol.flags.undefined); | ||
| 1261 | const ptr = index.ptr(wasm); | ||
| 1262 | const name = ptr.name; | ||
| 1263 | assert(name.toOptional() == symbol.name); | ||
| 1264 | ptr.flags = symbol.flags; | ||
| 1265 | if (symbol.flags.binding == .local) continue; // No participation in symbol resolution. | ||
| 1266 | const gop = try wasm.object_data_imports.getOrPut(gpa, name); | ||
| 1267 | if (gop.found_existing) { | ||
| 1268 | if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) { | ||
| 1269 | // Intentional: if they're both weak, take the last one. | ||
| 1270 | gop.value_ptr.source_location = source_location; | ||
| 1271 | gop.value_ptr.resolution = .fromObjectDataIndex(wasm, index); | ||
| 1272 | gop.value_ptr.flags = symbol.flags; | ||
| 1273 | continue; | ||
| 1274 | } | ||
| 1275 | if (ptr.flags.binding == .weak) { | ||
| 1276 | // Keep the existing one. | ||
| 1277 | continue; | ||
| 1278 | } | ||
| 1279 | var err = try diags.addErrorWithNotes(2); | ||
| 1280 | try err.addMsg("symbol collision: {s}", .{name.slice(wasm)}); | ||
| 1281 | gop.value_ptr.source_location.addNote(&err, "exported here", .{}); | ||
| 1282 | source_location.addNote(&err, "exported here", .{}); | ||
| 1283 | continue; | ||
| 1284 | } else { | ||
| 1285 | gop.value_ptr.* = .{ | ||
| 1286 | .flags = symbol.flags, | ||
| 1287 | .source_location = source_location, | ||
| 1288 | .resolution = .fromObjectDataIndex(wasm, index), | ||
| 1289 | }; | ||
| 1290 | } | ||
| 1291 | }, | ||
| 1292 | .section => |i| { | ||
| 1293 | // Name is provided by the section directly; symbol table does not have it. | ||
| 1294 | //const ptr = i.ptr(wasm); | ||
| 1295 | //ptr.flags = symbol.flags; | ||
| 1296 | _ = i; | ||
| 1297 | if (symbol.flags.undefined and symbol.flags.binding == .local) { | ||
| 1298 | const name = symbol.name.slice(wasm).?; | ||
| 1299 | diags.addParseError(path, "local symbol '{s}' references import", .{name}); | ||
| 1300 | } | ||
| 1301 | }, | ||
| 1302 | }; | ||
| 713 | 1303 | ||
| 714 | parser.object.symtable = try symbols.toOwnedSlice(); | 1304 | // Apply export section info. This is done after the symbol table above so |
| 1305 | // that the symbol table can take precedence, overriding the export name. | ||
| 1306 | for (ss.exports.items) |*exp| { | ||
| 1307 | switch (exp.pointee) { | ||
| 1308 | inline .function, .table, .memory, .global => |index| { | ||
| 1309 | const ptr = index.ptr(wasm); | ||
| 1310 | ptr.name = exp.name.toOptional(); | ||
| 1311 | ptr.flags.exported = true; | ||
| 715 | }, | 1312 | }, |
| 716 | } | 1313 | } |
| 717 | } | 1314 | } |
| 718 | 1315 | ||
| 719 | /// Parses the symbol information based on its kind, | 1316 | // Apply segment_info. |
| 720 | /// requires access to `Object` to find the name of a symbol when it's | 1317 | const data_segments = wasm.object_data_segments.items[data_segment_start..]; |
| 721 | /// an import and flag `WASM_SYM_EXPLICIT_NAME` is not set. | 1318 | if (data_segments.len != ss.segment_info.items.len) { |
| 722 | fn parseSymbol(parser: *Parser, gpa: Allocator, reader: anytype) !Symbol { | 1319 | return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{ |
| 723 | const wasm = parser.wasm; | 1320 | data_segments.len, ss.segment_info.items.len, |
| 724 | const tag: Symbol.Tag = @enumFromInt(try leb.readUleb128(u8, reader)); | 1321 | }); |
| 725 | const flags = try leb.readUleb128(u32, reader); | 1322 | } |
| 726 | var symbol: Symbol = .{ | 1323 | for (data_segments, ss.segment_info.items) |*data, info| { |
| 727 | .flags = flags, | 1324 | data.name = info.name.toOptional(); |
| 728 | .tag = tag, | 1325 | data.flags = .{ |
| 729 | .name = undefined, | 1326 | .is_passive = data.flags.is_passive, |
| 730 | .index = undefined, | 1327 | .strings = info.flags.strings, |
| 731 | .virtual_address = undefined, | 1328 | .tls = info.flags.tls, |
| 1329 | .retain = info.flags.retain, | ||
| 1330 | .alignment = info.flags.alignment, | ||
| 732 | }; | 1331 | }; |
| 1332 | } | ||
| 733 | 1333 | ||
| 734 | switch (tag) { | 1334 | // Check for indirect function table in case of an MVP object file. |
| 735 | .data => { | 1335 | legacy_indirect_function_table: { |
| 736 | const name_len = try leb.readUleb128(u32, reader); | 1336 | // If there is a symbol for each import table, this is not a legacy object file. |
| 737 | const name = try gpa.alloc(u8, name_len); | 1337 | if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table; |
| 738 | defer gpa.free(name); | 1338 | if (table_import_symbol_count != 0) { |
| 739 | try reader.readNoEof(name); | 1339 | return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{ |
| 740 | symbol.name = try wasm.internString(name); | 1340 | ss.table_imports.items.len, table_import_symbol_count, |
| 741 | 1341 | }); | |
| 742 | // Data symbols only have the following fields if the symbol is defined | ||
| 743 | if (symbol.isDefined()) { | ||
| 744 | symbol.index = try leb.readUleb128(u32, reader); | ||
| 745 | // @TODO: We should verify those values | ||
| 746 | _ = try leb.readUleb128(u32, reader); | ||
| 747 | _ = try leb.readUleb128(u32, reader); | ||
| 748 | } | ||
| 749 | }, | ||
| 750 | .section => { | ||
| 751 | symbol.index = try leb.readUleb128(u32, reader); | ||
| 752 | const section_data = parser.object.relocatable_data.get(.custom).?; | ||
| 753 | for (section_data) |*data| { | ||
| 754 | if (data.section_index == symbol.index) { | ||
| 755 | symbol.name = wasm.castToString(data.index); | ||
| 756 | data.represented = true; | ||
| 757 | break; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | }, | ||
| 761 | else => { | ||
| 762 | symbol.index = try leb.readUleb128(u32, reader); | ||
| 763 | const is_undefined = symbol.isUndefined(); | ||
| 764 | const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME); | ||
| 765 | symbol.name = if (!is_undefined or (is_undefined and explicit_name)) name: { | ||
| 766 | const name_len = try leb.readUleb128(u32, reader); | ||
| 767 | const name = try gpa.alloc(u8, name_len); | ||
| 768 | defer gpa.free(name); | ||
| 769 | try reader.readNoEof(name); | ||
| 770 | break :name try wasm.internString(name); | ||
| 771 | } else parser.object.findImport(symbol).name; | ||
| 772 | }, | ||
| 773 | } | 1342 | } |
| 774 | return symbol; | 1343 | // MVP object files cannot have any table definitions, only imports |
| 1344 | // (for the indirect function table). | ||
| 1345 | const tables = wasm.object_tables.items[tables_start..]; | ||
| 1346 | if (tables.len > 0) { | ||
| 1347 | return diags.failParse(path, "table definition without representing table symbols", .{}); | ||
| 1348 | } | ||
| 1349 | if (ss.table_imports.items.len != 1) { | ||
| 1350 | return diags.failParse(path, "found more than one table import, but no representing table symbols", .{}); | ||
| 1351 | } | ||
| 1352 | const table_import_name = ss.table_imports.items[0].name; | ||
| 1353 | if (table_import_name != wasm.preloaded_strings.__indirect_function_table) { | ||
| 1354 | return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{ | ||
| 1355 | table_import_name.slice(wasm), | ||
| 1356 | }); | ||
| 1357 | } | ||
| 1358 | const ptr = wasm.object_table_imports.getPtr(table_import_name).?; | ||
| 1359 | ptr.flags = .{ | ||
| 1360 | .undefined = true, | ||
| 1361 | .no_strip = true, | ||
| 1362 | }; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | for (wasm.object_init_funcs.items[init_funcs_start..]) |init_func| { | ||
| 1366 | const func = init_func.function_index.ptr(wasm); | ||
| 1367 | const params = func.type_index.ptr(wasm).params.slice(wasm); | ||
| 1368 | if (params.len != 0) diags.addError("constructor function '{s}' has non-empty parameter list", .{ | ||
| 1369 | func.name.slice(wasm).?, | ||
| 1370 | }); | ||
| 775 | } | 1371 | } |
| 776 | }; | ||
| 777 | 1372 | ||
| 778 | /// First reads the count from the reader and then allocate | 1373 | const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start); |
| 779 | /// a slice of ptr child's element type. | 1374 | if (functions_len > 0 and code_section_index == null) |
| 780 | fn readVec(ptr: anytype, reader: anytype, gpa: Allocator) ![]ElementType(@TypeOf(ptr)) { | 1375 | return diags.failParse(path, "code section missing ({d} functions)", .{functions_len}); |
| 781 | const len = try readLeb(u32, reader); | 1376 | |
| 782 | const slice = try gpa.alloc(ElementType(@TypeOf(ptr)), len); | 1377 | return .{ |
| 783 | ptr.* = slice; | 1378 | .version = version, |
| 784 | return slice; | 1379 | .path = path, |
| 1380 | .archive_member_name = try wasm.internOptionalString(archive_member_name), | ||
| 1381 | .start_function = start_function, | ||
| 1382 | .features = features, | ||
| 1383 | .functions = .{ | ||
| 1384 | .off = functions_start, | ||
| 1385 | .len = functions_len, | ||
| 1386 | }, | ||
| 1387 | .function_imports = .{ | ||
| 1388 | .off = function_imports_start, | ||
| 1389 | .len = @intCast(wasm.object_function_imports.entries.len - function_imports_start), | ||
| 1390 | }, | ||
| 1391 | .global_imports = .{ | ||
| 1392 | .off = global_imports_start, | ||
| 1393 | .len = @intCast(wasm.object_global_imports.entries.len - global_imports_start), | ||
| 1394 | }, | ||
| 1395 | .table_imports = .{ | ||
| 1396 | .off = table_imports_start, | ||
| 1397 | .len = @intCast(wasm.object_table_imports.entries.len - table_imports_start), | ||
| 1398 | }, | ||
| 1399 | .data_imports = .{ | ||
| 1400 | .off = data_imports_start, | ||
| 1401 | .len = @intCast(wasm.object_data_imports.entries.len - data_imports_start), | ||
| 1402 | }, | ||
| 1403 | .init_funcs = .{ | ||
| 1404 | .off = init_funcs_start, | ||
| 1405 | .len = @intCast(wasm.object_init_funcs.items.len - init_funcs_start), | ||
| 1406 | }, | ||
| 1407 | .comdats = .{ | ||
| 1408 | .off = comdats_start, | ||
| 1409 | .len = @intCast(wasm.object_comdats.items.len - comdats_start), | ||
| 1410 | }, | ||
| 1411 | .custom_segments = .{ | ||
| 1412 | .off = custom_segment_start, | ||
| 1413 | .len = @intCast(wasm.object_custom_segments.entries.len - custom_segment_start), | ||
| 1414 | }, | ||
| 1415 | .code_section_index = code_section_index, | ||
| 1416 | .global_section_index = global_section_index, | ||
| 1417 | .data_section_index = data_section_index, | ||
| 1418 | .is_included = must_link, | ||
| 1419 | }; | ||
| 785 | } | 1420 | } |
| 786 | 1421 | ||
| 787 | fn ElementType(comptime ptr: type) type { | 1422 | /// Based on the "features" custom section, parses it into a list of |
| 788 | return meta.Elem(meta.Child(ptr)); | 1423 | /// features that tell the linker what features were enabled and may be mandatory |
| 1424 | /// to be able to link. | ||
| 1425 | fn parseFeatures( | ||
| 1426 | wasm: *Wasm, | ||
| 1427 | bytes: []const u8, | ||
| 1428 | start_pos: usize, | ||
| 1429 | path: Path, | ||
| 1430 | ) error{ OutOfMemory, LinkFailure }!struct { Wasm.Feature.Set, usize } { | ||
| 1431 | const gpa = wasm.base.comp.gpa; | ||
| 1432 | const diags = &wasm.base.comp.link_diags; | ||
| 1433 | const features_len, var pos = readLeb(u32, bytes, start_pos); | ||
| 1434 | // This temporary allocation could be avoided by using the string_bytes buffer as a scratch space. | ||
| 1435 | const feature_buffer = try gpa.alloc(Wasm.Feature, features_len); | ||
| 1436 | defer gpa.free(feature_buffer); | ||
| 1437 | for (feature_buffer) |*feature| { | ||
| 1438 | const prefix: Wasm.Feature.Prefix = switch (bytes[pos]) { | ||
| 1439 | '-' => .@"-", | ||
| 1440 | '+' => .@"+", | ||
| 1441 | '=' => .@"=", | ||
| 1442 | else => |b| return diags.failParse(path, "invalid feature prefix: 0x{x}", .{b}), | ||
| 1443 | }; | ||
| 1444 | pos += 1; | ||
| 1445 | const name, pos = readBytes(bytes, pos); | ||
| 1446 | const tag = std.meta.stringToEnum(Wasm.Feature.Tag, name) orelse { | ||
| 1447 | return diags.failParse(path, "unrecognized wasm feature in object: {s}", .{name}); | ||
| 1448 | }; | ||
| 1449 | feature.* = .{ | ||
| 1450 | .prefix = prefix, | ||
| 1451 | .tag = tag, | ||
| 1452 | }; | ||
| 1453 | } | ||
| 1454 | std.mem.sortUnstable(Wasm.Feature, feature_buffer, {}, Wasm.Feature.lessThan); | ||
| 1455 | |||
| 1456 | return .{ | ||
| 1457 | .fromString(try wasm.internString(@ptrCast(feature_buffer))), | ||
| 1458 | pos, | ||
| 1459 | }; | ||
| 789 | } | 1460 | } |
| 790 | 1461 | ||
| 791 | /// Uses either `readIleb128` or `readUleb128` depending on the | 1462 | fn readLeb(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } { |
| 792 | /// signedness of the given type `T`. | 1463 | var fbr = std.io.fixedBufferStream(bytes[pos..]); |
| 793 | /// Asserts `T` is an integer. | 1464 | return .{ |
| 794 | fn readLeb(comptime T: type, reader: anytype) !T { | 1465 | switch (@typeInfo(T).int.signedness) { |
| 795 | return switch (@typeInfo(T).int.signedness) { | 1466 | .signed => std.leb.readIleb128(T, fbr.reader()) catch unreachable, |
| 796 | .signed => try leb.readIleb128(T, reader), | 1467 | .unsigned => std.leb.readUleb128(T, fbr.reader()) catch unreachable, |
| 797 | .unsigned => try leb.readUleb128(T, reader), | 1468 | }, |
| 1469 | pos + fbr.pos, | ||
| 798 | }; | 1470 | }; |
| 799 | } | 1471 | } |
| 800 | 1472 | ||
| 801 | /// Reads an enum type from the given reader. | 1473 | fn readBytes(bytes: []const u8, start_pos: usize) struct { []const u8, usize } { |
| 802 | /// Asserts `T` is an enum | 1474 | const len, const pos = readLeb(u32, bytes, start_pos); |
| 803 | fn readEnum(comptime T: type, reader: anytype) !T { | 1475 | return .{ |
| 804 | switch (@typeInfo(T)) { | 1476 | bytes[pos..][0..len], |
| 805 | .@"enum" => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))), | 1477 | pos + len, |
| 806 | else => @compileError("T must be an enum. Instead was given type " ++ @typeName(T)), | 1478 | }; |
| 807 | } | ||
| 808 | } | 1479 | } |
| 809 | 1480 | ||
| 810 | fn readLimits(reader: anytype) !std.wasm.Limits { | 1481 | fn readEnum(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } { |
| 811 | const flags = try reader.readByte(); | 1482 | const Tag = @typeInfo(T).@"enum".tag_type; |
| 812 | const min = try readLeb(u32, reader); | 1483 | const int, const new_pos = readLeb(Tag, bytes, pos); |
| 813 | var limits: std.wasm.Limits = .{ | 1484 | return .{ @enumFromInt(int), new_pos }; |
| 1485 | } | ||
| 1486 | |||
| 1487 | fn readLimits(bytes: []const u8, start_pos: usize) struct { std.wasm.Limits, usize } { | ||
| 1488 | const flags: std.wasm.Limits.Flags = @bitCast(bytes[start_pos]); | ||
| 1489 | const min, const max_pos = readLeb(u32, bytes, start_pos + 1); | ||
| 1490 | const max, const end_pos = if (flags.has_max) readLeb(u32, bytes, max_pos) else .{ 0, max_pos }; | ||
| 1491 | return .{ .{ | ||
| 814 | .flags = flags, | 1492 | .flags = flags, |
| 815 | .min = min, | 1493 | .min = min, |
| 816 | .max = undefined, | 1494 | .max = max, |
| 817 | }; | 1495 | }, end_pos }; |
| 818 | if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) { | ||
| 819 | limits.max = try readLeb(u32, reader); | ||
| 820 | } | ||
| 821 | return limits; | ||
| 822 | } | 1496 | } |
| 823 | 1497 | ||
| 824 | fn readInit(reader: anytype) !std.wasm.InitExpression { | 1498 | fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usize } { |
| 825 | const opcode = try reader.readByte(); | 1499 | const end_pos = try skipInit(bytes, pos); // one after the end opcode |
| 826 | const init_expr: std.wasm.InitExpression = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) { | 1500 | return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos }; |
| 827 | .i32_const => .{ .i32_const = try readLeb(i32, reader) }, | 1501 | } |
| 828 | .global_get => .{ .global_get = try readLeb(u32, reader) }, | ||
| 829 | else => @panic("TODO: initexpression for other opcodes"), | ||
| 830 | }; | ||
| 831 | 1502 | ||
| 832 | if ((try readEnum(std.wasm.Opcode, reader)) != .end) return error.MissingEndForExpression; | 1503 | pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize { |
| 833 | return init_expr; | 1504 | const opcode = bytes[pos]; |
| 1505 | return switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) { | ||
| 1506 | .i32_const => readLeb(i32, bytes, pos + 1)[1], | ||
| 1507 | .i64_const => readLeb(i64, bytes, pos + 1)[1], | ||
| 1508 | .f32_const => pos + 5, | ||
| 1509 | .f64_const => pos + 9, | ||
| 1510 | .global_get => readLeb(u32, bytes, pos + 1)[1], | ||
| 1511 | else => return error.InvalidInitOpcode, | ||
| 1512 | }; | ||
| 834 | } | 1513 | } |
| 835 | 1514 | ||
| 836 | fn assertEnd(reader: anytype) !void { | 1515 | fn skipInit(bytes: []const u8, pos: usize) !usize { |
| 837 | var buf: [1]u8 = undefined; | 1516 | const end_pos = try exprEndPos(bytes, pos); |
| 838 | const len = try reader.read(&buf); | 1517 | const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos); |
| 839 | if (len != 0) return error.MalformedSection; | 1518 | if (op != .end) return error.InitExprMissingEnd; |
| 840 | if (reader.context.bytes_left != 0) return error.MalformedSection; | 1519 | return final_pos; |
| 841 | } | 1520 | } |
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` | ||
| 9 | flags: 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. | ||
| 12 | name: 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. | ||
| 16 | index: u32, | ||
| 17 | /// Represents the kind of the symbol, such as a function or global. | ||
| 18 | tag: 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. | ||
| 21 | virtual_address: u32, | ||
| 22 | |||
| 23 | /// Represents a symbol index where `null` represents an invalid index. | ||
| 24 | pub const Index = enum(u32) { | ||
| 25 | null, | ||
| 26 | _, | ||
| 27 | }; | ||
| 28 | |||
| 29 | pub 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 | |||
| 57 | pub 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 | ||
| 91 | pub 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. | ||
| 101 | pub fn mark(symbol: *Symbol) void { | ||
| 102 | symbol.flags |= @intFromEnum(Flag.alive); | ||
| 103 | } | ||
| 104 | |||
| 105 | pub fn unmark(symbol: *Symbol) void { | ||
| 106 | symbol.flags &= ~@intFromEnum(Flag.alive); | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn isAlive(symbol: Symbol) bool { | ||
| 110 | return symbol.flags & @intFromEnum(Flag.alive) != 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn isDead(symbol: Symbol) bool { | ||
| 114 | return symbol.flags & @intFromEnum(Flag.alive) == 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | pub fn isTLS(symbol: Symbol) bool { | ||
| 118 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_TLS) != 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | pub fn hasFlag(symbol: Symbol, flag: Flag) bool { | ||
| 122 | return symbol.flags & @intFromEnum(flag) != 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | pub fn setFlag(symbol: *Symbol, flag: Flag) void { | ||
| 126 | symbol.flags |= @intFromEnum(flag); | ||
| 127 | } | ||
| 128 | |||
| 129 | pub fn isUndefined(symbol: Symbol) bool { | ||
| 130 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_UNDEFINED) != 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | pub 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 | |||
| 141 | pub 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 | |||
| 149 | pub fn isDefined(symbol: Symbol) bool { | ||
| 150 | return !symbol.isUndefined(); | ||
| 151 | } | ||
| 152 | |||
| 153 | pub fn isVisible(symbol: Symbol) bool { | ||
| 154 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) == 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | pub fn isLocal(symbol: Symbol) bool { | ||
| 158 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) != 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | pub fn isGlobal(symbol: Symbol) bool { | ||
| 162 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_BINDING_LOCAL) == 0; | ||
| 163 | } | ||
| 164 | |||
| 165 | pub fn isHidden(symbol: Symbol) bool { | ||
| 166 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_VISIBILITY_HIDDEN) != 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn isNoStrip(symbol: Symbol) bool { | ||
| 170 | return symbol.flags & @intFromEnum(Flag.WASM_SYM_NO_STRIP) != 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | pub 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 | |||
| 179 | pub 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 | ||
| 184 | pub 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 | |||
| 208 | const std = @import("std"); | ||
| 209 | const Symbol = @This(); | ||
| 210 | const 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. | ||
| 6 | path: Path, | ||
| 7 | /// Map of all `Nav` that are currently alive. | ||
| 8 | /// Each index maps to the corresponding `NavInfo`. | ||
| 9 | navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavInfo) = .empty, | ||
| 10 | /// List of function type signatures for this Zig module. | ||
| 11 | func_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. | ||
| 14 | functions: std.ArrayListUnmanaged(std.wasm.Func) = .empty, | ||
| 15 | /// List of indexes pointing to an entry within the `functions` list which has been removed. | ||
| 16 | functions_free_list: std.ArrayListUnmanaged(u32) = .empty, | ||
| 17 | /// Map of symbol locations, represented by its `Wasm.Import`. | ||
| 18 | imports: std.AutoHashMapUnmanaged(Symbol.Index, Wasm.Import) = .empty, | ||
| 19 | /// List of WebAssembly globals. | ||
| 20 | globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty, | ||
| 21 | /// Mapping between an `Atom` and its type index representing the Wasm | ||
| 22 | /// type of the function signature. | ||
| 23 | atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .empty, | ||
| 24 | /// List of all symbols generated by Zig code. | ||
| 25 | symbols: std.ArrayListUnmanaged(Symbol) = .empty, | ||
| 26 | /// Map from symbol name to their index into the `symbols` list. | ||
| 27 | global_syms: std.AutoHashMapUnmanaged(Wasm.String, Symbol.Index) = .empty, | ||
| 28 | /// List of symbol indexes which are free to be used. | ||
| 29 | symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .empty, | ||
| 30 | /// Extra metadata about the linking section, such as alignment of segments and their name. | ||
| 31 | segment_info: std.ArrayListUnmanaged(Wasm.NamedSegment) = .empty, | ||
| 32 | /// List of indexes which contain a free slot in the `segment_info` list. | ||
| 33 | segment_free_list: std.ArrayListUnmanaged(u32) = .empty, | ||
| 34 | /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index. | ||
| 35 | uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .empty, | ||
| 36 | /// List of atom indexes of functions that are generated by the backend. | ||
| 37 | synthetic_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`. | ||
| 43 | error_table_symbol: Symbol.Index = .null, | ||
| 44 | /// Atom index of the table of symbol names. This is stored so we can clean up the atom. | ||
| 45 | error_names_atom: Atom.Index = .null, | ||
| 46 | /// Amount of functions in the `import` sections. | ||
| 47 | imported_functions_count: u32 = 0, | ||
| 48 | /// Amount of globals in the `import` section. | ||
| 49 | imported_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. | ||
| 53 | stack_pointer_sym: Symbol.Index, | ||
| 54 | /// Debug information for the Zig module. | ||
| 55 | dwarf: ?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. | ||
| 60 | debug_info_atom: ?Atom.Index = null, | ||
| 61 | debug_line_atom: ?Atom.Index = null, | ||
| 62 | debug_loc_atom: ?Atom.Index = null, | ||
| 63 | debug_ranges_atom: ?Atom.Index = null, | ||
| 64 | debug_abbrev_atom: ?Atom.Index = null, | ||
| 65 | debug_str_atom: ?Atom.Index = null, | ||
| 66 | debug_pubnames_atom: ?Atom.Index = null, | ||
| 67 | debug_pubtypes_atom: ?Atom.Index = null, | ||
| 68 | /// The index of the segment representing the custom '.debug_info' section. | ||
| 69 | debug_info_index: ?u32 = null, | ||
| 70 | /// The index of the segment representing the custom '.debug_line' section. | ||
| 71 | debug_line_index: ?u32 = null, | ||
| 72 | /// The index of the segment representing the custom '.debug_loc' section. | ||
| 73 | debug_loc_index: ?u32 = null, | ||
| 74 | /// The index of the segment representing the custom '.debug_ranges' section. | ||
| 75 | debug_ranges_index: ?u32 = null, | ||
| 76 | /// The index of the segment representing the custom '.debug_pubnames' section. | ||
| 77 | debug_pubnames_index: ?u32 = null, | ||
| 78 | /// The index of the segment representing the custom '.debug_pubtypes' section. | ||
| 79 | debug_pubtypes_index: ?u32 = null, | ||
| 80 | /// The index of the segment representing the custom '.debug_pubtypes' section. | ||
| 81 | debug_str_index: ?u32 = null, | ||
| 82 | /// The index of the segment representing the custom '.debug_pubtypes' section. | ||
| 83 | debug_abbrev_index: ?u32 = null, | ||
| 84 | |||
| 85 | const 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. | ||
| 112 | pub 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 | |||
| 121 | fn 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 | |||
| 137 | pub 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`. | ||
| 143 | pub 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. | ||
| 212 | pub 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(). | ||
| 232 | pub 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 | |||
| 286 | pub 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 | |||
| 325 | fn 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. | ||
| 390 | fn 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`. | ||
| 414 | pub 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 | |||
| 433 | pub 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 | |||
| 466 | const LowerConstResult = union(enum) { | ||
| 467 | ok: Atom.Index, | ||
| 468 | fail: *Zcu.ErrorMsg, | ||
| 469 | }; | ||
| 470 | |||
| 471 | fn 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. | ||
| 532 | pub 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. | ||
| 566 | fn 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. | ||
| 637 | pub 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. | ||
| 692 | pub 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 | ||
| 721 | pub 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 | |||
| 777 | pub 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 | |||
| 819 | pub 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 | |||
| 842 | pub 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 | |||
| 919 | pub 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 | |||
| 973 | fn 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`. | ||
| 983 | pub 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. | ||
| 1003 | fn 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. | ||
| 1040 | pub 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`. | ||
| 1057 | pub 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 | |||
| 1077 | pub 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. | ||
| 1085 | fn 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. | ||
| 1116 | pub 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. | ||
| 1126 | pub 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. | ||
| 1165 | pub 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. | ||
| 1192 | fn 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 | |||
| 1205 | pub 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 | |||
| 1210 | const build_options = @import("build_options"); | ||
| 1211 | const builtin = @import("builtin"); | ||
| 1212 | const codegen = @import("../../codegen.zig"); | ||
| 1213 | const link = @import("../../link.zig"); | ||
| 1214 | const log = std.log.scoped(.zig_object); | ||
| 1215 | const std = @import("std"); | ||
| 1216 | const Path = std.Build.Cache.Path; | ||
| 1217 | |||
| 1218 | const Air = @import("../../Air.zig"); | ||
| 1219 | const Atom = Wasm.Atom; | ||
| 1220 | const Dwarf = @import("../Dwarf.zig"); | ||
| 1221 | const InternPool = @import("../../InternPool.zig"); | ||
| 1222 | const Liveness = @import("../../Liveness.zig"); | ||
| 1223 | const Zcu = @import("../../Zcu.zig"); | ||
| 1224 | const Symbol = @import("Symbol.zig"); | ||
| 1225 | const Type = @import("../../Type.zig"); | ||
| 1226 | const Value = @import("../../Value.zig"); | ||
| 1227 | const Wasm = @import("../Wasm.zig"); | ||
| 1228 | const AnalUnit = InternPool.AnalUnit; | ||
| 1229 | const 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 | } |
| 77 | 77 | ||
| 78 | /// Shaming all the locations that inappropriately use an O(N) search algorithm. | ||
| 79 | /// Please delete this and fix the compilation errors! | ||
| 80 | pub const @"bad O(N)" = void; | ||
| 81 | |||
| 78 | const normal_usage = | 82 | const 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"); |
| 14 | 14 | ||
| 15 | const log = std.log.scoped(.register_manager); | 15 | const log = std.log.scoped(.register_manager); |
| 16 | 16 | ||
| 17 | pub const AllocateRegistersError = error{ | 17 | pub 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 integer | 20 | /// 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 codegen | 22 | /// 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 | }; |
| 30 | 25 | ||
| 31 | pub fn RegisterManager( | 26 | pub 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); |
| 286 | 281 | ||
| 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 | } |
| 344 | 339 | ||
| ... | @@ -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 a | 350 | // 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 | } |
| 376 | 371 |
test/behavior.zig+10-3| ... | @@ -31,8 +31,6 @@ test { | ... | @@ -31,8 +31,6 @@ test { |
| 31 | _ = @import("behavior/error.zig"); | 31 | _ = @import("behavior/error.zig"); |
| 32 | _ = @import("behavior/eval.zig"); | 32 | _ = @import("behavior/eval.zig"); |
| 33 | _ = @import("behavior/export_builtin.zig"); | 33 | _ = @import("behavior/export_builtin.zig"); |
| 34 | _ = @import("behavior/export_self_referential_type_info.zig"); | ||
| 35 | _ = @import("behavior/extern.zig"); | ||
| 36 | _ = @import("behavior/field_parent_ptr.zig"); | 34 | _ = @import("behavior/field_parent_ptr.zig"); |
| 37 | _ = @import("behavior/floatop.zig"); | 35 | _ = @import("behavior/floatop.zig"); |
| 38 | _ = @import("behavior/fn.zig"); | 36 | _ = @import("behavior/fn.zig"); |
| ... | @@ -45,7 +43,6 @@ test { | ... | @@ -45,7 +43,6 @@ test { |
| 45 | _ = @import("behavior/hasfield.zig"); | 43 | _ = @import("behavior/hasfield.zig"); |
| 46 | _ = @import("behavior/if.zig"); | 44 | _ = @import("behavior/if.zig"); |
| 47 | _ = @import("behavior/import.zig"); | 45 | _ = @import("behavior/import.zig"); |
| 48 | _ = @import("behavior/import_c_keywords.zig"); | ||
| 49 | _ = @import("behavior/incomplete_struct_param_tld.zig"); | 46 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| 50 | _ = @import("behavior/inline_switch.zig"); | 47 | _ = @import("behavior/inline_switch.zig"); |
| 51 | _ = @import("behavior/int128.zig"); | 48 | _ = @import("behavior/int128.zig"); |
| ... | @@ -127,6 +124,16 @@ test { | ... | @@ -127,6 +124,16 @@ test { |
| 127 | { | 124 | { |
| 128 | _ = @import("behavior/export_keyword.zig"); | 125 | _ = @import("behavior/export_keyword.zig"); |
| 129 | } | 126 | } |
| 127 | |||
| 128 | if (!builtin.cpu.arch.isWasm()) { | ||
| 129 | // Due to lack of import/export of global support | ||
| 130 | // (https://github.com/ziglang/zig/issues/4866), these tests correctly | ||
| 131 | // cause linker errors, since a data symbol cannot be exported when | ||
| 132 | // building an executable. | ||
| 133 | _ = @import("behavior/export_self_referential_type_info.zig"); | ||
| 134 | _ = @import("behavior/extern.zig"); | ||
| 135 | _ = @import("behavior/import_c_keywords.zig"); | ||
| 136 | } | ||
| 130 | } | 137 | } |
| 131 | 138 | ||
| 132 | // This bug only repros in the root file | 139 | // This bug only repros in the root file |
test/behavior/export_builtin.zig+15-1| ... | @@ -6,6 +6,11 @@ test "exporting enum value" { | ... | @@ -6,6 +6,11 @@ test "exporting enum value" { |
| 6 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 6 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 8 | 8 | ||
| 9 | if (builtin.cpu.arch.isWasm()) { | ||
| 10 | // https://github.com/ziglang/zig/issues/4866 | ||
| 11 | return error.SkipZigTest; | ||
| 12 | } | ||
| 13 | |||
| 9 | const S = struct { | 14 | const S = struct { |
| 10 | const E = enum(c_int) { one, two }; | 15 | const E = enum(c_int) { one, two }; |
| 11 | const e: E = .two; | 16 | const e: E = .two; |
| ... | @@ -33,6 +38,11 @@ test "exporting using namespace access" { | ... | @@ -33,6 +38,11 @@ test "exporting using namespace access" { |
| 33 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 34 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 39 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 35 | 40 | ||
| 41 | if (builtin.cpu.arch.isWasm()) { | ||
| 42 | // https://github.com/ziglang/zig/issues/4866 | ||
| 43 | return error.SkipZigTest; | ||
| 44 | } | ||
| 45 | |||
| 36 | const S = struct { | 46 | const S = struct { |
| 37 | const Inner = struct { | 47 | const Inner = struct { |
| 38 | const x: u32 = 5; | 48 | const x: u32 = 5; |
| ... | @@ -46,7 +56,6 @@ test "exporting using namespace access" { | ... | @@ -46,7 +56,6 @@ test "exporting using namespace access" { |
| 46 | } | 56 | } |
| 47 | 57 | ||
| 48 | test "exporting comptime-known value" { | 58 | test "exporting comptime-known value" { |
| 49 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 50 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 59 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 51 | if (builtin.zig_backend == .stage2_x86_64 and | 60 | if (builtin.zig_backend == .stage2_x86_64 and |
| 52 | (builtin.target.ofmt != .elf and | 61 | (builtin.target.ofmt != .elf and |
| ... | @@ -56,6 +65,11 @@ test "exporting comptime-known value" { | ... | @@ -56,6 +65,11 @@ test "exporting comptime-known value" { |
| 56 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 65 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 57 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | 66 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 58 | 67 | ||
| 68 | if (builtin.cpu.arch.isWasm()) { | ||
| 69 | // https://github.com/ziglang/zig/issues/4866 | ||
| 70 | return error.SkipZigTest; | ||
| 71 | } | ||
| 72 | |||
| 59 | const x: u32 = 10; | 73 | const x: u32 = 10; |
| 60 | @export(&x, .{ .name = "exporting_comptime_known_value_foo" }); | 74 | @export(&x, .{ .name = "exporting_comptime_known_value_foo" }); |
| 61 | const S = struct { | 75 | const S = struct { |
test/incremental/add_decl+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/incremental/add_decl_namespaced+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/incremental/change_generic_line_number+1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=wasm32-wasi-selfhosted | ||
| 2 | #update=initial version | 3 | #update=initial version |
| 3 | #file=main.zig | 4 | #file=main.zig |
| 4 | const std = @import("std"); | 5 | const std = @import("std"); |
test/incremental/change_line_number+1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=wasm32-wasi-selfhosted | ||
| 2 | #update=initial version | 3 | #update=initial version |
| 3 | #file=main.zig | 4 | #file=main.zig |
| 4 | const std = @import("std"); | 5 | const std = @import("std"); |
test/incremental/change_shift_op+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | pub fn main() !void { | 7 | pub fn main() !void { |
test/incremental/change_struct_same_fields+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const S = extern struct { x: u8, y: u8 }; | 7 | const S = extern struct { x: u8, y: u8 }; |
test/incremental/compile_error_then_log+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version with compile error | 5 | #update=initial version with compile error |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | comptime { | 7 | comptime { |
test/incremental/delete_comptime_decls+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | pub fn main() void {} | 7 | pub fn main() void {} |
test/incremental/fix_astgen_failure+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version with error | 5 | #update=initial version with error |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | pub fn main() !void { | 7 | pub fn main() !void { |
test/incremental/hello+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/incremental/modify_inline_fn+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/incremental/move_src+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/incremental/recursive_function_becomes_non_recursive+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | pub fn main() !void { | 7 | pub fn main() !void { |
test/incremental/remove_enum_field+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const MyEnum = enum(u8) { | 7 | const MyEnum = enum(u8) { |
test/incremental/remove_invalid_union_backing_enum+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const E = enum { a, b, c }; | 7 | const E = enum { a, b, c }; |
test/incremental/temporary_parse_error+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/incremental/type_becomes_comptime_only+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const SomeType = u32; | 7 | const SomeType = u32; |
test/incremental/unreferenced_error+1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | #target=x86_64-linux-selfhosted | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=x86_64-linux-cbe | 2 | #target=x86_64-linux-cbe |
| 3 | #target=x86_64-windows-cbe | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | ||
| 4 | #update=initial version | 5 | #update=initial version |
| 5 | #file=main.zig | 6 | #file=main.zig |
| 6 | const std = @import("std"); | 7 | const std = @import("std"); |
test/link/build.zig.zon-6| ... | @@ -24,9 +24,6 @@ | ... | @@ -24,9 +24,6 @@ |
| 24 | .wasm_basic_features = .{ | 24 | .wasm_basic_features = .{ |
| 25 | .path = "wasm/basic-features", | 25 | .path = "wasm/basic-features", |
| 26 | }, | 26 | }, |
| 27 | .wasm_bss = .{ | ||
| 28 | .path = "wasm/bss", | ||
| 29 | }, | ||
| 30 | .wasm_export = .{ | 27 | .wasm_export = .{ |
| 31 | .path = "wasm/export", | 28 | .path = "wasm/export", |
| 32 | }, | 29 | }, |
| ... | @@ -48,9 +45,6 @@ | ... | @@ -48,9 +45,6 @@ |
| 48 | .wasm_producers = .{ | 45 | .wasm_producers = .{ |
| 49 | .path = "wasm/producers", | 46 | .path = "wasm/producers", |
| 50 | }, | 47 | }, |
| 51 | .wasm_segments = .{ | ||
| 52 | .path = "wasm/segments", | ||
| 53 | }, | ||
| 54 | .wasm_shared_memory = .{ | 48 | .wasm_shared_memory = .{ |
| 55 | .path = "wasm/shared-memory", | 49 | .path = "wasm/shared-memory", |
| 56 | }, | 50 | }, |
test/link/wasm/archive/build.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | 4 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
test/link/wasm/basic-features/build.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | // Library with explicitly set cpu features | 4 | // Library with explicitly set cpu features |
| 7 | const lib = b.addExecutable(.{ | 5 | const lib = b.addExecutable(.{ |
test/link/wasm/bss/build.zig deleted-95| ... | @@ -1,95 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | ||
| 6 | const test_step = b.step("test", "Test"); | ||
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 9 | add(b, test_step, .Debug, true); | ||
| 10 | add(b, test_step, .ReleaseFast, false); | ||
| 11 | add(b, test_step, .ReleaseSmall, false); | ||
| 12 | add(b, test_step, .ReleaseSafe, true); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode, is_safe: bool) void { | ||
| 16 | { | ||
| 17 | const lib = b.addExecutable(.{ | ||
| 18 | .name = "lib", | ||
| 19 | .root_module = b.createModule(.{ | ||
| 20 | .root_source_file = b.path("lib.zig"), | ||
| 21 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | ||
| 22 | .optimize = optimize_mode, | ||
| 23 | .strip = false, | ||
| 24 | }), | ||
| 25 | }); | ||
| 26 | lib.entry = .disabled; | ||
| 27 | lib.use_llvm = false; | ||
| 28 | lib.use_lld = false; | ||
| 29 | // to make sure the bss segment is emitted, we must import memory | ||
| 30 | lib.import_memory = true; | ||
| 31 | lib.link_gc_sections = false; | ||
| 32 | |||
| 33 | const check_lib = lib.checkObject(); | ||
| 34 | |||
| 35 | // since we import memory, make sure it exists with the correct naming | ||
| 36 | check_lib.checkInHeaders(); | ||
| 37 | check_lib.checkExact("Section import"); | ||
| 38 | check_lib.checkExact("entries 1"); | ||
| 39 | check_lib.checkExact("module env"); // default module name is "env" | ||
| 40 | check_lib.checkExact("name memory"); // as per linker specification | ||
| 41 | |||
| 42 | // since we are importing memory, ensure it's not exported | ||
| 43 | check_lib.checkInHeaders(); | ||
| 44 | check_lib.checkNotPresent("Section export"); | ||
| 45 | |||
| 46 | // validate the name of the stack pointer | ||
| 47 | check_lib.checkInHeaders(); | ||
| 48 | check_lib.checkExact("Section custom"); | ||
| 49 | check_lib.checkExact("type data_segment"); | ||
| 50 | check_lib.checkExact("names 2"); | ||
| 51 | check_lib.checkExact("index 0"); | ||
| 52 | check_lib.checkExact("name .rodata"); | ||
| 53 | // for safe optimization modes `undefined` is stored in data instead of bss. | ||
| 54 | if (is_safe) { | ||
| 55 | check_lib.checkExact("index 1"); | ||
| 56 | check_lib.checkExact("name .data"); | ||
| 57 | check_lib.checkNotPresent("name .bss"); | ||
| 58 | } else { | ||
| 59 | check_lib.checkExact("index 1"); // bss section always last | ||
| 60 | check_lib.checkExact("name .bss"); | ||
| 61 | } | ||
| 62 | test_step.dependOn(&check_lib.step); | ||
| 63 | } | ||
| 64 | |||
| 65 | // verify zero'd declaration is stored in bss for all optimization modes. | ||
| 66 | { | ||
| 67 | const lib = b.addExecutable(.{ | ||
| 68 | .name = "lib", | ||
| 69 | .root_module = b.createModule(.{ | ||
| 70 | .root_source_file = b.path("lib2.zig"), | ||
| 71 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | ||
| 72 | .optimize = optimize_mode, | ||
| 73 | .strip = false, | ||
| 74 | }), | ||
| 75 | }); | ||
| 76 | lib.entry = .disabled; | ||
| 77 | lib.use_llvm = false; | ||
| 78 | lib.use_lld = false; | ||
| 79 | // to make sure the bss segment is emitted, we must import memory | ||
| 80 | lib.import_memory = true; | ||
| 81 | lib.link_gc_sections = false; | ||
| 82 | |||
| 83 | const check_lib = lib.checkObject(); | ||
| 84 | check_lib.checkInHeaders(); | ||
| 85 | check_lib.checkExact("Section custom"); | ||
| 86 | check_lib.checkExact("type data_segment"); | ||
| 87 | check_lib.checkExact("names 2"); | ||
| 88 | check_lib.checkExact("index 0"); | ||
| 89 | check_lib.checkExact("name .rodata"); | ||
| 90 | check_lib.checkExact("index 1"); | ||
| 91 | check_lib.checkExact("name .bss"); | ||
| 92 | |||
| 93 | test_step.dependOn(&check_lib.step); | ||
| 94 | } | ||
| 95 | } | ||
test/link/wasm/bss/lib.zig deleted-5| ... | @@ -1,5 +0,0 @@ | ||
| 1 | pub var bss: u32 = undefined; | ||
| 2 | |||
| 3 | export fn foo() void { | ||
| 4 | _ = bss; | ||
| 5 | } | ||
test/link/wasm/bss/lib2.zig deleted-5| ... | @@ -1,5 +0,0 @@ | ||
| 1 | pub var bss: u32 = 0; | ||
| 2 | |||
| 3 | export fn foo() void { | ||
| 4 | _ = bss; | ||
| 5 | } | ||
test/link/wasm/export-data/build.zig+9-33| ... | @@ -4,48 +4,24 @@ pub fn build(b: *std.Build) void { | ... | @@ -4,48 +4,24 @@ pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 5 | b.default_step = test_step; | 5 | b.default_step = test_step; |
| 6 | 6 | ||
| 7 | if (@import("builtin").os.tag == .windows) { | ||
| 8 | // TODO: Fix open handle in wasm-linker refraining rename from working on Windows. | ||
| 9 | return; | ||
| 10 | } | ||
| 11 | |||
| 12 | const lib = b.addExecutable(.{ | 7 | const lib = b.addExecutable(.{ |
| 13 | .name = "lib", | 8 | .name = "lib", |
| 14 | .root_module = b.createModule(.{ | 9 | .root_module = b.createModule(.{ |
| 15 | .root_source_file = b.path("lib.zig"), | 10 | .root_source_file = b.path("lib.zig"), |
| 16 | .optimize = .ReleaseSafe, // to make the output deterministic in address positions | 11 | .optimize = .Debug, |
| 17 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | 12 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), |
| 18 | }), | 13 | }), |
| 19 | }); | 14 | }); |
| 20 | lib.entry = .disabled; | 15 | lib.entry = .disabled; |
| 21 | lib.use_lld = false; | 16 | lib.use_lld = false; |
| 22 | lib.root_module.export_symbol_names = &.{ "foo", "bar" }; | 17 | lib.root_module.export_symbol_names = &.{ "foo", "bar" }; |
| 23 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse | 18 | // Object being linked has neither functions nor globals named "foo" or "bar" and |
| 24 | 19 | // so these names correctly fail to be exported when creating an executable. | |
| 25 | const check_lib = lib.checkObject(); | 20 | lib.expect_errors = .{ .exact = &.{ |
| 26 | 21 | "error: manually specified export name 'foo' undefined", | |
| 27 | check_lib.checkInHeaders(); | 22 | "error: manually specified export name 'bar' undefined", |
| 28 | check_lib.checkExact("Section global"); | 23 | } }; |
| 29 | check_lib.checkExact("entries 3"); | 24 | _ = lib.getEmittedBin(); |
| 30 | check_lib.checkExact("type i32"); // stack pointer so skip other fields | ||
| 31 | check_lib.checkExact("type i32"); | ||
| 32 | check_lib.checkExact("mutable false"); | ||
| 33 | check_lib.checkExtract("i32.const {foo_address}"); | ||
| 34 | check_lib.checkExact("type i32"); | ||
| 35 | check_lib.checkExact("mutable false"); | ||
| 36 | check_lib.checkExtract("i32.const {bar_address}"); | ||
| 37 | check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 4 } }); | ||
| 38 | check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0 } }); | ||
| 39 | |||
| 40 | check_lib.checkInHeaders(); | ||
| 41 | check_lib.checkExact("Section export"); | ||
| 42 | check_lib.checkExact("entries 3"); | ||
| 43 | check_lib.checkExact("name foo"); | ||
| 44 | check_lib.checkExact("kind global"); | ||
| 45 | check_lib.checkExact("index 1"); | ||
| 46 | check_lib.checkExact("name bar"); | ||
| 47 | check_lib.checkExact("kind global"); | ||
| 48 | check_lib.checkExact("index 2"); | ||
| 49 | 25 | ||
| 50 | test_step.dependOn(&check_lib.step); | 26 | test_step.dependOn(&lib.step); |
| 51 | } | 27 | } |
test/link/wasm/export/build.zig+2-7| ... | @@ -1,22 +1,17 @@ | ... | @@ -1,22 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | 4 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
| 8 | 6 | ||
| 9 | add(b, test_step, .Debug); | 7 | add(b, test_step, .Debug); |
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | 8 | } |
| 14 | 9 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | 10 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const no_export = b.addExecutable(.{ | 11 | const no_export = b.addExecutable(.{ |
| 17 | .name = "no-export", | 12 | .name = "no-export", |
| 18 | .root_module = b.createModule(.{ | 13 | .root_module = b.createModule(.{ |
| 19 | .root_source_file = b.path("main.zig"), | 14 | .root_source_file = b.path("main-hidden.zig"), |
| 20 | .optimize = optimize, | 15 | .optimize = optimize, |
| 21 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | 16 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), |
| 22 | }), | 17 | }), |
| ... | @@ -41,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize | ... | @@ -41,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 41 | const force_export = b.addExecutable(.{ | 36 | const force_export = b.addExecutable(.{ |
| 42 | .name = "force", | 37 | .name = "force", |
| 43 | .root_module = b.createModule(.{ | 38 | .root_module = b.createModule(.{ |
| 44 | .root_source_file = b.path("main.zig"), | 39 | .root_source_file = b.path("main-hidden.zig"), |
| 45 | .optimize = optimize, | 40 | .optimize = optimize, |
| 46 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | 41 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), |
| 47 | }), | 42 | }), |
test/link/wasm/export/main-hidden.zig created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | fn foo() callconv(.c) void {} | ||
| 2 | comptime { | ||
| 3 | @export(&foo, .{ .name = "foo", .visibility = .hidden }); | ||
| 4 | } | ||
test/link/wasm/extern/build.zig-5| ... | @@ -1,15 +1,10 @@ | ... | @@ -1,15 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | 4 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
| 8 | 6 | ||
| 9 | add(b, test_step, .Debug); | 7 | add(b, test_step, .Debug); |
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | 8 | } |
| 14 | 9 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | 10 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
test/link/wasm/function-table/build.zig+1-33| ... | @@ -1,32 +1,13 @@ | ... | @@ -1,32 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | 4 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
| 8 | 6 | ||
| 9 | add(b, test_step, .Debug); | 7 | add(b, test_step, .Debug); |
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | 8 | } |
| 14 | 9 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | 10 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const import_table = b.addExecutable(.{ | ||
| 17 | .name = "import_table", | ||
| 18 | .root_module = b.createModule(.{ | ||
| 19 | .root_source_file = b.path("lib.zig"), | ||
| 20 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | ||
| 21 | .optimize = optimize, | ||
| 22 | }), | ||
| 23 | }); | ||
| 24 | import_table.entry = .disabled; | ||
| 25 | import_table.use_llvm = false; | ||
| 26 | import_table.use_lld = false; | ||
| 27 | import_table.import_table = true; | ||
| 28 | import_table.link_gc_sections = false; | ||
| 29 | |||
| 30 | const export_table = b.addExecutable(.{ | 11 | const export_table = b.addExecutable(.{ |
| 31 | .name = "export_table", | 12 | .name = "export_table", |
| 32 | .root_module = b.createModule(.{ | 13 | .root_module = b.createModule(.{ |
| ... | @@ -54,24 +35,12 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize | ... | @@ -54,24 +35,12 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 54 | regular_table.use_lld = false; | 35 | regular_table.use_lld = false; |
| 55 | regular_table.link_gc_sections = false; // Ensure function table is not empty | 36 | regular_table.link_gc_sections = false; // Ensure function table is not empty |
| 56 | 37 | ||
| 57 | const check_import = import_table.checkObject(); | ||
| 58 | const check_export = export_table.checkObject(); | 38 | const check_export = export_table.checkObject(); |
| 59 | const check_regular = regular_table.checkObject(); | 39 | const check_regular = regular_table.checkObject(); |
| 60 | 40 | ||
| 61 | check_import.checkInHeaders(); | ||
| 62 | check_import.checkExact("Section import"); | ||
| 63 | check_import.checkExact("entries 1"); | ||
| 64 | check_import.checkExact("module env"); | ||
| 65 | check_import.checkExact("name __indirect_function_table"); | ||
| 66 | check_import.checkExact("kind table"); | ||
| 67 | check_import.checkExact("type funcref"); | ||
| 68 | check_import.checkExact("min 1"); // 1 function pointer | ||
| 69 | check_import.checkNotPresent("max"); // when importing, we do not provide a max | ||
| 70 | check_import.checkNotPresent("Section table"); // we're importing it | ||
| 71 | |||
| 72 | check_export.checkInHeaders(); | 41 | check_export.checkInHeaders(); |
| 73 | check_export.checkExact("Section export"); | 42 | check_export.checkExact("Section export"); |
| 74 | check_export.checkExact("entries 2"); | 43 | check_export.checkExact("entries 3"); |
| 75 | check_export.checkExact("name __indirect_function_table"); // as per linker specification | 44 | check_export.checkExact("name __indirect_function_table"); // as per linker specification |
| 76 | check_export.checkExact("kind table"); | 45 | check_export.checkExact("kind table"); |
| 77 | 46 | ||
| ... | @@ -89,7 +58,6 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize | ... | @@ -89,7 +58,6 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 89 | check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1 | 58 | check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1 |
| 90 | check_regular.checkExact("indexes 1"); // 1 function pointer | 59 | check_regular.checkExact("indexes 1"); // 1 function pointer |
| 91 | 60 | ||
| 92 | test_step.dependOn(&check_import.step); | ||
| 93 | test_step.dependOn(&check_export.step); | 61 | test_step.dependOn(&check_export.step); |
| 94 | test_step.dependOn(&check_regular.step); | 62 | test_step.dependOn(&check_regular.step); |
| 95 | } | 63 | } |
test/link/wasm/infer-features/build.zig+3-22| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | // Wasm Object file which we will use to infer the features from | 4 | // Wasm Object file which we will use to infer the features from |
| 7 | const c_obj = b.addObject(.{ | 5 | const c_obj = b.addObject(.{ |
| ... | @@ -37,27 +35,10 @@ pub fn build(b: *std.Build) void { | ... | @@ -37,27 +35,10 @@ pub fn build(b: *std.Build) void { |
| 37 | lib.use_lld = false; | 35 | lib.use_lld = false; |
| 38 | lib.root_module.addObject(c_obj); | 36 | lib.root_module.addObject(c_obj); |
| 39 | 37 | ||
| 40 | // Verify the result contains the features from the C Object file. | 38 | lib.expect_errors = .{ .contains = "error: object requires atomics but specified target features exclude atomics" }; |
| 41 | const check = lib.checkObject(); | 39 | _ = lib.getEmittedBin(); |
| 42 | check.checkInHeaders(); | ||
| 43 | check.checkExact("name target_features"); | ||
| 44 | check.checkExact("features 14"); | ||
| 45 | check.checkExact("+ atomics"); | ||
| 46 | check.checkExact("+ bulk-memory"); | ||
| 47 | check.checkExact("+ exception-handling"); | ||
| 48 | check.checkExact("+ extended-const"); | ||
| 49 | check.checkExact("+ half-precision"); | ||
| 50 | check.checkExact("+ multimemory"); | ||
| 51 | check.checkExact("+ multivalue"); | ||
| 52 | check.checkExact("+ mutable-globals"); | ||
| 53 | check.checkExact("+ nontrapping-fptoint"); | ||
| 54 | check.checkExact("+ reference-types"); | ||
| 55 | check.checkExact("+ relaxed-simd"); | ||
| 56 | check.checkExact("+ sign-ext"); | ||
| 57 | check.checkExact("+ simd128"); | ||
| 58 | check.checkExact("+ tail-call"); | ||
| 59 | 40 | ||
| 60 | const test_step = b.step("test", "Run linker test"); | 41 | const test_step = b.step("test", "Run linker test"); |
| 61 | test_step.dependOn(&check.step); | 42 | test_step.dependOn(&lib.step); |
| 62 | b.default_step = test_step; | 43 | b.default_step = test_step; |
| 63 | } | 44 | } |
test/link/wasm/producers/build.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub const requires_stage2 = true; | ||
| 5 | |||
| 6 | pub fn build(b: *std.Build) void { | 4 | pub fn build(b: *std.Build) void { |
| 7 | const test_step = b.step("test", "Test it"); | 5 | const test_step = b.step("test", "Test it"); |
| 8 | b.default_step = test_step; | 6 | b.default_step = test_step; |
test/link/wasm/segments/build.zig deleted-48| ... | @@ -1,48 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | ||
| 6 | const test_step = b.step("test", "Test it"); | ||
| 7 | b.default_step = test_step; | ||
| 8 | |||
| 9 | add(b, test_step, .Debug); | ||
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | ||
| 16 | const lib = b.addExecutable(.{ | ||
| 17 | .name = "lib", | ||
| 18 | .root_module = b.createModule(.{ | ||
| 19 | .root_source_file = b.path("lib.zig"), | ||
| 20 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), | ||
| 21 | .optimize = optimize, | ||
| 22 | .strip = false, | ||
| 23 | }), | ||
| 24 | }); | ||
| 25 | lib.entry = .disabled; | ||
| 26 | lib.use_llvm = false; | ||
| 27 | lib.use_lld = false; | ||
| 28 | lib.link_gc_sections = false; // so data is not garbage collected and we can verify data section | ||
| 29 | b.installArtifact(lib); | ||
| 30 | |||
| 31 | const check_lib = lib.checkObject(); | ||
| 32 | check_lib.checkInHeaders(); | ||
| 33 | check_lib.checkExact("Section data"); | ||
| 34 | check_lib.checkExact("entries 2"); // rodata & data, no bss because we're exporting memory | ||
| 35 | |||
| 36 | check_lib.checkInHeaders(); | ||
| 37 | check_lib.checkExact("Section custom"); | ||
| 38 | check_lib.checkInHeaders(); | ||
| 39 | check_lib.checkExact("name name"); // names custom section | ||
| 40 | check_lib.checkInHeaders(); | ||
| 41 | check_lib.checkExact("type data_segment"); | ||
| 42 | check_lib.checkExact("names 2"); | ||
| 43 | check_lib.checkExact("index 0"); | ||
| 44 | check_lib.checkExact("name .rodata"); | ||
| 45 | check_lib.checkExact("index 1"); | ||
| 46 | check_lib.checkExact("name .data"); | ||
| 47 | test_step.dependOn(&check_lib.step); | ||
| 48 | } | ||
test/link/wasm/segments/lib.zig deleted-9| ... | @@ -1,9 +0,0 @@ | ||
| 1 | pub const rodata: u32 = 5; | ||
| 2 | pub var data: u32 = 10; | ||
| 3 | pub var bss: u32 = undefined; | ||
| 4 | |||
| 5 | export fn foo() void { | ||
| 6 | _ = rodata; | ||
| 7 | _ = data; | ||
| 8 | _ = bss; | ||
| 9 | } | ||
test/link/wasm/shared-memory/build.zig+11-8| ... | @@ -6,8 +6,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -6,8 +6,6 @@ pub fn build(b: *std.Build) void { |
| 6 | 6 | ||
| 7 | add(b, test_step, .Debug); | 7 | add(b, test_step, .Debug); |
| 8 | add(b, test_step, .ReleaseFast); | 8 | add(b, test_step, .ReleaseFast); |
| 9 | add(b, test_step, .ReleaseSmall); | ||
| 10 | add(b, test_step, .ReleaseSafe); | ||
| 11 | } | 9 | } |
| 12 | 10 | ||
| 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void { | 11 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void { |
| ... | @@ -45,6 +43,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt | ... | @@ -45,6 +43,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt |
| 45 | check_exe.checkInHeaders(); | 43 | check_exe.checkInHeaders(); |
| 46 | check_exe.checkExact("Section export"); | 44 | check_exe.checkExact("Section export"); |
| 47 | check_exe.checkExact("entries 2"); | 45 | check_exe.checkExact("entries 2"); |
| 46 | check_exe.checkExact("name foo"); | ||
| 48 | check_exe.checkExact("name memory"); // ensure we also export memory again | 47 | check_exe.checkExact("name memory"); // ensure we also export memory again |
| 49 | 48 | ||
| 50 | // This section *must* be emit as the start function is set to the index | 49 | // This section *must* be emit as the start function is set to the index |
| ... | @@ -71,23 +70,27 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt | ... | @@ -71,23 +70,27 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt |
| 71 | check_exe.checkExact("type function"); | 70 | check_exe.checkExact("type function"); |
| 72 | if (optimize_mode == .Debug) { | 71 | if (optimize_mode == .Debug) { |
| 73 | check_exe.checkExact("name __wasm_init_memory"); | 72 | check_exe.checkExact("name __wasm_init_memory"); |
| 73 | check_exe.checkExact("name __wasm_init_tls"); | ||
| 74 | } | 74 | } |
| 75 | check_exe.checkExact("name __wasm_init_tls"); | ||
| 76 | check_exe.checkExact("type global"); | 75 | check_exe.checkExact("type global"); |
| 77 | 76 | ||
| 78 | // In debug mode the symbol __tls_base is resolved to an undefined symbol | 77 | // In debug mode the symbol __tls_base is resolved to an undefined symbol |
| 79 | // from the object file, hence its placement differs than in release modes | 78 | // from the object file, hence its placement differs than in release modes |
| 80 | // where the entire tls segment is optimized away, and tls_base will have | 79 | // where the entire tls segment is optimized away, and tls_base will have |
| 81 | // its original position. | 80 | // its original position. |
| 82 | check_exe.checkExact("name __tls_base"); | ||
| 83 | check_exe.checkExact("name __tls_size"); | ||
| 84 | check_exe.checkExact("name __tls_align"); | ||
| 85 | |||
| 86 | check_exe.checkExact("type data_segment"); | ||
| 87 | if (optimize_mode == .Debug) { | 81 | if (optimize_mode == .Debug) { |
| 82 | check_exe.checkExact("name __tls_base"); | ||
| 83 | check_exe.checkExact("name __tls_size"); | ||
| 84 | check_exe.checkExact("name __tls_align"); | ||
| 85 | |||
| 86 | check_exe.checkExact("type data_segment"); | ||
| 88 | check_exe.checkExact("names 1"); | 87 | check_exe.checkExact("names 1"); |
| 89 | check_exe.checkExact("index 0"); | 88 | check_exe.checkExact("index 0"); |
| 90 | check_exe.checkExact("name .tdata"); | 89 | check_exe.checkExact("name .tdata"); |
| 90 | } else { | ||
| 91 | check_exe.checkNotPresent("name __tls_base"); | ||
| 92 | check_exe.checkNotPresent("name __tls_size"); | ||
| 93 | check_exe.checkNotPresent("name __tls_align"); | ||
| 91 | } | 94 | } |
| 92 | 95 | ||
| 93 | test_step.dependOn(&check_exe.step); | 96 | test_step.dependOn(&check_exe.step); |
test/link/wasm/stack_pointer/build.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | 4 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
test/link/wasm/type/build.zig-5| ... | @@ -1,15 +1,10 @@ | ... | @@ -1,15 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test it"); | 4 | const test_step = b.step("test", "Test it"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
| 8 | 6 | ||
| 9 | add(b, test_step, .Debug); | 7 | add(b, test_step, .Debug); |
| 10 | add(b, test_step, .ReleaseFast); | ||
| 11 | add(b, test_step, .ReleaseSmall); | ||
| 12 | add(b, test_step, .ReleaseSafe); | ||
| 13 | } | 8 | } |
| 14 | 9 | ||
| 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { | 10 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
test/standalone/test_runner_path/build.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub const requires_stage2 = true; | ||
| 4 | |||
| 5 | pub fn build(b: *std.Build) void { | 3 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test the program"); | 4 | const test_step = b.step("test", "Test the program"); |
| 7 | b.default_step = test_step; | 5 | b.default_step = test_step; |
test/tests.zig+5| ... | @@ -1375,6 +1375,7 @@ const ModuleTestOptions = struct { | ... | @@ -1375,6 +1375,7 @@ const ModuleTestOptions = struct { |
| 1375 | skip_single_threaded: bool, | 1375 | skip_single_threaded: bool, |
| 1376 | skip_non_native: bool, | 1376 | skip_non_native: bool, |
| 1377 | skip_libc: bool, | 1377 | skip_libc: bool, |
| 1378 | use_llvm: ?bool = null, | ||
| 1378 | max_rss: usize = 0, | 1379 | max_rss: usize = 0, |
| 1379 | no_builtin: bool = false, | 1380 | no_builtin: bool = false, |
| 1380 | build_options: ?*std.Build.Step.Options = null, | 1381 | build_options: ?*std.Build.Step.Options = null, |
| ... | @@ -1411,6 +1412,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { | ... | @@ -1411,6 +1412,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 1411 | if (options.skip_single_threaded and test_target.single_threaded == true) | 1412 | if (options.skip_single_threaded and test_target.single_threaded == true) |
| 1412 | continue; | 1413 | continue; |
| 1413 | 1414 | ||
| 1415 | if (options.use_llvm) |use_llvm| { | ||
| 1416 | if (test_target.use_llvm != use_llvm) continue; | ||
| 1417 | } | ||
| 1418 | |||
| 1414 | // TODO get compiler-rt tests passing for self-hosted backends. | 1419 | // TODO get compiler-rt tests passing for self-hosted backends. |
| 1415 | if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and | 1420 | if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and |
| 1416 | test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) | 1421 | test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) |