| 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 | 643 | src/link/StringTable.zig |
| 644 | 644 | src/link/Wasm.zig |
| 645 | 645 | src/link/Wasm/Archive.zig |
| 646 | src/link/Wasm/Flush.zig | |
| 646 | 647 | src/link/Wasm/Object.zig |
| 647 | src/link/Wasm/Symbol.zig | |
| 648 | src/link/Wasm/ZigObject.zig | |
| 649 | 648 | src/link/aarch64.zig |
| 650 | 649 | src/link/riscv.zig |
| 651 | 650 | src/link/table_section.zig |
build.zig+5| ... | ... | @@ -447,6 +447,7 @@ pub fn build(b: *std.Build) !void { |
| 447 | 447 | .skip_single_threaded = skip_single_threaded, |
| 448 | 448 | .skip_non_native = skip_non_native, |
| 449 | 449 | .skip_libc = skip_libc, |
| 450 | .use_llvm = use_llvm, | |
| 450 | 451 | .max_rss = 1 * 1024 * 1024 * 1024, |
| 451 | 452 | })); |
| 452 | 453 | |
| ... | ... | @@ -462,6 +463,7 @@ pub fn build(b: *std.Build) !void { |
| 462 | 463 | .skip_single_threaded = true, |
| 463 | 464 | .skip_non_native = skip_non_native, |
| 464 | 465 | .skip_libc = skip_libc, |
| 466 | .use_llvm = use_llvm, | |
| 465 | 467 | })); |
| 466 | 468 | |
| 467 | 469 | test_modules_step.dependOn(tests.addModuleTests(b, .{ |
| ... | ... | @@ -476,6 +478,7 @@ pub fn build(b: *std.Build) !void { |
| 476 | 478 | .skip_single_threaded = true, |
| 477 | 479 | .skip_non_native = skip_non_native, |
| 478 | 480 | .skip_libc = true, |
| 481 | .use_llvm = use_llvm, | |
| 479 | 482 | .no_builtin = true, |
| 480 | 483 | })); |
| 481 | 484 | |
| ... | ... | @@ -491,6 +494,7 @@ pub fn build(b: *std.Build) !void { |
| 491 | 494 | .skip_single_threaded = true, |
| 492 | 495 | .skip_non_native = skip_non_native, |
| 493 | 496 | .skip_libc = true, |
| 497 | .use_llvm = use_llvm, | |
| 494 | 498 | .no_builtin = true, |
| 495 | 499 | })); |
| 496 | 500 | |
| ... | ... | @@ -506,6 +510,7 @@ pub fn build(b: *std.Build) !void { |
| 506 | 510 | .skip_single_threaded = skip_single_threaded, |
| 507 | 511 | .skip_non_native = skip_non_native, |
| 508 | 512 | .skip_libc = skip_libc, |
| 513 | .use_llvm = use_llvm, | |
| 509 | 514 | // I observed a value of 4572626944 on the M2 CI. |
| 510 | 515 | .max_rss = 5029889638, |
| 511 | 516 | })); |
lib/std/Build/Step/CheckObject.zig+17-4| ... | ... | @@ -2424,7 +2424,22 @@ const WasmDumper = struct { |
| 2424 | 2424 | } |
| 2425 | 2425 | |
| 2426 | 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 | 2443 | const writer = output.writer(); |
| 2429 | 2444 | |
| 2430 | 2445 | switch (check.kind) { |
| ... | ... | @@ -2442,8 +2457,6 @@ const WasmDumper = struct { |
| 2442 | 2457 | |
| 2443 | 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 | 2462 | fn parseAndDumpSection( |
| ... | ... | @@ -2682,7 +2695,7 @@ const WasmDumper = struct { |
| 2682 | 2695 | else => unreachable, |
| 2683 | 2696 | } |
| 2684 | 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 | 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 | 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 | 1228 | pub fn isEnabled(set: Set, arch_feature_index: Index) bool { |
| 1223 | 1229 | const usize_index = arch_feature_index / @bitSizeOf(usize); |
| 1224 | 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 | 1018 | return .{ .thread = &instance.thread }; |
| 1019 | 1019 | } |
| 1020 | 1020 | |
| 1021 | /// Bootstrap procedure, called by the host environment after thread creation. | |
| 1022 | export fn wasi_thread_start(tid: i32, arg: *Instance) void { | |
| 1023 | if (builtin.single_threaded) { | |
| 1024 | // ensure function is not analyzed in single-threaded mode | |
| 1025 | return; | |
| 1021 | comptime { | |
| 1022 | if (!builtin.single_threaded) { | |
| 1023 | @export(wasi_thread_start, .{ .name = "wasi_thread_start" }); | |
| 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 | 1030 | __set_stack_pointer(arg.thread.memory.ptr + arg.stack_offset); |
| 1028 | 1031 | __wasm_init_tls(arg.thread.memory.ptr + arg.tls_offset); |
| 1029 | 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 | 641 | return self; |
| 642 | 642 | } |
| 643 | 643 | |
| 644 | /// An empty `value_list` may be passed, in which case the values array becomes `undefined`. | |
| 644 | 645 | pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void { |
| 645 | 646 | try self.entries.resize(gpa, key_list.len); |
| 646 | 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 | 651 | assert(key_list.len == value_list.len); |
| 649 | 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 | 267 | /// Never invalidates element pointers. |
| 268 | 268 | /// Asserts that the list can hold one additional item. |
| 269 | 269 | pub fn appendAssumeCapacity(self: *Self, item: T) void { |
| 270 | const new_item_ptr = self.addOneAssumeCapacity(); | |
| 271 | new_item_ptr.* = item; | |
| 270 | self.addOneAssumeCapacity().* = item; | |
| 272 | 271 | } |
| 273 | 272 | |
| 274 | 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 | 878 | /// Never invalidates element pointers. |
| 880 | 879 | /// Asserts that the list can hold one additional item. |
| 881 | 880 | pub fn appendAssumeCapacity(self: *Self, item: T) void { |
| 882 | const new_item_ptr = self.addOneAssumeCapacity(); | |
| 883 | new_item_ptr.* = item; | |
| 881 | self.addOneAssumeCapacity().* = item; | |
| 884 | 882 | } |
| 885 | 883 | |
| 886 | 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 | 16 | |
| 17 | 17 | fn getStdOutHandle() posix.fd_t { |
| 18 | 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 | 19 | return windows.peb().ProcessParameters.hStdOutput; |
| 24 | 20 | } |
| 25 | 21 | |
| ... | ... | @@ -36,10 +32,6 @@ pub fn getStdOut() File { |
| 36 | 32 | |
| 37 | 33 | fn getStdErrHandle() posix.fd_t { |
| 38 | 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 | 35 | return windows.peb().ProcessParameters.hStdError; |
| 44 | 36 | } |
| 45 | 37 | |
| ... | ... | @@ -56,10 +48,6 @@ pub fn getStdErr() File { |
| 56 | 48 | |
| 57 | 49 | fn getStdInHandle() posix.fd_t { |
| 58 | 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 | 51 | return windows.peb().ProcessParameters.hStdInput; |
| 64 | 52 | } |
| 65 | 53 |
lib/std/wasm.zig+17-180| ... | ... | @@ -4,8 +4,6 @@ |
| 4 | 4 | const std = @import("std.zig"); |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | |
| 7 | // TODO: Add support for multi-byte ops (e.g. table operations) | |
| 8 | ||
| 9 | 7 | /// Wasm instruction opcodes |
| 10 | 8 | /// |
| 11 | 9 | /// All instructions are defined as per spec: |
| ... | ... | @@ -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 | 196 | /// Opcodes that require a prefix `0xFC`. |
| 220 | 197 | /// Each opcode represents a varuint32, meaning |
| 221 | 198 | /// they are encoded as leb128 in binary. |
| ... | ... | @@ -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 | 221 | /// Simd opcodes that require a prefix `0xFD`. |
| 251 | 222 | /// Each opcode represents a varuint32, meaning |
| 252 | 223 | /// they are encoded as leb128 in binary. |
| ... | ... | @@ -512,12 +483,6 @@ pub const SimdOpcode = enum(u32) { |
| 512 | 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 | 486 | /// Atomic opcodes that require a prefix `0xFE`. |
| 522 | 487 | /// Each opcode represents a varuint32, meaning |
| 523 | 488 | /// they are encoded as leb128 in binary. |
| ... | ... | @@ -592,12 +557,6 @@ pub const AtomicsOpcode = enum(u32) { |
| 592 | 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 | 560 | /// Enum representing all Wasm value types as per spec: |
| 602 | 561 | /// https://webassembly.github.io/spec/core/binary/types.html |
| 603 | 562 | pub const Valtype = enum(u8) { |
| ... | ... | @@ -608,11 +567,6 @@ pub const Valtype = enum(u8) { |
| 608 | 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 | 570 | /// Reference types, where the funcref references to a function regardless of its type |
| 617 | 571 | /// and ref references an object from the embedder. |
| 618 | 572 | pub const RefType = enum(u8) { |
| ... | ... | @@ -620,41 +574,17 @@ pub const RefType = enum(u8) { |
| 620 | 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 | 577 | /// Limits classify the size range of resizeable storage associated with memory types and table types. |
| 641 | 578 | pub const Limits = struct { |
| 642 | flags: u8, | |
| 579 | flags: Flags, | |
| 643 | 580 | min: u32, |
| 644 | 581 | max: u32, |
| 645 | 582 | |
| 646 | pub const Flags = enum(u8) { | |
| 647 | WASM_LIMITS_FLAG_HAS_MAX = 0x1, | |
| 648 | WASM_LIMITS_FLAG_IS_SHARED = 0x2, | |
| 583 | pub const Flags = packed struct(u8) { | |
| 584 | has_max: bool, | |
| 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 | 590 | /// Initialization expressions are used to set the initial value on an object |
| ... | ... | @@ -667,18 +597,6 @@ pub const InitExpression = union(enum) { |
| 667 | 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 | 600 | /// Describes the layout of the memory where `min` represents |
| 683 | 601 | /// the minimal amount of pages, and the optional `max` represents |
| 684 | 602 | /// the max pages. When `null` will allow the host to determine the |
| ... | ... | @@ -687,88 +605,6 @@ pub const Memory = struct { |
| 687 | 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 | 608 | /// Wasm module sections as per spec: |
| 773 | 609 | /// https://webassembly.github.io/spec/core/binary/modules.html |
| 774 | 610 | 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 | 627 | /// The kind of the type when importing or exporting to/from the host environment. |
| 797 | 628 | /// https://webassembly.github.io/spec/core/syntax/modules.html |
| 798 | 629 | pub const ExternalKind = enum(u8) { |
| ... | ... | @@ -802,11 +633,6 @@ pub const ExternalKind = enum(u8) { |
| 802 | 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 | 636 | /// Defines the enum values for each subsection id for the "Names" custom section |
| 811 | 637 | /// as described by: |
| 812 | 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 | 655 | pub const result_type: u8 = 0x40; |
| 830 | 656 | |
| 831 | 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 | 671 | // binary constants |
| 835 | 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 | 11 | /// The first thing in this array is an `ErrorMessageList`. |
| 12 | 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 | 19 | /// Special encoding when there are no errors. |
| 15 | 20 | pub const empty: ErrorBundle = .{ |
| 16 | 21 | .string_bytes = &.{}, |
| ... | ... | @@ -33,14 +38,13 @@ pub const ErrorMessageList = struct { |
| 33 | 38 | len: u32, |
| 34 | 39 | start: u32, |
| 35 | 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 | 44 | /// Trailing: |
| 40 | 45 | /// * ReferenceTrace for each reference_trace_len |
| 41 | 46 | pub const SourceLocation = struct { |
| 42 | /// null terminated string index | |
| 43 | src_path: u32, | |
| 47 | src_path: String, | |
| 44 | 48 | line: u32, |
| 45 | 49 | column: u32, |
| 46 | 50 | /// byte offset of starting token |
| ... | ... | @@ -49,17 +53,15 @@ pub const SourceLocation = struct { |
| 49 | 53 | span_main: u32, |
| 50 | 54 | /// byte offset of end of last token |
| 51 | 55 | span_end: u32, |
| 52 | /// null terminated string index, possibly null. | |
| 53 | 56 | /// Does not include the trailing newline. |
| 54 | source_line: u32 = 0, | |
| 57 | source_line: OptionalString = 0, | |
| 55 | 58 | reference_trace_len: u32 = 0, |
| 56 | 59 | }; |
| 57 | 60 | |
| 58 | 61 | /// Trailing: |
| 59 | 62 | /// * MessageIndex for each notes_len. |
| 60 | 63 | pub const ErrorMessage = struct { |
| 61 | /// null terminated string index | |
| 62 | msg: u32, | |
| 64 | msg: String, | |
| 63 | 65 | /// Usually one, but incremented for redundant messages. |
| 64 | 66 | count: u32 = 1, |
| 65 | 67 | src_loc: SourceLocationIndex = .none, |
| ... | ... | @@ -71,7 +73,7 @@ pub const ReferenceTrace = struct { |
| 71 | 73 | /// Except for the sentinel ReferenceTrace element, in which case: |
| 72 | 74 | /// * 0 means remaining references hidden |
| 73 | 75 | /// * >0 means N references hidden |
| 74 | decl_name: u32, | |
| 76 | decl_name: String, | |
| 75 | 77 | /// Index into extra of a SourceLocation |
| 76 | 78 | /// If this is 0, this is the sentinel ReferenceTrace element. |
| 77 | 79 | src_loc: SourceLocationIndex, |
| ... | ... | @@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T, |
| 138 | 140 | } |
| 139 | 141 | |
| 140 | 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 | 144 | const string_bytes = eb.string_bytes; |
| 143 | 145 | var end: usize = index; |
| 144 | 146 | while (string_bytes[end] != 0) { |
| ... | ... | @@ -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 | 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 | 392 | try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); |
| 391 | 393 | wip.string_bytes.appendSliceAssumeCapacity(s); |
| 392 | 394 | wip.string_bytes.appendAssumeCapacity(0); |
| 393 | 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 | 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 | 401 | try wip.string_bytes.writer(gpa).print(fmt, args); |
| 400 | 402 | try wip.string_bytes.append(gpa, 0); |
| 401 | 403 | return index; |
src/Compilation.zig+164-8| ... | ... | @@ -113,6 +113,14 @@ link_diags: link.Diags, |
| 113 | 113 | link_task_queue: ThreadSafeQueue(link.Task) = .empty, |
| 114 | 114 | /// Ensure only 1 simultaneous call to `flushTaskQueue`. |
| 115 | 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 | 125 | work_queues: [ |
| 118 | 126 | len: { |
| ... | ... | @@ -1515,6 +1523,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1515 | 1523 | .file_system_inputs = options.file_system_inputs, |
| 1516 | 1524 | .parent_whole_cache = options.parent_whole_cache, |
| 1517 | 1525 | .link_diags = .init(gpa), |
| 1526 | .remaining_prelink_tasks = 0, | |
| 1518 | 1527 | }; |
| 1519 | 1528 | |
| 1520 | 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 | 1596 | .pdb_source_path = options.pdb_source_path, |
| 1588 | 1597 | .pdb_out_path = options.pdb_out_path, |
| 1589 | 1598 | .entry_addr = null, // CLI does not expose this option (yet?) |
| 1599 | .object_host_name = "env", | |
| 1590 | 1600 | }; |
| 1591 | 1601 | |
| 1592 | 1602 | switch (options.cache_mode) { |
| ... | ... | @@ -1715,6 +1725,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1715 | 1725 | }; |
| 1716 | 1726 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); |
| 1717 | 1727 | } |
| 1728 | comp.remaining_prelink_tasks += @intCast(comp.c_object_table.count()); | |
| 1718 | 1729 | |
| 1719 | 1730 | // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`. |
| 1720 | 1731 | const win32_resource_count = |
| ... | ... | @@ -1722,6 +1733,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1722 | 1733 | if (win32_resource_count > 0) { |
| 1723 | 1734 | dev.check(.win32_resource); |
| 1724 | 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 | 1740 | for (options.rc_source_files) |rc_source_file| { |
| 1726 | 1741 | const win32_resource = try gpa.create(Win32Resource); |
| 1727 | 1742 | errdefer gpa.destroy(win32_resource); |
| ... | ... | @@ -1732,6 +1747,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1732 | 1747 | }; |
| 1733 | 1748 | comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {}); |
| 1734 | 1749 | } |
| 1750 | ||
| 1735 | 1751 | if (options.manifest_file) |manifest_path| { |
| 1736 | 1752 | const win32_resource = try gpa.create(Win32Resource); |
| 1737 | 1753 | errdefer gpa.destroy(win32_resource); |
| ... | ... | @@ -1779,10 +1795,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1779 | 1795 | inline for (fields) |field| { |
| 1780 | 1796 | if (@field(paths, field.name)) |path| { |
| 1781 | 1797 | comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); |
| 1798 | comp.remaining_prelink_tasks += 1; | |
| 1782 | 1799 | } |
| 1783 | 1800 | } |
| 1784 | 1801 | // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. |
| 1785 | 1802 | comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc); |
| 1803 | comp.remaining_prelink_tasks += 1; | |
| 1786 | 1804 | } else if (target.isMusl() and !target.isWasm()) { |
| 1787 | 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 | 1809 | .{ .musl_crt_file = .crti_o }, |
| 1792 | 1810 | .{ .musl_crt_file = .crtn_o }, |
| 1793 | 1811 | }); |
| 1812 | comp.remaining_prelink_tasks += 2; | |
| 1794 | 1813 | } |
| 1795 | 1814 | if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { |
| 1796 | 1815 | try comp.queueJobs(&.{.{ .musl_crt_file = f }}); |
| 1816 | comp.remaining_prelink_tasks += 1; | |
| 1797 | 1817 | } |
| 1798 | 1818 | try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) { |
| 1799 | 1819 | .static => .libc_a, |
| 1800 | 1820 | .dynamic => .libc_so, |
| 1801 | 1821 | } }}); |
| 1822 | comp.remaining_prelink_tasks += 1; | |
| 1802 | 1823 | } else if (target.isGnuLibC()) { |
| 1803 | 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 | 1828 | .{ .glibc_crt_file = .crti_o }, |
| 1808 | 1829 | .{ .glibc_crt_file = .crtn_o }, |
| 1809 | 1830 | }); |
| 1831 | comp.remaining_prelink_tasks += 2; | |
| 1810 | 1832 | } |
| 1811 | 1833 | if (glibc.needsCrt0(comp.config.output_mode)) |f| { |
| 1812 | 1834 | try comp.queueJobs(&.{.{ .glibc_crt_file = f }}); |
| 1835 | comp.remaining_prelink_tasks += 1; | |
| 1813 | 1836 | } |
| 1814 | 1837 | try comp.queueJobs(&[_]Job{ |
| 1815 | 1838 | .{ .glibc_shared_objects = {} }, |
| 1816 | 1839 | .{ .glibc_crt_file = .libc_nonshared_a }, |
| 1817 | 1840 | }); |
| 1841 | comp.remaining_prelink_tasks += 1; | |
| 1842 | comp.remaining_prelink_tasks += glibc.sharedObjectsCount(&target); | |
| 1818 | 1843 | } else if (target.isWasm() and target.os.tag == .wasi) { |
| 1819 | 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 | 1847 | try comp.queueJob(.{ |
| 1823 | 1848 | .wasi_libc_crt_file = crt_file, |
| 1824 | 1849 | }); |
| 1850 | comp.remaining_prelink_tasks += 1; | |
| 1825 | 1851 | } |
| 1826 | 1852 | try comp.queueJobs(&[_]Job{ |
| 1827 | 1853 | .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, |
| 1828 | 1854 | .{ .wasi_libc_crt_file = .libc_a }, |
| 1829 | 1855 | }); |
| 1856 | comp.remaining_prelink_tasks += 2; | |
| 1830 | 1857 | } else if (target.isMinGW()) { |
| 1831 | 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 | 1862 | .{ .mingw_crt_file = .mingw32_lib }, |
| 1836 | 1863 | crt_job, |
| 1837 | 1864 | }); |
| 1865 | comp.remaining_prelink_tasks += 2; | |
| 1838 | 1866 | |
| 1839 | 1867 | // When linking mingw-w64 there are some import libs we always need. |
| 1840 | 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 | 1874 | } |
| 1847 | 1875 | } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) { |
| 1848 | 1876 | try comp.queueJob(.{ .zig_libc = {} }); |
| 1877 | comp.remaining_prelink_tasks += 1; | |
| 1849 | 1878 | } else { |
| 1850 | 1879 | return error.LibCUnavailable; |
| 1851 | 1880 | } |
| ... | ... | @@ -1860,13 +1889,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1860 | 1889 | } |
| 1861 | 1890 | if (comp.wantBuildLibUnwindFromSource()) { |
| 1862 | 1891 | try comp.queueJob(.{ .libunwind = {} }); |
| 1892 | comp.remaining_prelink_tasks += 1; | |
| 1863 | 1893 | } |
| 1864 | 1894 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { |
| 1865 | 1895 | try comp.queueJob(.libcxx); |
| 1866 | 1896 | try comp.queueJob(.libcxxabi); |
| 1897 | comp.remaining_prelink_tasks += 2; | |
| 1867 | 1898 | } |
| 1868 | 1899 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) { |
| 1869 | 1900 | try comp.queueJob(.libtsan); |
| 1901 | comp.remaining_prelink_tasks += 1; | |
| 1870 | 1902 | } |
| 1871 | 1903 | |
| 1872 | 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 | 1917 | if (is_exe_or_dyn_lib) { |
| 1886 | 1918 | log.debug("queuing a job to build compiler_rt_lib", .{}); |
| 1887 | 1919 | comp.job_queued_compiler_rt_lib = true; |
| 1920 | comp.remaining_prelink_tasks += 1; | |
| 1888 | 1921 | } else if (output_mode != .Obj) { |
| 1889 | 1922 | log.debug("queuing a job to build compiler_rt_obj", .{}); |
| 1890 | 1923 | // In this case we are making a static library, so we ask |
| 1891 | 1924 | // for a compiler-rt object to put in it. |
| 1892 | 1925 | comp.job_queued_compiler_rt_obj = true; |
| 1926 | comp.remaining_prelink_tasks += 1; | |
| 1893 | 1927 | } |
| 1894 | 1928 | } |
| 1895 | 1929 | |
| 1896 | 1930 | if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) { |
| 1897 | 1931 | log.debug("queuing a job to build libfuzzer", .{}); |
| 1898 | 1932 | comp.job_queued_fuzzer_lib = true; |
| 1933 | comp.remaining_prelink_tasks += 1; | |
| 1899 | 1934 | } |
| 1900 | 1935 | } |
| 1901 | 1936 | |
| 1902 | 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 | 1942 | return comp; |
| 1906 | 1943 | } |
| ... | ... | @@ -1976,6 +2013,7 @@ pub fn destroy(comp: *Compilation) void { |
| 1976 | 2013 | |
| 1977 | 2014 | comp.link_diags.deinit(); |
| 1978 | 2015 | comp.link_task_queue.deinit(gpa); |
| 2016 | comp.link_task_queue_postponed.deinit(gpa); | |
| 1979 | 2017 | |
| 1980 | 2018 | comp.clearMiscFailures(); |
| 1981 | 2019 | |
| ... | ... | @@ -2438,9 +2476,8 @@ fn flush( |
| 2438 | 2476 | if (comp.bin_file) |lf| { |
| 2439 | 2477 | // This is needed before reading the error flags. |
| 2440 | 2478 | lf.flush(arena, tid, prog_node) catch |err| switch (err) { |
| 2441 | error.FlushFailure, error.LinkFailure => {}, // error reported through link_diags.flags | |
| 2442 | error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr | |
| 2443 | else => |e| return e, | |
| 2479 | error.LinkFailure => {}, // Already reported. | |
| 2480 | error.OutOfMemory => return error.OutOfMemory, | |
| 2444 | 2481 | }; |
| 2445 | 2482 | } |
| 2446 | 2483 | |
| ... | ... | @@ -3025,8 +3062,120 @@ pub fn saveState(comp: *Compilation) !void { |
| 3025 | 3062 | //// TODO: compilation errors |
| 3026 | 3063 | //// TODO: namespaces |
| 3027 | 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 | 3179 | var basename_buf: [255]u8 = undefined; |
| 3031 | 3180 | const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{ |
| 3032 | 3181 | comp.root_name, |
| ... | ... | @@ -3209,6 +3358,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3209 | 3358 | if (!zcu.navFileScope(nav).okToReportErrors()) continue; |
| 3210 | 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 | 3365 | for (zcu.failed_exports.values()) |value| { |
| 3213 | 3366 | try addModuleErrorMsg(zcu, &bundle, value.*); |
| 3214 | 3367 | } |
| ... | ... | @@ -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 | 3410 | if (comp.zcu) |zcu| { |
| 3258 | 3411 | if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { |
| ... | ... | @@ -3524,9 +3677,9 @@ pub fn performAllTheWork( |
| 3524 | 3677 | |
| 3525 | 3678 | defer if (comp.zcu) |zcu| { |
| 3526 | 3679 | zcu.sema_prog_node.end(); |
| 3527 | zcu.sema_prog_node = std.Progress.Node.none; | |
| 3680 | zcu.sema_prog_node = .none; | |
| 3528 | 3681 | zcu.codegen_prog_node.end(); |
| 3529 | zcu.codegen_prog_node = std.Progress.Node.none; | |
| 3682 | zcu.codegen_prog_node = .none; | |
| 3530 | 3683 | |
| 3531 | 3684 | zcu.generation += 1; |
| 3532 | 3685 | }; |
| ... | ... | @@ -3659,7 +3812,7 @@ fn performAllTheWorkInner( |
| 3659 | 3812 | try zcu.flushRetryableFailures(); |
| 3660 | 3813 | |
| 3661 | 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 | 3818 | if (!comp.separateCodegenThreadOk()) { |
| ... | ... | @@ -3689,6 +3842,8 @@ fn performAllTheWorkInner( |
| 3689 | 3842 | }); |
| 3690 | 3843 | continue; |
| 3691 | 3844 | } |
| 3845 | zcu.sema_prog_node.end(); | |
| 3846 | zcu.sema_prog_node = .none; | |
| 3692 | 3847 | } |
| 3693 | 3848 | break; |
| 3694 | 3849 | } |
| ... | ... | @@ -3962,6 +4117,7 @@ fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) voi |
| 3962 | 4117 | if (comp.separateCodegenThreadOk()) { |
| 3963 | 4118 | comp.queueLinkTasks(&.{link_task}); |
| 3964 | 4119 | } else { |
| 4120 | assert(comp.remaining_prelink_tasks == 0); | |
| 3965 | 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 | 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 | 564 | /// Always returns `null` for `status == .type_resolved`. This function is inteded |
| 556 | 565 | /// to be used by code generation, since semantic analysis will ensure that any `Nav` |
| 557 | 566 | /// which is potentially `extern` is fully resolved. |
| ... | ... | @@ -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 | 606 | /// Asserts that `status != .unresolved`. |
| 589 | 607 | pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool { |
| 590 | 608 | return switch (nav.status) { |
| ... | ... | @@ -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 | 633 | /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer |
| 602 | 634 | /// to some other `Nav` due to an extern definition or extern alias (see #21027). |
| 603 | 635 | /// This query is valid on `Nav`s for whom only the type is resolved. |
| ... | ... | @@ -3360,6 +3392,10 @@ pub const LoadedUnionType = struct { |
| 3360 | 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 | 3399 | pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool) RequiresComptime { |
| 3364 | 3400 | const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; |
| 3365 | 3401 | extra_mutex.lock(); |
| ... | ... | @@ -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 | 4054 | return switch (s.layout) { |
| 4019 | 4055 | .@"packed" => s.backingIntTypeUnordered(ip) != .none, |
| 4020 | 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 | 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 | 11840 | pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 { |
| 11801 | 11841 | return switch (ip.indexToKey(ty)) { |
| 11802 | 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 | 38298 | // So, pick up and delete any existing exports. This strategy performs |
| 38299 | 38299 | // redundant work, but that's okay, because this case is exceedingly rare. |
| 38300 | 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 | 38302 | } else if (zcu.multi_exports.get(sema.owner)) |info| { |
| 38303 | 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 | 38307 | // `sema.exports` is completed; store the data into the `Zcu`. |
| 38308 | 38308 | if (sema.exports.items.len == 1) { |
| 38309 | 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 | 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]; | |
| 38315 | zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, @intCast(export_idx)); | |
| 38314 | export_idx.ptr(zcu).* = sema.exports.items[0]; | |
| 38315 | zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx); | |
| 38316 | 38316 | } else { |
| 38317 | 38317 | try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); |
| 38318 | 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 | 441 | |
| 442 | 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 | 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 | 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 | 456 | return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; |
| 457 | 457 | } |
| 458 | 458 | |
| ... | ... | @@ -471,7 +471,7 @@ pub fn hasRuntimeBitsInner( |
| 471 | 471 | ty: Type, |
| 472 | 472 | ignore_comptime_only: bool, |
| 473 | 473 | comptime strat: ResolveStratLazy, |
| 474 | zcu: *Zcu, | |
| 474 | zcu: strat.ZcuPtr(), | |
| 475 | 475 | tid: strat.Tid(), |
| 476 | 476 | ) RuntimeBitsError!bool { |
| 477 | 477 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -560,7 +560,7 @@ pub fn hasRuntimeBitsInner( |
| 560 | 560 | }, |
| 561 | 561 | .struct_type => { |
| 562 | 562 | const struct_type = ip.loadStructType(ty.toIntern()); |
| 563 | if (struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) { | |
| 563 | if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) { | |
| 564 | 564 | // In this case, we guess that hasRuntimeBits() for this type is true, |
| 565 | 565 | // and then later if our guess was incorrect, we emit a compile error. |
| 566 | 566 | return true; |
| ... | ... | @@ -596,7 +596,7 @@ pub fn hasRuntimeBitsInner( |
| 596 | 596 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 597 | 597 | const union_flags = union_type.flagsUnordered(ip); |
| 598 | 598 | switch (union_flags.runtime_tag) { |
| 599 | .none => { | |
| 599 | .none => if (strat != .eager) { | |
| 600 | 600 | // In this case, we guess that hasRuntimeBits() for this type is true, |
| 601 | 601 | // and then later if our guess was incorrect, we emit a compile error. |
| 602 | 602 | if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip)) return true; |
| ... | ... | @@ -774,7 +774,7 @@ pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { |
| 774 | 774 | pub fn fnHasRuntimeBitsInner( |
| 775 | 775 | ty: Type, |
| 776 | 776 | comptime strat: ResolveStrat, |
| 777 | zcu: *Zcu, | |
| 777 | zcu: strat.ZcuPtr(), | |
| 778 | 778 | tid: strat.Tid(), |
| 779 | 779 | ) SemaError!bool { |
| 780 | 780 | const fn_info = zcu.typeToFunc(ty).?; |
| ... | ... | @@ -815,7 +815,7 @@ pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { |
| 815 | 815 | pub fn ptrAlignmentInner( |
| 816 | 816 | ty: Type, |
| 817 | 817 | comptime strat: ResolveStrat, |
| 818 | zcu: *Zcu, | |
| 818 | zcu: strat.ZcuPtr(), | |
| 819 | 819 | tid: strat.Tid(), |
| 820 | 820 | ) !Alignment { |
| 821 | 821 | return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { |
| ... | ... | @@ -868,14 +868,25 @@ pub const ResolveStratLazy = enum { |
| 868 | 868 | /// This should typically be used from semantic analysis. |
| 869 | 869 | sema, |
| 870 | 870 | |
| 871 | pub fn Tid(comptime strat: ResolveStratLazy) type { | |
| 871 | pub fn Tid(strat: ResolveStratLazy) type { | |
| 872 | 872 | return switch (strat) { |
| 873 | 873 | .lazy, .sema => Zcu.PerThread.Id, |
| 874 | 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 | 890 | .lazy, .sema => Zcu.PerThread, |
| 880 | 891 | .eager => void, |
| 881 | 892 | } { |
| ... | ... | @@ -896,14 +907,21 @@ pub const ResolveStrat = enum { |
| 896 | 907 | /// This should typically be used from semantic analysis. |
| 897 | 908 | sema, |
| 898 | 909 | |
| 899 | pub fn Tid(comptime strat: ResolveStrat) type { | |
| 910 | pub fn Tid(strat: ResolveStrat) type { | |
| 900 | 911 | return switch (strat) { |
| 901 | 912 | .sema => Zcu.PerThread.Id, |
| 902 | 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 | 925 | .sema => Zcu.PerThread, |
| 908 | 926 | .normal => void, |
| 909 | 927 | } { |
| ... | ... | @@ -922,7 +940,7 @@ pub const ResolveStrat = enum { |
| 922 | 940 | }; |
| 923 | 941 | |
| 924 | 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 | 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 | 957 | pub fn abiAlignmentInner( |
| 940 | 958 | ty: Type, |
| 941 | 959 | comptime strat: ResolveStratLazy, |
| 942 | zcu: *Zcu, | |
| 960 | zcu: strat.ZcuPtr(), | |
| 943 | 961 | tid: strat.Tid(), |
| 944 | 962 | ) SemaError!AbiAlignmentInner { |
| 945 | 963 | const pt = strat.pt(zcu, tid); |
| ... | ... | @@ -1156,7 +1174,7 @@ pub fn abiAlignmentInner( |
| 1156 | 1174 | fn abiAlignmentInnerErrorUnion( |
| 1157 | 1175 | ty: Type, |
| 1158 | 1176 | comptime strat: ResolveStratLazy, |
| 1159 | zcu: *Zcu, | |
| 1177 | zcu: strat.ZcuPtr(), | |
| 1160 | 1178 | tid: strat.Tid(), |
| 1161 | 1179 | payload_ty: Type, |
| 1162 | 1180 | ) SemaError!AbiAlignmentInner { |
| ... | ... | @@ -1198,7 +1216,7 @@ fn abiAlignmentInnerErrorUnion( |
| 1198 | 1216 | fn abiAlignmentInnerOptional( |
| 1199 | 1217 | ty: Type, |
| 1200 | 1218 | comptime strat: ResolveStratLazy, |
| 1201 | zcu: *Zcu, | |
| 1219 | zcu: strat.ZcuPtr(), | |
| 1202 | 1220 | tid: strat.Tid(), |
| 1203 | 1221 | ) SemaError!AbiAlignmentInner { |
| 1204 | 1222 | const pt = strat.pt(zcu, tid); |
| ... | ... | @@ -1244,7 +1262,7 @@ const AbiSizeInner = union(enum) { |
| 1244 | 1262 | |
| 1245 | 1263 | /// Asserts the type has the ABI size already resolved. |
| 1246 | 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 | 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 | 1287 | pub fn abiSizeInner( |
| 1270 | 1288 | ty: Type, |
| 1271 | 1289 | comptime strat: ResolveStratLazy, |
| 1272 | zcu: *Zcu, | |
| 1290 | zcu: strat.ZcuPtr(), | |
| 1273 | 1291 | tid: strat.Tid(), |
| 1274 | 1292 | ) SemaError!AbiSizeInner { |
| 1275 | 1293 | const target = zcu.getTarget(); |
| ... | ... | @@ -1542,7 +1560,7 @@ pub fn abiSizeInner( |
| 1542 | 1560 | fn abiSizeInnerOptional( |
| 1543 | 1561 | ty: Type, |
| 1544 | 1562 | comptime strat: ResolveStratLazy, |
| 1545 | zcu: *Zcu, | |
| 1563 | zcu: strat.ZcuPtr(), | |
| 1546 | 1564 | tid: strat.Tid(), |
| 1547 | 1565 | ) SemaError!AbiSizeInner { |
| 1548 | 1566 | const child_ty = ty.optionalChild(zcu); |
| ... | ... | @@ -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 | 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 | 1730 | pub fn bitSizeInner( |
| 1713 | 1731 | ty: Type, |
| 1714 | 1732 | comptime strat: ResolveStrat, |
| 1715 | zcu: *Zcu, | |
| 1733 | zcu: strat.ZcuPtr(), | |
| 1716 | 1734 | tid: strat.Tid(), |
| 1717 | 1735 | ) SemaError!u64 { |
| 1718 | 1736 | const target = zcu.getTarget(); |
| ... | ... | @@ -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 | 2170 | const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); |
| 2153 | 2171 | return Type.getUnionLayout(union_obj, zcu); |
| 2154 | 2172 | } |
| ... | ... | @@ -2746,7 +2764,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value { |
| 2746 | 2764 | |
| 2747 | 2765 | /// During semantic analysis, instead call `ty.comptimeOnlySema` which |
| 2748 | 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 | 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 | 2777 | pub fn comptimeOnlyInner( |
| 2760 | 2778 | ty: Type, |
| 2761 | 2779 | comptime strat: ResolveStrat, |
| 2762 | zcu: *Zcu, | |
| 2780 | zcu: strat.ZcuPtr(), | |
| 2763 | 2781 | tid: strat.Tid(), |
| 2764 | 2782 | ) SemaError!bool { |
| 2765 | 2783 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -2834,40 +2852,44 @@ pub fn comptimeOnlyInner( |
| 2834 | 2852 | if (struct_type.layout == .@"packed") |
| 2835 | 2853 | return false; |
| 2836 | 2854 | |
| 2837 | // A struct with no fields is not comptime-only. | |
| 2838 | return switch (struct_type.setRequiresComptimeWip(ip)) { | |
| 2839 | .no, .wip => false, | |
| 2840 | .yes => true, | |
| 2841 | .unknown => { | |
| 2842 | // Inlined `assert` so that the resolution calls below are not statically reachable. | |
| 2843 | if (strat != .sema) unreachable; | |
| 2844 | ||
| 2845 | if (struct_type.flagsUnordered(ip).field_types_wip) { | |
| 2846 | struct_type.setRequiresComptime(ip, .unknown); | |
| 2847 | return false; | |
| 2848 | } | |
| 2855 | return switch (strat) { | |
| 2856 | .normal => switch (struct_type.requiresComptime(ip)) { | |
| 2857 | .wip => unreachable, | |
| 2858 | .no => false, | |
| 2859 | .yes => true, | |
| 2860 | .unknown => unreachable, | |
| 2861 | }, | |
| 2862 | .sema => switch (struct_type.setRequiresComptimeWip(ip)) { | |
| 2863 | .no, .wip => false, | |
| 2864 | .yes => true, | |
| 2865 | .unknown => { | |
| 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); | |
| 2853 | try ty.resolveFields(pt); | |
| 2854 | ||
| 2855 | for (0..struct_type.field_types.len) |i_usize| { | |
| 2856 | const i: u32 = @intCast(i_usize); | |
| 2857 | if (struct_type.fieldIsComptime(ip, i)) continue; | |
| 2858 | const field_ty = struct_type.field_types.get(ip)[i]; | |
| 2859 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { | |
| 2860 | // Note that this does not cause the layout to | |
| 2861 | // be considered resolved. Comptime-only types | |
| 2862 | // still maintain a layout of their | |
| 2863 | // runtime-known fields. | |
| 2864 | struct_type.setRequiresComptime(ip, .yes); | |
| 2865 | return true; | |
| 2873 | const pt = strat.pt(zcu, tid); | |
| 2874 | try ty.resolveFields(pt); | |
| 2875 | ||
| 2876 | for (0..struct_type.field_types.len) |i_usize| { | |
| 2877 | const i: u32 = @intCast(i_usize); | |
| 2878 | if (struct_type.fieldIsComptime(ip, i)) continue; | |
| 2879 | const field_ty = struct_type.field_types.get(ip)[i]; | |
| 2880 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { | |
| 2881 | // Note that this does not cause the layout to | |
| 2882 | // be considered resolved. Comptime-only types | |
| 2883 | // still maintain a layout of their | |
| 2884 | // runtime-known fields. | |
| 2885 | struct_type.setRequiresComptime(ip, .yes); | |
| 2886 | return true; | |
| 2887 | } | |
| 2866 | 2888 | } |
| 2867 | } | |
| 2868 | 2889 | |
| 2869 | struct_type.setRequiresComptime(ip, .no); | |
| 2870 | return false; | |
| 2890 | struct_type.setRequiresComptime(ip, .no); | |
| 2891 | return false; | |
| 2892 | }, | |
| 2871 | 2893 | }, |
| 2872 | 2894 | }; |
| 2873 | 2895 | }, |
| ... | ... | @@ -2882,35 +2904,40 @@ pub fn comptimeOnlyInner( |
| 2882 | 2904 | |
| 2883 | 2905 | .union_type => { |
| 2884 | 2906 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 2885 | switch (union_type.setRequiresComptimeWip(ip)) { | |
| 2886 | .no, .wip => return false, | |
| 2887 | .yes => return true, | |
| 2888 | .unknown => { | |
| 2889 | // Inlined `assert` so that the resolution calls below are not statically reachable. | |
| 2890 | if (strat != .sema) unreachable; | |
| 2891 | ||
| 2892 | if (union_type.flagsUnordered(ip).status == .field_types_wip) { | |
| 2893 | union_type.setRequiresComptime(ip, .unknown); | |
| 2894 | return false; | |
| 2895 | } | |
| 2907 | return switch (strat) { | |
| 2908 | .normal => switch (union_type.requiresComptime(ip)) { | |
| 2909 | .wip => unreachable, | |
| 2910 | .no => false, | |
| 2911 | .yes => true, | |
| 2912 | .unknown => unreachable, | |
| 2913 | }, | |
| 2914 | .sema => switch (union_type.setRequiresComptimeWip(ip)) { | |
| 2915 | .no, .wip => return false, | |
| 2916 | .yes => return true, | |
| 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); | |
| 2900 | try ty.resolveFields(pt); | |
| 2925 | const pt = strat.pt(zcu, tid); | |
| 2926 | try ty.resolveFields(pt); | |
| 2901 | 2927 | |
| 2902 | for (0..union_type.field_types.len) |field_idx| { | |
| 2903 | const field_ty = union_type.field_types.get(ip)[field_idx]; | |
| 2904 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { | |
| 2905 | union_type.setRequiresComptime(ip, .yes); | |
| 2906 | return true; | |
| 2928 | for (0..union_type.field_types.len) |field_idx| { | |
| 2929 | const field_ty = union_type.field_types.get(ip)[field_idx]; | |
| 2930 | if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { | |
| 2931 | union_type.setRequiresComptime(ip, .yes); | |
| 2932 | return true; | |
| 2933 | } | |
| 2907 | 2934 | } |
| 2908 | } | |
| 2909 | 2935 | |
| 2910 | union_type.setRequiresComptime(ip, .no); | |
| 2911 | return false; | |
| 2936 | union_type.setRequiresComptime(ip, .no); | |
| 2937 | return false; | |
| 2938 | }, | |
| 2912 | 2939 | }, |
| 2913 | } | |
| 2940 | }; | |
| 2914 | 2941 | }, |
| 2915 | 2942 | |
| 2916 | 2943 | .opaque_type => false, |
| ... | ... | @@ -3207,7 +3234,7 @@ pub fn fieldAlignmentInner( |
| 3207 | 3234 | ty: Type, |
| 3208 | 3235 | index: usize, |
| 3209 | 3236 | comptime strat: ResolveStrat, |
| 3210 | zcu: *Zcu, | |
| 3237 | zcu: strat.ZcuPtr(), | |
| 3211 | 3238 | tid: strat.Tid(), |
| 3212 | 3239 | ) SemaError!Alignment { |
| 3213 | 3240 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -3281,7 +3308,7 @@ pub fn structFieldAlignmentInner( |
| 3281 | 3308 | explicit_alignment: Alignment, |
| 3282 | 3309 | layout: std.builtin.Type.ContainerLayout, |
| 3283 | 3310 | comptime strat: Type.ResolveStrat, |
| 3284 | zcu: *Zcu, | |
| 3311 | zcu: strat.ZcuPtr(), | |
| 3285 | 3312 | tid: strat.Tid(), |
| 3286 | 3313 | ) SemaError!Alignment { |
| 3287 | 3314 | assert(layout != .@"packed"); |
| ... | ... | @@ -3323,7 +3350,7 @@ pub fn unionFieldAlignmentInner( |
| 3323 | 3350 | explicit_alignment: Alignment, |
| 3324 | 3351 | layout: std.builtin.Type.ContainerLayout, |
| 3325 | 3352 | comptime strat: Type.ResolveStrat, |
| 3326 | zcu: *Zcu, | |
| 3353 | zcu: strat.ZcuPtr(), | |
| 3327 | 3354 | tid: strat.Tid(), |
| 3328 | 3355 | ) SemaError!Alignment { |
| 3329 | 3356 | assert(layout != .@"packed"); |
| ... | ... | @@ -3392,11 +3419,7 @@ pub const FieldOffset = struct { |
| 3392 | 3419 | }; |
| 3393 | 3420 | |
| 3394 | 3421 | /// Supports structs and unions. |
| 3395 | pub fn structFieldOffset( | |
| 3396 | ty: Type, | |
| 3397 | index: usize, | |
| 3398 | zcu: *Zcu, | |
| 3399 | ) u64 { | |
| 3422 | pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 { | |
| 3400 | 3423 | const ip = &zcu.intern_pool; |
| 3401 | 3424 | switch (ip.indexToKey(ty.toIntern())) { |
| 3402 | 3425 | .struct_type => { |
| ... | ... | @@ -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 | 3971 | const ip = &zcu.intern_pool; |
| 3949 | 3972 | assert(loaded_union.haveLayout(ip)); |
| 3950 | 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 | 241 | |
| 242 | 242 | /// If the value fits in a u64, return it, otherwise null. |
| 243 | 243 | /// Asserts not undefined. |
| 244 | pub fn getUnsignedInt(val: Value, zcu: *Zcu) ?u64 { | |
| 244 | pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 { | |
| 245 | 245 | return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable; |
| 246 | 246 | } |
| 247 | 247 | |
| 248 | 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 | 250 | return getUnsignedInt(val, zcu).?; |
| 251 | 251 | } |
| 252 | 252 | |
| ... | ... | @@ -259,7 +259,7 @@ pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 { |
| 259 | 259 | pub fn getUnsignedIntInner( |
| 260 | 260 | val: Value, |
| 261 | 261 | comptime strat: ResolveStrat, |
| 262 | zcu: *Zcu, | |
| 262 | zcu: strat.ZcuPtr(), | |
| 263 | 263 | tid: strat.Tid(), |
| 264 | 264 | ) !?u64 { |
| 265 | 265 | return switch (val.toIntern()) { |
| ... | ... | @@ -304,7 +304,7 @@ pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 { |
| 304 | 304 | } |
| 305 | 305 | |
| 306 | 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 | 308 | return switch (val.toIntern()) { |
| 309 | 309 | .bool_false => 0, |
| 310 | 310 | .bool_true => 1, |
src/Zcu.zig+93-24| ... | ... | @@ -19,8 +19,8 @@ const Ast = std.zig.Ast; |
| 19 | 19 | const Zcu = @This(); |
| 20 | 20 | const Compilation = @import("Compilation.zig"); |
| 21 | 21 | const Cache = std.Build.Cache; |
| 22 | const Value = @import("Value.zig"); | |
| 23 | const Type = @import("Type.zig"); | |
| 22 | pub const Value = @import("Value.zig"); | |
| 23 | pub const Type = @import("Type.zig"); | |
| 24 | 24 | const Package = @import("Package.zig"); |
| 25 | 25 | const link = @import("link.zig"); |
| 26 | 26 | const Air = @import("Air.zig"); |
| ... | ... | @@ -79,11 +79,11 @@ local_zir_cache: Compilation.Directory, |
| 79 | 79 | all_exports: std.ArrayListUnmanaged(Export) = .empty, |
| 80 | 80 | /// This is a list of free indices in `all_exports`. These indices may be reused by exports from |
| 81 | 81 | /// future semantic analysis. |
| 82 | free_exports: std.ArrayListUnmanaged(u32) = .empty, | |
| 82 | free_exports: std.ArrayListUnmanaged(Export.Index) = .empty, | |
| 83 | 83 | /// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of |
| 84 | 84 | /// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit` |
| 85 | 85 | /// whose analysis triggered the export. |
| 86 | single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, | |
| 86 | single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, Export.Index) = .empty, | |
| 87 | 87 | /// Like `single_exports`, but for `AnalUnit`s which perform multiple exports. |
| 88 | 88 | /// The exports are `all_exports.items[index..][0..len]`. |
| 89 | 89 | multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { |
| ... | ... | @@ -127,6 +127,7 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp |
| 127 | 127 | /// This may be a simple "value" `Nav`, or it may be a function. |
| 128 | 128 | /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. |
| 129 | 129 | failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, |
| 130 | failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty, | |
| 130 | 131 | /// Keep track of one `@compileLog` callsite per `AnalUnit`. |
| 131 | 132 | /// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`. |
| 132 | 133 | compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { |
| ... | ... | @@ -144,8 +145,7 @@ compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { |
| 144 | 145 | failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty, |
| 145 | 146 | /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. |
| 146 | 147 | failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .empty, |
| 147 | /// Key is index into `all_exports`. | |
| 148 | failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .empty, | |
| 148 | failed_exports: std.AutoArrayHashMapUnmanaged(Export.Index, *ErrorMsg) = .empty, | |
| 149 | 149 | /// If analysis failed due to a cimport error, the corresponding Clang errors |
| 150 | 150 | /// are stored here. |
| 151 | 151 | cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .empty, |
| ... | ... | @@ -524,6 +524,15 @@ pub const Export = struct { |
| 524 | 524 | section: InternPool.OptionalNullTerminatedString = .none, |
| 525 | 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 | 538 | pub const Reference = struct { |
| ... | ... | @@ -2439,16 +2448,14 @@ pub fn deinit(zcu: *Zcu) void { |
| 2439 | 2448 | zcu.local_zir_cache.handle.close(); |
| 2440 | 2449 | zcu.global_zir_cache.handle.close(); |
| 2441 | 2450 | |
| 2442 | for (zcu.failed_analysis.values()) |value| { | |
| 2443 | value.destroy(gpa); | |
| 2444 | } | |
| 2445 | for (zcu.failed_codegen.values()) |value| { | |
| 2446 | value.destroy(gpa); | |
| 2447 | } | |
| 2451 | for (zcu.failed_analysis.values()) |value| value.destroy(gpa); | |
| 2452 | for (zcu.failed_codegen.values()) |value| value.destroy(gpa); | |
| 2453 | for (zcu.failed_types.values()) |value| value.destroy(gpa); | |
| 2448 | 2454 | zcu.analysis_in_progress.deinit(gpa); |
| 2449 | 2455 | zcu.failed_analysis.deinit(gpa); |
| 2450 | 2456 | zcu.transitive_failed_analysis.deinit(gpa); |
| 2451 | 2457 | zcu.failed_codegen.deinit(gpa); |
| 2458 | zcu.failed_types.deinit(gpa); | |
| 2452 | 2459 | |
| 2453 | 2460 | for (zcu.failed_files.values()) |value| { |
| 2454 | 2461 | if (value) |msg| msg.destroy(gpa); |
| ... | ... | @@ -3093,7 +3100,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3093 | 3100 | const gpa = zcu.gpa; |
| 3094 | 3101 | |
| 3095 | 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 | 3104 | else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| |
| 3098 | 3105 | .{ info.value.index, info.value.len } |
| 3099 | 3106 | else |
| ... | ... | @@ -3107,11 +3114,12 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3107 | 3114 | // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports |
| 3108 | 3115 | // within a single update. |
| 3109 | 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 | 3119 | if (zcu.comp.bin_file) |lf| { |
| 3112 | 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 | 3123 | failed_kv.value.destroy(gpa); |
| 3116 | 3124 | } |
| 3117 | 3125 | } |
| ... | ... | @@ -3123,7 +3131,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3123 | 3131 | return; |
| 3124 | 3132 | }; |
| 3125 | 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 | 3277 | |
| 3270 | 3278 | pub fn handleUpdateExports( |
| 3271 | 3279 | zcu: *Zcu, |
| 3272 | export_indices: []const u32, | |
| 3280 | export_indices: []const Export.Index, | |
| 3273 | 3281 | result: link.File.UpdateExportsError!void, |
| 3274 | 3282 | ) Allocator.Error!void { |
| 3275 | 3283 | const gpa = zcu.gpa; |
| ... | ... | @@ -3277,12 +3285,10 @@ pub fn handleUpdateExports( |
| 3277 | 3285 | error.OutOfMemory => return error.OutOfMemory, |
| 3278 | 3286 | error.AnalysisFail => { |
| 3279 | 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 | 3289 | new_export.status = .failed_retryable; |
| 3282 | 3290 | try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); |
| 3283 | const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{ | |
| 3284 | @errorName(err), | |
| 3285 | }); | |
| 3291 | const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)}); | |
| 3286 | 3292 | zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); |
| 3287 | 3293 | }, |
| 3288 | 3294 | }; |
| ... | ... | @@ -3443,7 +3449,7 @@ pub fn atomicPtrAlignment( |
| 3443 | 3449 | /// * `@TypeOf(.{})` |
| 3444 | 3450 | /// * A struct which has no fields (`struct {}`). |
| 3445 | 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 | 3453 | if (ty.ip_index == .none) return null; |
| 3448 | 3454 | const ip = &zcu.intern_pool; |
| 3449 | 3455 | return switch (ip.indexToKey(ty.ip_index)) { |
| ... | ... | @@ -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 | 3462 | const s = zcu.typeToStruct(ty) orelse return null; |
| 3457 | 3463 | if (s.layout != .@"packed") return null; |
| 3458 | 3464 | return s; |
| ... | ... | @@ -3477,7 +3483,7 @@ pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Ind |
| 3477 | 3483 | } |
| 3478 | 3484 | |
| 3479 | 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 | 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 | 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 | 3812 | pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { |
| 3795 | 3813 | const ip = &zcu.intern_pool; |
| 3796 | 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 | 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 | 1722 | // Correcting this failure will involve changing a type this function |
| 1723 | 1723 | // depends on, hence triggering re-analysis of this function, so this |
| 1724 | 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 | 1725 | } else if (comp.bin_file) |lf| { |
| 1728 | 1726 | lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) { |
| 1729 | 1727 | error.OutOfMemory => return error.OutOfMemory, |
| 1730 | error.AnalysisFail => { | |
| 1731 | assert(zcu.failed_codegen.contains(nav_index)); | |
| 1732 | }, | |
| 1733 | else => { | |
| 1728 | error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)), | |
| 1729 | error.Overflow => { | |
| 1734 | 1730 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( |
| 1735 | 1731 | gpa, |
| 1736 | 1732 | zcu.navSrcLoc(nav_index), |
| 1737 | 1733 | "unable to codegen: {s}", |
| 1738 | 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 | 1739 | } else if (zcu.llvm_object) |llvm_object| { |
| ... | ... | @@ -2819,8 +2815,8 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2819 | 2815 | const gpa = zcu.gpa; |
| 2820 | 2816 | |
| 2821 | 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; | |
| 2823 | var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(u32)) = .empty; | |
| 2818 | var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty; | |
| 2819 | var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayListUnmanaged(Zcu.Export.Index)) = .empty; | |
| 2824 | 2820 | defer { |
| 2825 | 2821 | for (nav_exports.values()) |*exports| { |
| 2826 | 2822 | exports.deinit(gpa); |
| ... | ... | @@ -2839,7 +2835,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2839 | 2835 | try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count()); |
| 2840 | 2836 | |
| 2841 | 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 | 2839 | const value_ptr, const found_existing = switch (exp.exported) { |
| 2844 | 2840 | .nav => |nav| gop: { |
| 2845 | 2841 | const gop = try nav_exports.getOrPut(gpa, nav); |
| ... | ... | @@ -2867,7 +2863,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 2867 | 2863 | }, |
| 2868 | 2864 | }; |
| 2869 | 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 | 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 | 2887 | fn processExportsInner( |
| 2892 | 2888 | pt: Zcu.PerThread, |
| 2893 | 2889 | symbol_exports: *SymbolExports, |
| 2894 | 2890 | exported: Zcu.Exported, |
| 2895 | export_indices: []const u32, | |
| 2891 | export_indices: []const Zcu.Export.Index, | |
| 2896 | 2892 | ) error{OutOfMemory}!void { |
| 2897 | 2893 | const zcu = pt.zcu; |
| 2898 | 2894 | const gpa = zcu.gpa; |
| 2899 | 2895 | const ip = &zcu.intern_pool; |
| 2900 | 2896 | |
| 2901 | 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 | 2899 | const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); |
| 2904 | 2900 | if (gop.found_existing) { |
| 2905 | 2901 | new_export.status = .failed_retryable; |
| ... | ... | @@ -2908,7 +2904,7 @@ fn processExportsInner( |
| 2908 | 2904 | new_export.opts.name.fmt(ip), |
| 2909 | 2905 | }); |
| 2910 | 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 | 2908 | try zcu.errNote(other_export.src, msg, "other symbol here", .{}); |
| 2913 | 2909 | zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); |
| 2914 | 2910 | new_export.status = .failed; |
| ... | ... | @@ -3100,6 +3096,7 @@ pub fn populateTestFunctions( |
| 3100 | 3096 | pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void { |
| 3101 | 3097 | const zcu = pt.zcu; |
| 3102 | 3098 | const comp = zcu.comp; |
| 3099 | const gpa = zcu.gpa; | |
| 3103 | 3100 | const ip = &zcu.intern_pool; |
| 3104 | 3101 | |
| 3105 | 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 | 3110 | } else if (comp.bin_file) |lf| { |
| 3114 | 3111 | lf.updateNav(pt, nav_index) catch |err| switch (err) { |
| 3115 | 3112 | error.OutOfMemory => return error.OutOfMemory, |
| 3116 | error.AnalysisFail => { | |
| 3117 | assert(zcu.failed_codegen.contains(nav_index)); | |
| 3118 | }, | |
| 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( | |
| 3113 | error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)), | |
| 3114 | error.Overflow => { | |
| 3115 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( | |
| 3123 | 3116 | gpa, |
| 3124 | 3117 | zcu.navSrcLoc(nav_index), |
| 3125 | 3118 | "unable to codegen: {s}", |
| 3126 | 3119 | .{@errorName(err)}, |
| 3127 | 3120 | )); |
| 3128 | if (nav.analysis != null) { | |
| 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 | } | |
| 3121 | // Not a retryable failure. | |
| 3136 | 3122 | }, |
| 3137 | 3123 | }; |
| 3138 | 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 | 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 | 3132 | const zcu = pt.zcu; |
| 3133 | const gpa = zcu.gpa; | |
| 3147 | 3134 | const comp = zcu.comp; |
| 3148 | 3135 | const ip = &zcu.intern_pool; |
| 3149 | 3136 | |
| 3150 | 3137 | const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0); |
| 3151 | 3138 | defer codegen_prog_node.end(); |
| 3152 | 3139 | |
| 3140 | if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(gpa); | |
| 3141 | ||
| 3153 | 3142 | if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) { |
| 3154 | 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 | |
| 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 | }; | |
| 3144 | return; | |
| 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 | 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 | 24 | const Alignment = InternPool.Alignment; |
| 25 | 25 | |
| 26 | 26 | const CodeGenError = codegen.CodeGenError; |
| 27 | const Result = codegen.Result; | |
| 28 | 27 | |
| 29 | 28 | const bits = @import("bits.zig"); |
| 30 | 29 | const abi = @import("abi.zig"); |
| ... | ... | @@ -51,7 +50,6 @@ debug_output: link.File.DebugInfoOutput, |
| 51 | 50 | target: *const std.Target, |
| 52 | 51 | func_index: InternPool.Index, |
| 53 | 52 | owner_nav: InternPool.Nav.Index, |
| 54 | err_msg: ?*ErrorMsg, | |
| 55 | 53 | args: []MCValue, |
| 56 | 54 | ret_mcv: MCValue, |
| 57 | 55 | fn_type: Type, |
| ... | ... | @@ -325,9 +323,9 @@ pub fn generate( |
| 325 | 323 | func_index: InternPool.Index, |
| 326 | 324 | air: Air, |
| 327 | 325 | liveness: Liveness, |
| 328 | code: *std.ArrayList(u8), | |
| 326 | code: *std.ArrayListUnmanaged(u8), | |
| 329 | 327 | debug_output: link.File.DebugInfoOutput, |
| 330 | ) CodeGenError!Result { | |
| 328 | ) CodeGenError!void { | |
| 331 | 329 | const zcu = pt.zcu; |
| 332 | 330 | const gpa = zcu.gpa; |
| 333 | 331 | const func = zcu.funcInfo(func_index); |
| ... | ... | @@ -353,7 +351,6 @@ pub fn generate( |
| 353 | 351 | .bin_file = lf, |
| 354 | 352 | .func_index = func_index, |
| 355 | 353 | .owner_nav = func.owner_nav, |
| 356 | .err_msg = null, | |
| 357 | 354 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 358 | 355 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 359 | 356 | .fn_type = fn_type, |
| ... | ... | @@ -370,10 +367,7 @@ pub fn generate( |
| 370 | 367 | defer function.dbg_info_relocs.deinit(gpa); |
| 371 | 368 | |
| 372 | 369 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 373 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 370 | error.CodegenFail => return error.CodegenFail, | |
| 377 | 371 | else => |e| return e, |
| 378 | 372 | }; |
| 379 | 373 | defer call_info.deinit(&function); |
| ... | ... | @@ -384,24 +378,23 @@ pub fn generate( |
| 384 | 378 | function.max_end_stack = call_info.stack_byte_count; |
| 385 | 379 | |
| 386 | 380 | function.gen() catch |err| switch (err) { |
| 387 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 388 | error.OutOfRegisters => return Result{ | |
| 389 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 390 | }, | |
| 381 | error.CodegenFail => return error.CodegenFail, | |
| 382 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 391 | 383 | else => |e| return e, |
| 392 | 384 | }; |
| 393 | 385 | |
| 394 | 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 | 392 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 400 | 393 | .extra = try function.mir_extra.toOwnedSlice(gpa), |
| 401 | 394 | }; |
| 402 | 395 | defer mir.deinit(gpa); |
| 403 | 396 | |
| 404 | var emit = Emit{ | |
| 397 | var emit: Emit = .{ | |
| 405 | 398 | .mir = mir, |
| 406 | 399 | .bin_file = lf, |
| 407 | 400 | .debug_output = debug_output, |
| ... | ... | @@ -417,15 +410,9 @@ pub fn generate( |
| 417 | 410 | defer emit.deinit(); |
| 418 | 411 | |
| 419 | 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 | 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 | 418 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| ... | ... | @@ -567,7 +554,7 @@ fn gen(self: *Self) !void { |
| 567 | 554 | .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, |
| 568 | 555 | }); |
| 569 | 556 | } else { |
| 570 | return self.failSymbol("TODO AArch64: allow larger stacks", .{}); | |
| 557 | @panic("TODO AArch64: allow larger stacks"); | |
| 571 | 558 | } |
| 572 | 559 | |
| 573 | 560 | _ = try self.addInst(.{ |
| ... | ... | @@ -723,7 +710,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 723 | 710 | .cmp_gt => try self.airCmp(inst, .gt), |
| 724 | 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 | 714 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 728 | 715 | |
| 729 | 716 | .alloc => try self.airAlloc(inst), |
| ... | ... | @@ -744,7 +731,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 744 | 731 | .fpext => try self.airFpext(inst), |
| 745 | 732 | .intcast => try self.airIntCast(inst), |
| 746 | 733 | .trunc => try self.airTrunc(inst), |
| 747 | .int_from_bool => try self.airIntFromBool(inst), | |
| 734 | .int_from_bool => try self.airIntFromBool(inst), | |
| 748 | 735 | .is_non_null => try self.airIsNonNull(inst), |
| 749 | 736 | .is_non_null_ptr => try self.airIsNonNullPtr(inst), |
| 750 | 737 | .is_null => try self.airIsNull(inst), |
| ... | ... | @@ -756,7 +743,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 756 | 743 | .load => try self.airLoad(inst), |
| 757 | 744 | .loop => try self.airLoop(inst), |
| 758 | 745 | .not => try self.airNot(inst), |
| 759 | .int_from_ptr => try self.airIntFromPtr(inst), | |
| 746 | .int_from_ptr => try self.airIntFromPtr(inst), | |
| 760 | 747 | .ret => try self.airRet(inst), |
| 761 | 748 | .ret_safe => try self.airRet(inst), // TODO |
| 762 | 749 | .ret_load => try self.airRetLoad(inst), |
| ... | ... | @@ -765,8 +752,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 765 | 752 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 766 | 753 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 767 | 754 | .array_to_slice => try self.airArrayToSlice(inst), |
| 768 | .float_from_int => try self.airFloatFromInt(inst), | |
| 769 | .int_from_float => try self.airIntFromFloat(inst), | |
| 755 | .float_from_int => try self.airFloatFromInt(inst), | |
| 756 | .int_from_float => try self.airIntFromFloat(inst), | |
| 770 | 757 | .cmpxchg_strong => try self.airCmpxchg(inst), |
| 771 | 758 | .cmpxchg_weak => try self.airCmpxchg(inst), |
| 772 | 759 | .atomic_rmw => try self.airAtomicRmw(inst), |
| ... | ... | @@ -1107,7 +1094,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 1107 | 1094 | /// Copies a value to a register without tracking the register. The register is not considered |
| 1108 | 1095 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 1109 | 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 | 1098 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1112 | 1099 | const reg = self.registerAlias(raw_reg, ty); |
| 1113 | 1100 | try self.genSetReg(ty, reg, mcv); |
| ... | ... | @@ -1125,12 +1112,12 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa |
| 1125 | 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 | 1116 | const stack_offset = try self.allocMemPtr(inst); |
| 1130 | 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 | 1121 | const pt = self.pt; |
| 1135 | 1122 | const zcu = pt.zcu; |
| 1136 | 1123 | const result: MCValue = switch (self.ret_mcv) { |
| ... | ... | @@ -1152,19 +1139,19 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1152 | 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 | 1143 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1157 | 1144 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); |
| 1158 | 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 | 1149 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1163 | 1150 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); |
| 1164 | 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 | 1155 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1169 | 1156 | if (self.liveness.isUnused(inst)) |
| 1170 | 1157 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -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 | 1284 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1298 | 1285 | const operand = try self.resolveInst(ty_op.operand); |
| 1299 | 1286 | const operand_ty = self.typeOf(ty_op.operand); |
| ... | ... | @@ -1306,14 +1293,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | 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 | 1297 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 1311 | 1298 | const operand = try self.resolveInst(un_op); |
| 1312 | 1299 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; |
| 1313 | 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 | 1304 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1318 | 1305 | const pt = self.pt; |
| 1319 | 1306 | const zcu = pt.zcu; |
| ... | ... | @@ -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 | 1475 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 1489 | 1476 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 1490 | 1477 | const lhs_ty = self.typeOf(bin_op.lhs); |
| ... | ... | @@ -1502,7 +1489,7 @@ fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1502 | 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 | 1493 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1507 | 1494 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1508 | 1495 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | ... | @@ -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 | 2431 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2445 | 2432 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 2446 | 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 | 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 | 2481 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2495 | 2482 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2496 | 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 | 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 | 2496 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2510 | 2497 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); |
| 2511 | 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 | 2502 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2516 | 2503 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); |
| 2517 | 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 | 2508 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2522 | 2509 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); |
| 2523 | 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 | 2514 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 2528 | 2515 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2529 | 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 | 2523 | const rhs_ty = self.typeOf(extra.rhs); |
| 2537 | 2524 | |
| 2538 | 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 | 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 | 2530 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 2544 | 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 | 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 | 2643 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2657 | 2644 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2658 | 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 | 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 | 2867 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2881 | 2868 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2882 | 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 | 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 | 3003 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3017 | 3004 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 3018 | 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 | 3009 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3023 | 3010 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3024 | 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 | 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 | 3046 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3060 | 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 | 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 | 3052 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3066 | 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 | 3054 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -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 | 3128 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3142 | 3129 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3143 | 3130 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| ... | ... | @@ -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 | 3209 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3223 | 3210 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3224 | 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 | 3217 | } |
| 3231 | 3218 | |
| 3232 | 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 | 3221 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3235 | 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 | 3223 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3237 | 3224 | } |
| 3238 | 3225 | |
| 3239 | 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 | 3228 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3242 | 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 | 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 | 3234 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3248 | 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 | 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 | 3240 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 3254 | 3241 | .dead |
| 3255 | 3242 | else |
| ... | ... | @@ -3257,17 +3244,17 @@ fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { |
| 3257 | 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 | 3248 | _ = inst; |
| 3262 | 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 | 3253 | _ = inst; |
| 3267 | 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 | 3258 | const pt = self.pt; |
| 3272 | 3259 | const zcu = pt.zcu; |
| 3273 | 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 | 3300 | } |
| 3314 | 3301 | |
| 3315 | 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 | 3304 | const pt = self.pt; |
| 3318 | 3305 | const zcu = pt.zcu; |
| 3319 | 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 | 3325 | } |
| 3339 | 3326 | |
| 3340 | 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 | 3329 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3343 | 3330 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3344 | 3331 | const pt = self.pt; |
| ... | ... | @@ -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 | 3370 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3384 | 3371 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3385 | 3372 | const mcv = try self.resolveInst(ty_op.operand); |
| ... | ... | @@ -3388,7 +3375,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3388 | 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 | 3379 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3393 | 3380 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3394 | 3381 | const ptr_bits = 64; |
| ... | ... | @@ -3412,7 +3399,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 3412 | 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 | 3403 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3417 | 3404 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3418 | 3405 | const ptr_bits = 64; |
| ... | ... | @@ -3429,7 +3416,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3429 | 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 | 3420 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3434 | 3421 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3435 | 3422 | const mcv = try self.resolveInst(ty_op.operand); |
| ... | ... | @@ -3444,7 +3431,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3444 | 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 | 3435 | const pt = self.pt; |
| 3449 | 3436 | const zcu = pt.zcu; |
| 3450 | 3437 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | ... | @@ -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 | 3478 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3492 | 3479 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3493 | 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 | 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 | 3497 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3511 | 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 | 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 | 3503 | const pt = self.pt; |
| 3517 | 3504 | const zcu = pt.zcu; |
| 3518 | 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 | 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 | 3517 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3531 | 3518 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3532 | 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 | 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 | 3533 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3547 | 3534 | _ = bin_op; |
| 3548 | 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 | 3539 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3553 | 3540 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); |
| 3554 | 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 | 3545 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3559 | 3546 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); |
| 3560 | 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 | 3551 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3565 | 3552 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); |
| 3566 | 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 | 3557 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3571 | 3558 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); |
| 3572 | 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 | 3563 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3577 | 3564 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); |
| 3578 | 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 | 3569 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3583 | 3570 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); |
| 3584 | 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 | 3575 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3589 | 3576 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); |
| 3590 | 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 | 3581 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 3595 | 3582 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 3596 | 3583 | .dead |
| ... | ... | @@ -3885,7 +3872,7 @@ fn genInlineMemsetCode( |
| 3885 | 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 | 3876 | const pt = self.pt; |
| 3890 | 3877 | const zcu = pt.zcu; |
| 3891 | 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 | 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 | 4077 | if (safety) { |
| 4091 | 4078 | // TODO if the value is undef, write 0xaa bytes to dest |
| 4092 | 4079 | } else { |
| ... | ... | @@ -4103,14 +4090,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 4103 | 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 | 4094 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4108 | 4095 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 4109 | 4096 | const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); |
| 4110 | 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 | 4101 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4115 | 4102 | const result = try self.structFieldPtr(inst, ty_op.operand, index); |
| 4116 | 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 | 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 | 4129 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4143 | 4130 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 4144 | 4131 | const operand = extra.struct_operand; |
| ... | ... | @@ -4194,7 +4181,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 4194 | 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 | 4185 | const pt = self.pt; |
| 4199 | 4186 | const zcu = pt.zcu; |
| 4200 | 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 | 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 | 4209 | // skip zero-bit arguments as they don't have a corresponding arg instruction |
| 4223 | 4210 | var arg_index = self.arg_index; |
| 4224 | 4211 | while (self.args[arg_index] == .none) arg_index += 1; |
| ... | ... | @@ -4238,7 +4225,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 4238 | 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 | 4229 | _ = try self.addInst(.{ |
| 4243 | 4230 | .tag = .brk, |
| 4244 | 4231 | .data = .{ .imm16 = 0x0001 }, |
| ... | ... | @@ -4246,7 +4233,7 @@ fn airTrap(self: *Self) !void { |
| 4246 | 4233 | return self.finishAirBookkeeping(); |
| 4247 | 4234 | } |
| 4248 | 4235 | |
| 4249 | fn airBreakpoint(self: *Self) !void { | |
| 4236 | fn airBreakpoint(self: *Self) InnerError!void { | |
| 4250 | 4237 | _ = try self.addInst(.{ |
| 4251 | 4238 | .tag = .brk, |
| 4252 | 4239 | .data = .{ .imm16 = 0xf000 }, |
| ... | ... | @@ -4254,17 +4241,17 @@ fn airBreakpoint(self: *Self) !void { |
| 4254 | 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 | 4245 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{}); |
| 4259 | 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 | 4250 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{}); |
| 4264 | 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 | 4255 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{}); |
| 4269 | 4256 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4270 | 4257 | const callee = pl_op.operand; |
| ... | ... | @@ -4422,7 +4409,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4422 | 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 | 4413 | const pt = self.pt; |
| 4427 | 4414 | const zcu = pt.zcu; |
| 4428 | 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 | 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 | 4446 | const pt = self.pt; |
| 4460 | 4447 | const zcu = pt.zcu; |
| 4461 | 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 | 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 | 4490 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4504 | 4491 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 4505 | 4492 | |
| ... | ... | @@ -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 | 4588 | _ = inst; |
| 4602 | 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 | 4593 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4607 | 4594 | const operand = try self.resolveInst(un_op); |
| 4608 | 4595 | _ = operand; |
| ... | ... | @@ -4610,7 +4597,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { |
| 4610 | 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 | 4601 | const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; |
| 4615 | 4602 | |
| 4616 | 4603 | _ = try self.addInst(.{ |
| ... | ... | @@ -4624,7 +4611,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 4624 | 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 | 4615 | const pt = self.pt; |
| 4629 | 4616 | const zcu = pt.zcu; |
| 4630 | 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 | 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 | 4626 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4640 | 4627 | const operand = pl_op.operand; |
| 4641 | 4628 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| ... | ... | @@ -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 | 4677 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4691 | 4678 | const cond = try self.resolveInst(pl_op.operand); |
| 4692 | 4679 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| ... | ... | @@ -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 | 4910 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4924 | 4911 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4925 | 4912 | const operand = try self.resolveInst(un_op); |
| ... | ... | @@ -4930,7 +4917,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4930 | 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 | 4921 | const pt = self.pt; |
| 4935 | 4922 | const zcu = pt.zcu; |
| 4936 | 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 | 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 | 4938 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4952 | 4939 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4953 | 4940 | const operand = try self.resolveInst(un_op); |
| ... | ... | @@ -4958,7 +4945,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4958 | 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 | 4949 | const pt = self.pt; |
| 4963 | 4950 | const zcu = pt.zcu; |
| 4964 | 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 | 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 | 4966 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4980 | 4967 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4981 | 4968 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| ... | ... | @@ -4986,7 +4973,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4986 | 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 | 4977 | const pt = self.pt; |
| 4991 | 4978 | const zcu = pt.zcu; |
| 4992 | 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 | 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 | 4994 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5008 | 4995 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5009 | 4996 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| ... | ... | @@ -5014,7 +5001,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 5014 | 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 | 5005 | const pt = self.pt; |
| 5019 | 5006 | const zcu = pt.zcu; |
| 5020 | 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 | 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 | 5022 | // A loop is a setup to be able to jump back to the beginning. |
| 5036 | 5023 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5037 | 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 | 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 | 5043 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5057 | 5044 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 5058 | 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 | 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 | 5081 | const switch_br = self.air.unwrapSwitch(inst); |
| 5095 | 5082 | const condition_ty = self.typeOf(switch_br.operand); |
| 5096 | 5083 | const liveness = try self.liveness.getSwitchBr( |
| ... | ... | @@ -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 | 5215 | const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; |
| 5229 | 5216 | try self.br(branch.block_inst, branch.operand); |
| 5230 | 5217 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| ... | ... | @@ -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 | 5259 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5273 | 5260 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 5274 | 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 | 5588 | .tag = .ldr_ptr_stack, |
| 5602 | 5589 | .data = .{ .load_store_stack = .{ |
| 5603 | 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 | 5604 | .immediate => |x| { |
| 5618 | 5605 | _ = try self.addInst(.{ |
| 5619 | 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 | 5610 | if (x & 0x0000_0000_ffff_0000 != 0) { |
| 5624 | 5611 | _ = try self.addInst(.{ |
| 5625 | 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 | 5618 | if (x & 0x0000_ffff_0000_0000 != 0) { |
| 5632 | 5619 | _ = try self.addInst(.{ |
| 5633 | 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 | 5624 | if (x & 0xffff_0000_0000_0000 != 0) { |
| 5638 | 5625 | _ = try self.addInst(.{ |
| 5639 | 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 | 5696 | .tag = tag, |
| 5710 | 5697 | .data = .{ .load_store_stack = .{ |
| 5711 | 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 | 5720 | .tag = tag, |
| 5734 | 5721 | .data = .{ .load_store_stack = .{ |
| 5735 | 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 | 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 | 5909 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5923 | 5910 | const result = try self.resolveInst(un_op); |
| 5924 | 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 | 5915 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5929 | 5916 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5930 | 5917 | const operand = try self.resolveInst(ty_op.operand); |
| ... | ... | @@ -5945,7 +5932,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 5945 | 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 | 5936 | const pt = self.pt; |
| 5950 | 5937 | const zcu = pt.zcu; |
| 5951 | 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 | 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 | 5954 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5968 | 5955 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{ |
| 5969 | 5956 | self.target.cpu.arch, |
| ... | ... | @@ -5971,7 +5958,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { |
| 5971 | 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 | 5962 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 5976 | 5963 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{ |
| 5977 | 5964 | self.target.cpu.arch, |
| ... | ... | @@ -5979,7 +5966,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 5979 | 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 | 5970 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5984 | 5971 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 5985 | 5972 | _ = extra; |
| ... | ... | @@ -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 | 5980 | _ = inst; |
| 5994 | 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 | 5985 | _ = inst; |
| 5999 | 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 | 5990 | _ = inst; |
| 6004 | 5991 | _ = order; |
| 6005 | 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 | 5996 | _ = inst; |
| 6010 | 5997 | if (safety) { |
| 6011 | 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 | 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 | 6006 | _ = inst; |
| 6020 | 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 | 6011 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 6025 | 6012 | const operand = try self.resolveInst(un_op); |
| 6026 | 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 | 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 | 6021 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 6035 | 6022 | const operand = try self.resolveInst(un_op); |
| 6036 | 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 | 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 | 6031 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6045 | 6032 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch}); |
| 6046 | 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 | 6037 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6051 | 6038 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; |
| 6052 | 6039 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch}); |
| 6053 | 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 | 6044 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6058 | 6045 | const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; |
| 6059 | 6046 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch}); |
| 6060 | 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 | 6051 | const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; |
| 6065 | 6052 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{}); |
| 6066 | 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 | 6057 | const pt = self.pt; |
| 6071 | 6058 | const zcu = pt.zcu; |
| 6072 | 6059 | const vector_ty = self.typeOfIndex(inst); |
| ... | ... | @@ -6090,19 +6077,19 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 6090 | 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 | 6081 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6095 | 6082 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; |
| 6096 | 6083 | _ = extra; |
| 6097 | 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 | 6088 | const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; |
| 6102 | 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 | 6093 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6107 | 6094 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; |
| 6108 | 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 | 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 | 6102 | const pt = self.pt; |
| 6116 | 6103 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 6117 | 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 | 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 | 6130 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 6144 | 6131 | const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); |
| 6145 | 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 | 6178 | .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, |
| 6192 | 6179 | .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO |
| 6193 | 6180 | }, |
| 6194 | .fail => |msg| { | |
| 6195 | self.err_msg = msg; | |
| 6196 | return error.CodegenFail; | |
| 6197 | }, | |
| 6181 | .fail => |msg| return self.failMsg(msg), | |
| 6198 | 6182 | }; |
| 6199 | 6183 | return mcv; |
| 6200 | 6184 | } |
| ... | ... | @@ -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 | 6343 | @branchHint(.cold); |
| 6360 | assert(self.err_msg == null); | |
| 6361 | self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); | |
| 6362 | return error.CodegenFail; | |
| 6344 | return self.pt.zcu.codegenFail(self.owner_nav, format, args); | |
| 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 | 6348 | @branchHint(.cold); |
| 6367 | assert(self.err_msg == null); | |
| 6368 | self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); | |
| 6369 | return error.CodegenFail; | |
| 6349 | return self.pt.zcu.codegenFailMsg(self.owner_nav, msg); | |
| 6370 | 6350 | } |
| 6371 | 6351 | |
| 6372 | 6352 | fn parseRegName(name: []const u8) ?Register { |
src/arch/aarch64/Emit.zig+4-2| ... | ... | @@ -20,7 +20,7 @@ debug_output: link.File.DebugInfoOutput, |
| 20 | 20 | target: *const std.Target, |
| 21 | 21 | err_msg: ?*ErrorMsg = null, |
| 22 | 22 | src_loc: Zcu.LazySrcLoc, |
| 23 | code: *std.ArrayList(u8), | |
| 23 | code: *std.ArrayListUnmanaged(u8), | |
| 24 | 24 | |
| 25 | 25 | prev_di_line: u32, |
| 26 | 26 | prev_di_column: u32, |
| ... | ... | @@ -424,8 +424,10 @@ fn lowerBranches(emit: *Emit) !void { |
| 424 | 424 | } |
| 425 | 425 | |
| 426 | 426 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 427 | const comp = emit.bin_file.comp; | |
| 428 | const gpa = comp.gpa; | |
| 427 | 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 | 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 | 23 | const build_options = @import("build_options"); |
| 24 | 24 | const Alignment = InternPool.Alignment; |
| 25 | 25 | |
| 26 | const Result = codegen.Result; | |
| 27 | 26 | const CodeGenError = codegen.CodeGenError; |
| 28 | 27 | |
| 29 | 28 | const bits = @import("bits.zig"); |
| ... | ... | @@ -333,9 +332,9 @@ pub fn generate( |
| 333 | 332 | func_index: InternPool.Index, |
| 334 | 333 | air: Air, |
| 335 | 334 | liveness: Liveness, |
| 336 | code: *std.ArrayList(u8), | |
| 335 | code: *std.ArrayListUnmanaged(u8), | |
| 337 | 336 | debug_output: link.File.DebugInfoOutput, |
| 338 | ) CodeGenError!Result { | |
| 337 | ) CodeGenError!void { | |
| 339 | 338 | const zcu = pt.zcu; |
| 340 | 339 | const gpa = zcu.gpa; |
| 341 | 340 | const func = zcu.funcInfo(func_index); |
| ... | ... | @@ -377,10 +376,7 @@ pub fn generate( |
| 377 | 376 | defer function.dbg_info_relocs.deinit(gpa); |
| 378 | 377 | |
| 379 | 378 | var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) { |
| 380 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 379 | error.CodegenFail => return error.CodegenFail, | |
| 384 | 380 | else => |e| return e, |
| 385 | 381 | }; |
| 386 | 382 | defer call_info.deinit(&function); |
| ... | ... | @@ -391,15 +387,14 @@ pub fn generate( |
| 391 | 387 | function.max_end_stack = call_info.stack_byte_count; |
| 392 | 388 | |
| 393 | 389 | function.gen() catch |err| switch (err) { |
| 394 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 395 | error.OutOfRegisters => return Result{ | |
| 396 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 397 | }, | |
| 390 | error.CodegenFail => return error.CodegenFail, | |
| 391 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 398 | 392 | else => |e| return e, |
| 399 | 393 | }; |
| 400 | 394 | |
| 401 | 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 | 400 | var mir = Mir{ |
| ... | ... | @@ -424,15 +419,9 @@ pub fn generate( |
| 424 | 419 | defer emit.deinit(); |
| 425 | 420 | |
| 426 | 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 | 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 | 427 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| ... | ... | @@ -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 | 6303 | @branchHint(.cold); |
| 6315 | assert(self.err_msg == null); | |
| 6316 | const gpa = self.gpa; | |
| 6317 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | |
| 6318 | return error.CodegenFail; | |
| 6304 | const zcu = self.pt.zcu; | |
| 6305 | const func = zcu.funcInfo(self.func_index); | |
| 6306 | const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args); | |
| 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 | 6311 | @branchHint(.cold); |
| 6323 | assert(self.err_msg == null); | |
| 6324 | const gpa = self.gpa; | |
| 6325 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | |
| 6326 | return error.CodegenFail; | |
| 6312 | const zcu = self.pt.zcu; | |
| 6313 | const func = zcu.funcInfo(self.func_index); | |
| 6314 | return zcu.codegenFailMsg(func.owner_nav, msg); | |
| 6327 | 6315 | } |
| 6328 | 6316 | |
| 6329 | 6317 | fn parseRegName(name: []const u8) ?Register { |
src/arch/arm/Emit.zig+4-2| ... | ... | @@ -24,7 +24,7 @@ debug_output: link.File.DebugInfoOutput, |
| 24 | 24 | target: *const std.Target, |
| 25 | 25 | err_msg: ?*ErrorMsg = null, |
| 26 | 26 | src_loc: Zcu.LazySrcLoc, |
| 27 | code: *std.ArrayList(u8), | |
| 27 | code: *std.ArrayListUnmanaged(u8), | |
| 28 | 28 | |
| 29 | 29 | prev_di_line: u32, |
| 30 | 30 | prev_di_column: u32, |
| ... | ... | @@ -342,8 +342,10 @@ fn lowerBranches(emit: *Emit) !void { |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | 344 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 345 | const comp = emit.bin_file.comp; | |
| 346 | const gpa = comp.gpa; | |
| 345 | 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 | 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 | 32 | const Alignment = InternPool.Alignment; |
| 33 | 33 | |
| 34 | 34 | const CodeGenError = codegen.CodeGenError; |
| 35 | const Result = codegen.Result; | |
| 36 | 35 | |
| 37 | 36 | const bits = @import("bits.zig"); |
| 38 | 37 | const abi = @import("abi.zig"); |
| ... | ... | @@ -62,7 +61,6 @@ gpa: Allocator, |
| 62 | 61 | mod: *Package.Module, |
| 63 | 62 | target: *const std.Target, |
| 64 | 63 | debug_output: link.File.DebugInfoOutput, |
| 65 | err_msg: ?*ErrorMsg, | |
| 66 | 64 | args: []MCValue, |
| 67 | 65 | ret_mcv: InstTracking, |
| 68 | 66 | fn_type: Type, |
| ... | ... | @@ -759,9 +757,9 @@ pub fn generate( |
| 759 | 757 | func_index: InternPool.Index, |
| 760 | 758 | air: Air, |
| 761 | 759 | liveness: Liveness, |
| 762 | code: *std.ArrayList(u8), | |
| 760 | code: *std.ArrayListUnmanaged(u8), | |
| 763 | 761 | debug_output: link.File.DebugInfoOutput, |
| 764 | ) CodeGenError!Result { | |
| 762 | ) CodeGenError!void { | |
| 765 | 763 | const zcu = pt.zcu; |
| 766 | 764 | const comp = zcu.comp; |
| 767 | 765 | const gpa = zcu.gpa; |
| ... | ... | @@ -788,7 +786,6 @@ pub fn generate( |
| 788 | 786 | .target = &mod.resolved_target.result, |
| 789 | 787 | .debug_output = debug_output, |
| 790 | 788 | .owner = .{ .nav_index = func.owner_nav }, |
| 791 | .err_msg = null, | |
| 792 | 789 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 793 | 790 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 794 | 791 | .fn_type = fn_type, |
| ... | ... | @@ -829,10 +826,7 @@ pub fn generate( |
| 829 | 826 | |
| 830 | 827 | const fn_info = zcu.typeToFunc(fn_type).?; |
| 831 | 828 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) { |
| 832 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 829 | error.CodegenFail => return error.CodegenFail, | |
| 836 | 830 | else => |e| return e, |
| 837 | 831 | }; |
| 838 | 832 | |
| ... | ... | @@ -861,10 +855,8 @@ pub fn generate( |
| 861 | 855 | })); |
| 862 | 856 | |
| 863 | 857 | function.gen() catch |err| switch (err) { |
| 864 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 865 | error.OutOfRegisters => return Result{ | |
| 866 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 867 | }, | |
| 858 | error.CodegenFail => return error.CodegenFail, | |
| 859 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 868 | 860 | else => |e| return e, |
| 869 | 861 | }; |
| 870 | 862 | |
| ... | ... | @@ -895,28 +887,10 @@ pub fn generate( |
| 895 | 887 | defer emit.deinit(); |
| 896 | 888 | |
| 897 | 889 | emit.emitMir() catch |err| switch (err) { |
| 898 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | |
| 899 | error.InvalidInstruction => |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 | }, | |
| 890 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), | |
| 891 | error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), | |
| 912 | 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 | 896 | pub fn generateLazy( |
| ... | ... | @@ -924,9 +898,9 @@ pub fn generateLazy( |
| 924 | 898 | pt: Zcu.PerThread, |
| 925 | 899 | src_loc: Zcu.LazySrcLoc, |
| 926 | 900 | lazy_sym: link.File.LazySymbol, |
| 927 | code: *std.ArrayList(u8), | |
| 901 | code: *std.ArrayListUnmanaged(u8), | |
| 928 | 902 | debug_output: link.File.DebugInfoOutput, |
| 929 | ) CodeGenError!Result { | |
| 903 | ) CodeGenError!void { | |
| 930 | 904 | const comp = bin_file.comp; |
| 931 | 905 | const gpa = comp.gpa; |
| 932 | 906 | const mod = comp.root_mod; |
| ... | ... | @@ -941,7 +915,6 @@ pub fn generateLazy( |
| 941 | 915 | .target = &mod.resolved_target.result, |
| 942 | 916 | .debug_output = debug_output, |
| 943 | 917 | .owner = .{ .lazy_sym = lazy_sym }, |
| 944 | .err_msg = null, | |
| 945 | 918 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 946 | 919 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 947 | 920 | .fn_type = undefined, |
| ... | ... | @@ -957,10 +930,8 @@ pub fn generateLazy( |
| 957 | 930 | defer function.mir_instructions.deinit(gpa); |
| 958 | 931 | |
| 959 | 932 | function.genLazy(lazy_sym) catch |err| switch (err) { |
| 960 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 961 | error.OutOfRegisters => return Result{ | |
| 962 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 963 | }, | |
| 933 | error.CodegenFail => return error.CodegenFail, | |
| 934 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 964 | 935 | else => |e| return e, |
| 965 | 936 | }; |
| 966 | 937 | |
| ... | ... | @@ -991,28 +962,10 @@ pub fn generateLazy( |
| 991 | 962 | defer emit.deinit(); |
| 992 | 963 | |
| 993 | 964 | emit.emitMir() catch |err| switch (err) { |
| 994 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | |
| 995 | error.InvalidInstruction => |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 | }, | |
| 965 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), | |
| 966 | error.InvalidInstruction => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), | |
| 1008 | 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 | 971 | const FormatWipMirData = struct { |
| ... | ... | @@ -4758,19 +4711,19 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4758 | 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 | 4715 | const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; |
| 4763 | 4716 | const ty = arg.ty.toType(); |
| 4764 | 4717 | if (arg.name == .none) return; |
| 4765 | 4718 | |
| 4766 | 4719 | switch (func.debug_output) { |
| 4767 | 4720 | .dwarf => |dw| switch (mcv) { |
| 4768 | .register => |reg| try dw.genLocalDebugInfo( | |
| 4721 | .register => |reg| dw.genLocalDebugInfo( | |
| 4769 | 4722 | .local_arg, |
| 4770 | 4723 | arg.name.toSlice(func.air), |
| 4771 | 4724 | ty, |
| 4772 | 4725 | .{ .reg = reg.dwarfNum() }, |
| 4773 | ), | |
| 4726 | ) catch |err| return func.fail("failed to generate debug info: {s}", .{@errorName(err)}), | |
| 4774 | 4727 | .load_frame => {}, |
| 4775 | 4728 | else => {}, |
| 4776 | 4729 | }, |
| ... | ... | @@ -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 | 4736 | var arg_index = func.arg_index; |
| 4784 | 4737 | |
| 4785 | 4738 | // we skip over args that have no bits |
| ... | ... | @@ -5255,7 +5208,7 @@ fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void { |
| 5255 | 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 | 5212 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 5260 | 5213 | const operand = pl_op.operand; |
| 5261 | 5214 | const ty = func.typeOf(operand); |
| ... | ... | @@ -5263,7 +5216,8 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void { |
| 5263 | 5216 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); |
| 5264 | 5217 | |
| 5265 | 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 | 5222 | return func.finishAir(inst, .unreach, .{ operand, .none, .none }); |
| 5269 | 5223 | } |
| ... | ... | @@ -8236,10 +8190,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { |
| 8236 | 8190 | return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); |
| 8237 | 8191 | }, |
| 8238 | 8192 | }, |
| 8239 | .fail => |msg| { | |
| 8240 | func.err_msg = msg; | |
| 8241 | return error.CodegenFail; | |
| 8242 | }, | |
| 8193 | .fail => |msg| return func.failMsg(msg), | |
| 8243 | 8194 | }; |
| 8244 | 8195 | return mcv; |
| 8245 | 8196 | } |
| ... | ... | @@ -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 | 8382 | @branchHint(.cold); |
| 8432 | assert(func.err_msg == null); | |
| 8433 | func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args); | |
| 8383 | const zcu = func.pt.zcu; | |
| 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 | 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 | 8392 | @branchHint(.cold); |
| 8439 | assert(func.err_msg == null); | |
| 8440 | func.err_msg = try ErrorMsg.create(func.gpa, func.src_loc, format, args); | |
| 8393 | const zcu = func.pt.zcu; | |
| 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 | 8398 | return error.CodegenFail; |
| 8442 | 8399 | } |
| 8443 | 8400 |
src/arch/riscv64/Emit.zig+9-8| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | bin_file: *link.File, |
| 4 | 4 | lower: Lower, |
| 5 | 5 | debug_output: link.File.DebugInfoOutput, |
| 6 | code: *std.ArrayList(u8), | |
| 6 | code: *std.ArrayListUnmanaged(u8), | |
| 7 | 7 | |
| 8 | 8 | prev_di_line: u32, |
| 9 | 9 | prev_di_column: u32, |
| ... | ... | @@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{ |
| 18 | 18 | }; |
| 19 | 19 | |
| 20 | 20 | pub fn emitMir(emit: *Emit) Error!void { |
| 21 | const gpa = emit.bin_file.comp.gpa; | |
| 21 | 22 | log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len}); |
| 22 | 23 | for (0..emit.lower.mir.instructions.len) |mir_i| { |
| 23 | 24 | const mir_index: Mir.Inst.Index = @intCast(mir_i); |
| ... | ... | @@ -30,7 +31,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 30 | 31 | var lowered_relocs = lowered.relocs; |
| 31 | 32 | for (lowered.insts, 0..) |lowered_inst, lowered_index| { |
| 32 | 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 | 36 | while (lowered_relocs.len > 0 and |
| 36 | 37 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ |
| ... | ... | @@ -56,13 +57,13 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 56 | 57 | const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); |
| 57 | 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 | 61 | .r_offset = start_offset, |
| 61 | 62 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, |
| 62 | 63 | .r_addend = 0, |
| 63 | 64 | }, zo); |
| 64 | 65 | |
| 65 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | |
| 66 | try atom_ptr.addReloc(gpa, .{ | |
| 66 | 67 | .r_offset = start_offset + 4, |
| 67 | 68 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, |
| 68 | 69 | .r_addend = 0, |
| ... | ... | @@ -76,19 +77,19 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 76 | 77 | |
| 77 | 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 | 81 | .r_offset = start_offset, |
| 81 | 82 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20), |
| 82 | 83 | .r_addend = 0, |
| 83 | 84 | }, zo); |
| 84 | 85 | |
| 85 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | |
| 86 | try atom_ptr.addReloc(gpa, .{ | |
| 86 | 87 | .r_offset = start_offset + 4, |
| 87 | 88 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD), |
| 88 | 89 | .r_addend = 0, |
| 89 | 90 | }, zo); |
| 90 | 91 | |
| 91 | try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ | |
| 92 | try atom_ptr.addReloc(gpa, .{ | |
| 92 | 93 | .r_offset = start_offset + 8, |
| 93 | 94 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I), |
| 94 | 95 | .r_addend = 0, |
| ... | ... | @@ -101,7 +102,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 101 | 102 | |
| 102 | 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 | 106 | .r_offset = start_offset, |
| 106 | 107 | .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, |
| 107 | 108 | .r_addend = 0, |
src/arch/sparc64/CodeGen.zig+24-28| ... | ... | @@ -21,7 +21,6 @@ const Emit = @import("Emit.zig"); |
| 21 | 21 | const Liveness = @import("../../Liveness.zig"); |
| 22 | 22 | const Type = @import("../../Type.zig"); |
| 23 | 23 | const CodeGenError = codegen.CodeGenError; |
| 24 | const Result = @import("../../codegen.zig").Result; | |
| 25 | 24 | const Endian = std.builtin.Endian; |
| 26 | 25 | const Alignment = InternPool.Alignment; |
| 27 | 26 | |
| ... | ... | @@ -55,7 +54,7 @@ liveness: Liveness, |
| 55 | 54 | bin_file: *link.File, |
| 56 | 55 | target: *const std.Target, |
| 57 | 56 | func_index: InternPool.Index, |
| 58 | code: *std.ArrayList(u8), | |
| 57 | code: *std.ArrayListUnmanaged(u8), | |
| 59 | 58 | debug_output: link.File.DebugInfoOutput, |
| 60 | 59 | err_msg: ?*ErrorMsg, |
| 61 | 60 | args: []MCValue, |
| ... | ... | @@ -266,9 +265,9 @@ pub fn generate( |
| 266 | 265 | func_index: InternPool.Index, |
| 267 | 266 | air: Air, |
| 268 | 267 | liveness: Liveness, |
| 269 | code: *std.ArrayList(u8), | |
| 268 | code: *std.ArrayListUnmanaged(u8), | |
| 270 | 269 | debug_output: link.File.DebugInfoOutput, |
| 271 | ) CodeGenError!Result { | |
| 270 | ) CodeGenError!void { | |
| 272 | 271 | const zcu = pt.zcu; |
| 273 | 272 | const gpa = zcu.gpa; |
| 274 | 273 | const func = zcu.funcInfo(func_index); |
| ... | ... | @@ -284,7 +283,7 @@ pub fn generate( |
| 284 | 283 | } |
| 285 | 284 | try branch_stack.append(.{}); |
| 286 | 285 | |
| 287 | var function = Self{ | |
| 286 | var function: Self = .{ | |
| 288 | 287 | .gpa = gpa, |
| 289 | 288 | .pt = pt, |
| 290 | 289 | .air = air, |
| ... | ... | @@ -310,10 +309,7 @@ pub fn generate( |
| 310 | 309 | defer function.exitlude_jump_relocs.deinit(gpa); |
| 311 | 310 | |
| 312 | 311 | var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) { |
| 313 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 312 | error.CodegenFail => return error.CodegenFail, | |
| 317 | 313 | else => |e| return e, |
| 318 | 314 | }; |
| 319 | 315 | defer call_info.deinit(&function); |
| ... | ... | @@ -324,10 +320,8 @@ pub fn generate( |
| 324 | 320 | function.max_end_stack = call_info.stack_byte_count; |
| 325 | 321 | |
| 326 | 322 | function.gen() catch |err| switch (err) { |
| 327 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 328 | error.OutOfRegisters => return Result{ | |
| 329 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 330 | }, | |
| 323 | error.CodegenFail => return error.CodegenFail, | |
| 324 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 331 | 325 | else => |e| return e, |
| 332 | 326 | }; |
| 333 | 327 | |
| ... | ... | @@ -337,7 +331,7 @@ pub fn generate( |
| 337 | 331 | }; |
| 338 | 332 | defer mir.deinit(gpa); |
| 339 | 333 | |
| 340 | var emit = Emit{ | |
| 334 | var emit: Emit = .{ | |
| 341 | 335 | .mir = mir, |
| 342 | 336 | .bin_file = lf, |
| 343 | 337 | .debug_output = debug_output, |
| ... | ... | @@ -351,15 +345,9 @@ pub fn generate( |
| 351 | 345 | defer emit.deinit(); |
| 352 | 346 | |
| 353 | 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 | 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 | 353 | fn gen(self: *Self) !void { |
| ... | ... | @@ -1014,7 +1002,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1014 | 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 | 1006 | const pt = self.pt; |
| 1019 | 1007 | const zcu = pt.zcu; |
| 1020 | 1008 | const arg_index = self.arg_index; |
| ... | ... | @@ -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 | 1030 | if (self.liveness.isUnused(inst)) |
| 1042 | 1031 | return self.finishAirBookkeeping(); |
| ... | ... | @@ -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 | 3504 | @branchHint(.cold); |
| 3516 | assert(self.err_msg == null); | |
| 3517 | const gpa = self.gpa; | |
| 3518 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | |
| 3519 | return error.CodegenFail; | |
| 3505 | const zcu = self.pt.zcu; | |
| 3506 | const func = zcu.funcInfo(self.func_index); | |
| 3507 | const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args); | |
| 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 | 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 | 22 | target: *const std.Target, |
| 23 | 23 | err_msg: ?*ErrorMsg = null, |
| 24 | 24 | src_loc: Zcu.LazySrcLoc, |
| 25 | code: *std.ArrayList(u8), | |
| 25 | code: *std.ArrayListUnmanaged(u8), | |
| 26 | 26 | |
| 27 | 27 | prev_di_line: u32, |
| 28 | 28 | prev_di_column: u32, |
| ... | ... | @@ -678,10 +678,13 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { |
| 678 | 678 | } |
| 679 | 679 | |
| 680 | 680 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 681 | const comp = emit.bin_file.comp; | |
| 682 | const gpa = comp.gpa; | |
| 683 | ||
| 681 | 684 | // SPARCv9 instructions are always arranged in BE regardless of the |
| 682 | 685 | // endianness mode the CPU is running in (Section 3.1 of the ISA specification). |
| 683 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | const ArrayList = std.ArrayList; | |
| 5 | 4 | const assert = std.debug.assert; |
| 6 | 5 | const testing = std.testing; |
| 7 | 6 | const leb = std.leb; |
| 8 | 7 | const mem = std.mem; |
| 9 | const wasm = std.wasm; | |
| 10 | 8 | const log = std.log.scoped(.codegen); |
| 11 | 9 | |
| 10 | const CodeGen = @This(); | |
| 12 | 11 | const codegen = @import("../../codegen.zig"); |
| 13 | 12 | const Zcu = @import("../../Zcu.zig"); |
| 14 | 13 | const InternPool = @import("../../InternPool.zig"); |
| ... | ... | @@ -19,13 +18,113 @@ const Compilation = @import("../../Compilation.zig"); |
| 19 | 18 | const link = @import("../../link.zig"); |
| 20 | 19 | const Air = @import("../../Air.zig"); |
| 21 | 20 | const Liveness = @import("../../Liveness.zig"); |
| 22 | const target_util = @import("../../target.zig"); | |
| 23 | 21 | const Mir = @import("Mir.zig"); |
| 24 | 22 | const Emit = @import("Emit.zig"); |
| 25 | 23 | const abi = @import("abi.zig"); |
| 26 | 24 | const Alignment = InternPool.Alignment; |
| 27 | 25 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 28 | 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 | 129 | /// Wasm Value, created when generating an instruction |
| 31 | 130 | const WValue = union(enum) { |
| ... | ... | @@ -55,22 +154,15 @@ const WValue = union(enum) { |
| 55 | 154 | float32: f32, |
| 56 | 155 | /// A constant 64bit float value |
| 57 | 156 | float64: f64, |
| 58 | /// A value that represents a pointer to the data section | |
| 59 | /// Note: The value contains the symbol index, rather than the actual address | |
| 60 | /// as we use this to perform the relocation. | |
| 61 | memory: u32, | |
| 62 | /// A value that represents a parent pointer and an offset | |
| 63 | /// from that pointer. i.e. when slicing with constant values. | |
| 64 | memory_offset: struct { | |
| 65 | /// The symbol of the parent pointer | |
| 66 | pointer: u32, | |
| 67 | /// Offset will be set as addend when relocating | |
| 68 | offset: u32, | |
| 157 | nav_ref: struct { | |
| 158 | nav_index: InternPool.Nav.Index, | |
| 159 | offset: i32 = 0, | |
| 160 | }, | |
| 161 | uav_ref: struct { | |
| 162 | ip_index: InternPool.Index, | |
| 163 | offset: i32 = 0, | |
| 164 | orig_ptr_ty: InternPool.Index = .none, | |
| 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 | 166 | /// Offset from the bottom of the virtual stack, with the offset |
| 75 | 167 | /// pointing to where the value lives. |
| 76 | 168 | stack_offset: struct { |
| ... | ... | @@ -101,7 +193,7 @@ const WValue = union(enum) { |
| 101 | 193 | switch (value) { |
| 102 | 194 | .stack => { |
| 103 | 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 | 197 | return new_local; |
| 106 | 198 | }, |
| 107 | 199 | .local, .stack_offset => return value, |
| ... | ... | @@ -119,7 +211,7 @@ const WValue = union(enum) { |
| 119 | 211 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 120 | 212 | |
| 121 | 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 | 215 | switch (valtype) { |
| 124 | 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 | 217 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| ... | ... | @@ -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 | 227 | const Op = enum { |
| 138 | 228 | @"unreachable", |
| 139 | 229 | nop, |
| ... | ... | @@ -147,12 +237,8 @@ const Op = enum { |
| 147 | 237 | br_table, |
| 148 | 238 | @"return", |
| 149 | 239 | call, |
| 150 | call_indirect, | |
| 151 | 240 | drop, |
| 152 | 241 | select, |
| 153 | local_get, | |
| 154 | local_set, | |
| 155 | local_tee, | |
| 156 | 242 | global_get, |
| 157 | 243 | global_set, |
| 158 | 244 | load, |
| ... | ... | @@ -200,70 +286,38 @@ const Op = enum { |
| 200 | 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 | 289 | const OpcodeBuildArguments = struct { |
| 232 | 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 | 292 | /// The operation (e.g. call, unreachable, div, min, sqrt, etc.) |
| 235 | 293 | op: Op, |
| 236 | 294 | /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s) |
| 237 | 295 | width: ?u8 = null, |
| 238 | 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 | 298 | /// Signedness of the op |
| 241 | 299 | signedness: ?std.builtin.Signedness = null, |
| 242 | 300 | }; |
| 243 | 301 | |
| 244 | /// Helper function that builds an Opcode given the arguments needed | |
| 245 | fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | |
| 302 | /// TODO: deprecated, should be split up per tag. | |
| 303 | fn buildOpcode(args: OpcodeBuildArguments) std.wasm.Opcode { | |
| 246 | 304 | switch (args.op) { |
| 247 | .@"unreachable" => return .@"unreachable", | |
| 248 | .nop => return .nop, | |
| 249 | .block => return .block, | |
| 250 | .loop => return .loop, | |
| 251 | .@"if" => return .@"if", | |
| 252 | .@"else" => return .@"else", | |
| 253 | .end => return .end, | |
| 254 | .br => return .br, | |
| 255 | .br_if => return .br_if, | |
| 256 | .br_table => return .br_table, | |
| 257 | .@"return" => return .@"return", | |
| 258 | .call => return .call, | |
| 259 | .call_indirect => return .call_indirect, | |
| 260 | .drop => return .drop, | |
| 261 | .select => return .select, | |
| 262 | .local_get => return .local_get, | |
| 263 | .local_set => return .local_set, | |
| 264 | .local_tee => return .local_tee, | |
| 265 | .global_get => return .global_get, | |
| 266 | .global_set => return .global_set, | |
| 305 | .@"unreachable" => unreachable, | |
| 306 | .nop => unreachable, | |
| 307 | .block => unreachable, | |
| 308 | .loop => unreachable, | |
| 309 | .@"if" => unreachable, | |
| 310 | .@"else" => unreachable, | |
| 311 | .end => unreachable, | |
| 312 | .br => unreachable, | |
| 313 | .br_if => unreachable, | |
| 314 | .br_table => unreachable, | |
| 315 | .@"return" => unreachable, | |
| 316 | .call => unreachable, | |
| 317 | .drop => unreachable, | |
| 318 | .select => unreachable, | |
| 319 | .global_get => unreachable, | |
| 320 | .global_set => unreachable, | |
| 267 | 321 | |
| 268 | 322 | .load => if (args.width) |width| switch (width) { |
| 269 | 323 | 8 => switch (args.valtype1.?) { |
| ... | ... | @@ -621,121 +675,17 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 621 | 675 | test "Wasm - buildOpcode" { |
| 622 | 676 | // Make sure buildOpcode is referenced, and test some examples |
| 623 | 677 | const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 }); |
| 624 | const end = buildOpcode(.{ .op = .end }); | |
| 625 | const local_get = buildOpcode(.{ .op = .local_get }); | |
| 626 | 678 | const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed }); |
| 627 | 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); | |
| 630 | try testing.expectEqual(@as(wasm.Opcode, .end), end); | |
| 631 | try testing.expectEqual(@as(wasm.Opcode, .local_get), local_get); | |
| 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); | |
| 681 | try testing.expectEqual(@as(std.wasm.Opcode, .i32_const), i32_const); | |
| 682 | try testing.expectEqual(@as(std.wasm.Opcode, .i64_extend32_s), i64_extend32_s); | |
| 683 | try testing.expectEqual(@as(std.wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64); | |
| 634 | 684 | } |
| 635 | 685 | |
| 636 | 686 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 637 | 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 | 689 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 740 | 690 | |
| 741 | 691 | const InnerError = error{ |
| ... | ... | @@ -746,38 +696,33 @@ const InnerError = error{ |
| 746 | 696 | Overflow, |
| 747 | 697 | } || link.File.UpdateDebugInfoError; |
| 748 | 698 | |
| 749 | pub fn deinit(func: *CodeGen) void { | |
| 750 | // in case of an error and we still have branches | |
| 751 | for (func.branches.items) |*branch| { | |
| 752 | branch.deinit(func.gpa); | |
| 753 | } | |
| 754 | func.branches.deinit(func.gpa); | |
| 755 | func.blocks.deinit(func.gpa); | |
| 756 | func.loops.deinit(func.gpa); | |
| 757 | func.locals.deinit(func.gpa); | |
| 758 | func.simd_immediates.deinit(func.gpa); | |
| 759 | func.mir_instructions.deinit(func.gpa); | |
| 760 | func.mir_extra.deinit(func.gpa); | |
| 761 | func.free_locals_i32.deinit(func.gpa); | |
| 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; | |
| 699 | pub fn deinit(cg: *CodeGen) void { | |
| 700 | const gpa = cg.gpa; | |
| 701 | for (cg.branches.items) |*branch| branch.deinit(gpa); | |
| 702 | cg.branches.deinit(gpa); | |
| 703 | cg.blocks.deinit(gpa); | |
| 704 | cg.loops.deinit(gpa); | |
| 705 | cg.simd_immediates.deinit(gpa); | |
| 706 | cg.free_locals_i32.deinit(gpa); | |
| 707 | cg.free_locals_i64.deinit(gpa); | |
| 708 | cg.free_locals_f32.deinit(gpa); | |
| 709 | cg.free_locals_f64.deinit(gpa); | |
| 710 | cg.free_locals_v128.deinit(gpa); | |
| 711 | cg.* = undefined; | |
| 767 | 712 | } |
| 768 | 713 | |
| 769 | /// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig | |
| 770 | fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError { | |
| 771 | func.err_msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args); | |
| 772 | return error.CodegenFail; | |
| 714 | fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { | |
| 715 | const zcu = cg.pt.zcu; | |
| 716 | const func = zcu.funcInfo(cg.func_index); | |
| 717 | return zcu.codegenFail(func.owner_nav, fmt, args); | |
| 773 | 718 | } |
| 774 | 719 | |
| 775 | 720 | /// Resolves the `WValue` for the given instruction `inst` |
| 776 | 721 | /// When the given instruction has a `Value`, it returns a constant instead |
| 777 | fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | |
| 778 | var branch_index = func.branches.items.len; | |
| 722 | fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | |
| 723 | var branch_index = cg.branches.items.len; | |
| 779 | 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 | 726 | if (branch.values.get(ref)) |value| { |
| 782 | 727 | return value; |
| 783 | 728 | } |
| ... | ... | @@ -787,16 +732,16 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 787 | 732 | // means we must generate it from a constant. |
| 788 | 733 | // We always store constants in the most outer branch as they must never |
| 789 | 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 | 736 | assert(!gop.found_existing); |
| 792 | 737 | |
| 793 | const pt = func.pt; | |
| 738 | const pt = cg.pt; | |
| 794 | 739 | const zcu = pt.zcu; |
| 795 | const val = (try func.air.value(ref, pt)).?; | |
| 796 | const ty = func.typeOf(ref); | |
| 740 | const val = (try cg.air.value(ref, pt)).?; | |
| 741 | const ty = cg.typeOf(ref); | |
| 797 | 742 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) { |
| 798 | 743 | gop.value_ptr.* = .none; |
| 799 | return gop.value_ptr.*; | |
| 744 | return .none; | |
| 800 | 745 | } |
| 801 | 746 | |
| 802 | 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 | 750 | // |
| 806 | 751 | // In the other cases, we will simply lower the constant to a value that fits |
| 807 | 752 | // into a single local (such as a pointer, integer, bool, etc). |
| 808 | const result: WValue = if (isByRef(ty, pt, func.target.*)) | |
| 809 | switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) { | |
| 810 | .mcv => |mcv| .{ .memory = mcv.load_symbol }, | |
| 811 | .fail => |err_msg| { | |
| 812 | func.err_msg = err_msg; | |
| 813 | return error.CodegenFail; | |
| 814 | }, | |
| 815 | } | |
| 753 | const result: WValue = if (isByRef(ty, zcu, cg.target)) | |
| 754 | .{ .uav_ref = .{ .ip_index = val.toIntern() } } | |
| 816 | 755 | else |
| 817 | try func.lowerConstant(val, ty); | |
| 756 | try cg.lowerConstant(val, ty); | |
| 818 | 757 | |
| 819 | 758 | gop.value_ptr.* = result; |
| 820 | 759 | return result; |
| 821 | 760 | } |
| 822 | 761 | |
| 823 | 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 | 764 | assert(operands.len <= Liveness.bpi - 1); |
| 826 | var tomb_bits = func.liveness.getTombBits(inst); | |
| 765 | var tomb_bits = cg.liveness.getTombBits(inst); | |
| 827 | 766 | for (operands) |operand| { |
| 828 | 767 | const dies = @as(u1, @truncate(tomb_bits)) != 0; |
| 829 | 768 | tomb_bits >>= 1; |
| 830 | 769 | if (!dies) continue; |
| 831 | processDeath(func, operand); | |
| 770 | processDeath(cg, operand); | |
| 832 | 771 | } |
| 833 | 772 | |
| 834 | 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 | 775 | const trackable_result = if (result != .stack) |
| 837 | 776 | result |
| 838 | 777 | else |
| 839 | try result.toLocal(func, func.typeOfIndex(inst)); | |
| 840 | const branch = func.currentBranch(); | |
| 778 | try result.toLocal(cg, cg.typeOfIndex(inst)); | |
| 779 | const branch = cg.currentBranch(); | |
| 841 | 780 | branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result); |
| 842 | 781 | } |
| 843 | 782 | |
| 844 | 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 | 794 | } |
| 856 | 795 | }; |
| 857 | 796 | |
| 858 | inline fn currentBranch(func: *CodeGen) *Branch { | |
| 859 | return &func.branches.items[func.branches.items.len - 1]; | |
| 797 | inline fn currentBranch(cg: *CodeGen) *Branch { | |
| 798 | return &cg.branches.items[cg.branches.items.len - 1]; | |
| 860 | 799 | } |
| 861 | 800 | |
| 862 | 801 | 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 { | |
| 887 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, operand_count + 1); | |
| 825 | fn iterateBigTomb(cg: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb { | |
| 826 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, operand_count + 1); | |
| 888 | 827 | return BigTomb{ |
| 889 | .gen = func, | |
| 828 | .gen = cg, | |
| 890 | 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 | 835 | if (ref.toIndex() == null) return; |
| 897 | 836 | // Branches are currently only allowed to free locals allocated |
| 898 | 837 | // within their own branch. |
| 899 | 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 | 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 | 842 | if (value.local.value < reserved_indexes) { |
| 904 | 843 | return; // function arguments can never be re-used |
| 905 | 844 | } |
| 906 | 845 | log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @intFromEnum(ref.toIndex().?), value.local.value }); |
| 907 | 846 | value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer |
| 908 | 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 | |
| 914 | fn addInst(func: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { | |
| 915 | try func.mir_instructions.append(func.gpa, inst); | |
| 852 | fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { | |
| 853 | try cg.mir_instructions.append(cg.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 { | |
| 919 | try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); | |
| 860 | fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void { | |
| 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 { | |
| 923 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | |
| 924 | try func.mir_extra.append(func.gpa, @intFromEnum(opcode)); | |
| 925 | try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } }); | |
| 866 | fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void { | |
| 867 | try cg.addInst(.{ .tag = tag, .data = .{ .label = label } }); | |
| 926 | 868 | } |
| 927 | 869 | |
| 928 | fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void { | |
| 929 | try func.addInst(.{ .tag = tag, .data = .{ .label = label } }); | |
| 870 | fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void { | |
| 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 | 878 | /// Accepts an unsigned 32bit integer rather than a signed integer to |
| 933 | 879 | /// prevent us from having to bitcast multiple times as most values |
| 934 | 880 | /// within codegen are represented as unsigned rather than signed. |
| 935 | fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void { | |
| 936 | try func.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } }); | |
| 881 | fn addImm32(cg: *CodeGen, imm: u32) error{OutOfMemory}!void { | |
| 882 | try cg.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } }); | |
| 937 | 883 | } |
| 938 | 884 | |
| 939 | 885 | /// Accepts an unsigned 64bit integer rather than a signed integer to |
| 940 | 886 | /// prevent us from having to bitcast multiple times as most values |
| 941 | 887 | /// within codegen are represented as unsigned rather than signed. |
| 942 | fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void { | |
| 943 | const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm)); | |
| 944 | try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); | |
| 888 | fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void { | |
| 889 | const extra_index = try cg.addExtra(Mir.Imm64.init(imm)); | |
| 890 | try cg.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); | |
| 945 | 891 | } |
| 946 | 892 | |
| 947 | 893 | /// Accepts the index into the list of 128bit-immediates |
| 948 | fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void { | |
| 949 | const simd_values = func.simd_immediates.items[index]; | |
| 950 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | |
| 894 | fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void { | |
| 895 | const simd_values = cg.simd_immediates.items[index]; | |
| 896 | const extra_index = cg.extraLen(); | |
| 951 | 897 | // tag + 128bit value |
| 952 | try func.mir_extra.ensureUnusedCapacity(func.gpa, 5); | |
| 953 | func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const)); | |
| 954 | func.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values))); | |
| 955 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 898 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5); | |
| 899 | cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const)); | |
| 900 | cg.mir_extra.appendSliceAssumeCapacity(@alignCast(mem.bytesAsSlice(u32, &simd_values))); | |
| 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 { | |
| 959 | const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float)); | |
| 960 | try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); | |
| 904 | fn addFloat64(cg: *CodeGen, float: f64) error{OutOfMemory}!void { | |
| 905 | const extra_index = try cg.addExtra(Mir.Float64.init(float)); | |
| 906 | try cg.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); | |
| 961 | 907 | } |
| 962 | 908 | |
| 963 | 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 { | |
| 965 | const extra_index = try func.addExtra(mem_arg); | |
| 966 | try func.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } }); | |
| 910 | fn addMemArg(cg: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void { | |
| 911 | const extra_index = try cg.addExtra(mem_arg); | |
| 912 | try cg.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } }); | |
| 967 | 913 | } |
| 968 | 914 | |
| 969 | 915 | /// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the |
| 970 | 916 | /// given `tag`. |
| 971 | fn addAtomicMemArg(func: *CodeGen, tag: wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void { | |
| 972 | const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) })); | |
| 973 | _ = try func.addExtra(mem_arg); | |
| 974 | try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); | |
| 917 | fn addAtomicMemArg(cg: *CodeGen, tag: std.wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void { | |
| 918 | const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) })); | |
| 919 | _ = try cg.addExtra(mem_arg); | |
| 920 | try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); | |
| 975 | 921 | } |
| 976 | 922 | |
| 977 | 923 | /// Helper function to emit atomic mir opcodes. |
| 978 | fn addAtomicTag(func: *CodeGen, tag: wasm.AtomicsOpcode) error{OutOfMemory}!void { | |
| 979 | const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) })); | |
| 980 | try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); | |
| 924 | fn addAtomicTag(cg: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void { | |
| 925 | const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) })); | |
| 926 | try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } }); | |
| 981 | 927 | } |
| 982 | 928 | |
| 983 | 929 | /// Appends entries to `mir_extra` based on the type of `extra`. |
| 984 | 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 | 932 | const fields = std.meta.fields(@TypeOf(extra)); |
| 987 | try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len); | |
| 988 | return func.addExtraAssumeCapacity(extra); | |
| 933 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, fields.len); | |
| 934 | return cg.addExtraAssumeCapacity(extra); | |
| 989 | 935 | } |
| 990 | 936 | |
| 991 | 937 | /// Appends entries to `mir_extra` based on the type of `extra`. |
| 992 | 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 | 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 | 942 | inline for (fields) |field| { |
| 997 | func.mir_extra.appendAssumeCapacity(switch (field.type) { | |
| 943 | cg.mir_extra.appendAssumeCapacity(switch (field.type) { | |
| 998 | 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 | 951 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 1000 | 952 | }); |
| 1001 | 953 | } |
| 1002 | 954 | return result; |
| 1003 | 955 | } |
| 1004 | 956 | |
| 1005 | /// Using a given `Type`, returns the corresponding valtype for .auto callconv | |
| 1006 | fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { | |
| 1007 | const zcu = pt.zcu; | |
| 957 | /// For `std.builtin.CallingConvention.auto`. | |
| 958 | pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.Valtype { | |
| 1008 | 959 | const ip = &zcu.intern_pool; |
| 1009 | 960 | return switch (ty.zigTypeTag(zcu)) { |
| 1010 | .float => switch (ty.floatBits(target)) { | |
| 961 | .float => switch (ty.floatBits(target.*)) { | |
| 1011 | 962 | 16 => .i32, // stored/loaded as u16 |
| 1012 | 963 | 32 => .f32, |
| 1013 | 964 | 64 => .f64, |
| ... | ... | @@ -1022,19 +973,20 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) wasm.Valtype { |
| 1022 | 973 | .@"struct" => blk: { |
| 1023 | 974 | if (zcu.typeToPackedStruct(ty)) |packed_struct| { |
| 1024 | 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 | 977 | } else { |
| 1027 | 978 | break :blk .i32; |
| 1028 | 979 | } |
| 1029 | 980 | }, |
| 1030 | .vector => switch (determineSimdStoreStrategy(ty, zcu, target)) { | |
| 981 | .vector => switch (CodeGen.determineSimdStoreStrategy(ty, zcu, target)) { | |
| 1031 | 982 | .direct => .v128, |
| 1032 | 983 | .unrolled => .i32, |
| 1033 | 984 | }, |
| 1034 | 985 | .@"union" => switch (ty.containerLayout(zcu)) { |
| 1035 | .@"packed" => blk: { | |
| 1036 | const int_ty = pt.intType(.unsigned, @as(u16, @intCast(ty.bitSize(zcu)))) catch @panic("out of memory"); | |
| 1037 | break :blk typeToValtype(int_ty, pt, target); | |
| 986 | .@"packed" => switch (ty.bitSize(zcu)) { | |
| 987 | 0...32 => .i32, | |
| 988 | 33...64 => .i64, | |
| 989 | else => .i32, | |
| 1038 | 990 | }, |
| 1039 | 991 | else => .i32, |
| 1040 | 992 | }, |
| ... | ... | @@ -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 | 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 | 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 | 1001 | return switch (ty.ip_index) { |
| 1055 | .void_type, .noreturn_type => wasm.block_empty, | |
| 1056 | else => genValtype(ty, pt, target), | |
| 1002 | .void_type, .noreturn_type => .empty, | |
| 1003 | else => .fromValtype(typeToValtype(ty, zcu, target)), | |
| 1057 | 1004 | }; |
| 1058 | 1005 | } |
| 1059 | 1006 | |
| 1060 | 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 | 1009 | switch (value) { |
| 1063 | 1010 | .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?) |
| 1064 | 1011 | .none, .stack => {}, // no-op |
| 1065 | .local => |idx| try func.addLabel(.local_get, idx.value), | |
| 1066 | .imm32 => |val| try func.addImm32(val), | |
| 1067 | .imm64 => |val| try func.addImm64(val), | |
| 1068 | .imm128 => |val| try func.addImm128(val), | |
| 1069 | .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | |
| 1070 | .float64 => |val| try func.addFloat64(val), | |
| 1071 | .memory => |ptr| { | |
| 1072 | const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 }); | |
| 1073 | try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); | |
| 1012 | .local => |idx| try cg.addLocal(.local_get, idx.value), | |
| 1013 | .imm32 => |val| try cg.addImm32(val), | |
| 1014 | .imm64 => |val| try cg.addImm64(val), | |
| 1015 | .imm128 => |val| try cg.addImm128(val), | |
| 1016 | .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | |
| 1017 | .float64 => |val| try cg.addFloat64(val), | |
| 1018 | .nav_ref => |nav_ref| { | |
| 1019 | const wasm = cg.wasm; | |
| 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| { | |
| 1076 | const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset }); | |
| 1077 | try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); | |
| 1045 | .uav_ref => |uav| { | |
| 1046 | const wasm = cg.wasm; | |
| 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 | |
| 1080 | .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset | |
| 1084 | .stack_offset => try cg.addLocal(.local_get, cg.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 | 1089 | /// The old `WValue` found at instruction `ref` is then replaced by the |
| 1086 | 1090 | /// modified `WValue` and returned. When given a non-local or non-stack-offset, |
| 1087 | 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 | 1093 | if (operand != .local and operand != .stack_offset) return operand; |
| 1090 | 1094 | var new_value = operand; |
| 1091 | 1095 | switch (new_value) { |
| ... | ... | @@ -1093,17 +1097,17 @@ fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue { |
| 1093 | 1097 | .stack_offset => |*stack_offset| stack_offset.references += 1, |
| 1094 | 1098 | else => unreachable, |
| 1095 | 1099 | } |
| 1096 | const old_value = func.getResolvedInst(ref); | |
| 1100 | const old_value = cg.getResolvedInst(ref); | |
| 1097 | 1101 | old_value.* = new_value; |
| 1098 | 1102 | return new_value; |
| 1099 | 1103 | } |
| 1100 | 1104 | |
| 1101 | 1105 | /// From a reference, returns its resolved `WValue`. |
| 1102 | 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 { | |
| 1104 | var index = func.branches.items.len; | |
| 1107 | fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { | |
| 1108 | var index = cg.branches.items.len; | |
| 1105 | 1109 | while (index > 0) : (index -= 1) { |
| 1106 | const branch = func.branches.items[index - 1]; | |
| 1110 | const branch = cg.branches.items[index - 1]; | |
| 1107 | 1111 | if (branch.values.getPtr(ref)) |value| { |
| 1108 | 1112 | return value; |
| 1109 | 1113 | } |
| ... | ... | @@ -1113,243 +1117,238 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 1113 | 1117 | |
| 1114 | 1118 | /// Creates one locals for a given `Type`. |
| 1115 | 1119 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 1116 | fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | |
| 1117 | const pt = func.pt; | |
| 1118 | const valtype = typeToValtype(ty, pt, func.target.*); | |
| 1120 | fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | |
| 1121 | const zcu = cg.pt.zcu; | |
| 1122 | const valtype = typeToValtype(ty, zcu, cg.target); | |
| 1119 | 1123 | const index_or_null = switch (valtype) { |
| 1120 | .i32 => func.free_locals_i32.popOrNull(), | |
| 1121 | .i64 => func.free_locals_i64.popOrNull(), | |
| 1122 | .f32 => func.free_locals_f32.popOrNull(), | |
| 1123 | .f64 => func.free_locals_f64.popOrNull(), | |
| 1124 | .v128 => func.free_locals_v128.popOrNull(), | |
| 1124 | .i32 => cg.free_locals_i32.popOrNull(), | |
| 1125 | .i64 => cg.free_locals_i64.popOrNull(), | |
| 1126 | .f32 => cg.free_locals_f32.popOrNull(), | |
| 1127 | .f64 => cg.free_locals_f64.popOrNull(), | |
| 1128 | .v128 => cg.free_locals_v128.popOrNull(), | |
| 1125 | 1129 | }; |
| 1126 | 1130 | if (index_or_null) |index| { |
| 1127 | 1131 | log.debug("reusing local ({d}) of type {}", .{ index, valtype }); |
| 1128 | 1132 | return .{ .local = .{ .value = index, .references = 1 } }; |
| 1129 | 1133 | } |
| 1130 | 1134 | log.debug("new local of type {}", .{valtype}); |
| 1131 | return func.ensureAllocLocal(ty); | |
| 1135 | return cg.ensureAllocLocal(ty); | |
| 1132 | 1136 | } |
| 1133 | 1137 | |
| 1134 | 1138 | /// Ensures a new local will be created. This is useful when it's useful |
| 1135 | 1139 | /// to use a zero-initialized local. |
| 1136 | fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | |
| 1137 | const pt = func.pt; | |
| 1138 | try func.locals.append(func.gpa, genValtype(ty, pt, func.target.*)); | |
| 1139 | const initial_index = func.local_index; | |
| 1140 | func.local_index += 1; | |
| 1140 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | |
| 1141 | const zcu = cg.pt.zcu; | |
| 1142 | try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); | |
| 1143 | const initial_index = cg.local_index; | |
| 1144 | cg.local_index += 1; | |
| 1141 | 1145 | return .{ .local = .{ .value = initial_index, .references = 1 } }; |
| 1142 | 1146 | } |
| 1143 | 1147 | |
| 1144 | /// Generates a `wasm.Type` from a given function type. | |
| 1145 | /// Memory is owned by the caller. | |
| 1146 | fn genFunctype( | |
| 1147 | gpa: Allocator, | |
| 1148 | cc: std.builtin.CallingConvention, | |
| 1149 | params: []const InternPool.Index, | |
| 1150 | return_type: Type, | |
| 1151 | pt: Zcu.PerThread, | |
| 1152 | target: std.Target, | |
| 1153 | ) !wasm.Type { | |
| 1154 | const zcu = pt.zcu; | |
| 1155 | var temp_params = std.ArrayList(wasm.Valtype).init(gpa); | |
| 1156 | defer temp_params.deinit(); | |
| 1157 | var returns = std.ArrayList(wasm.Valtype).init(gpa); | |
| 1158 | defer returns.deinit(); | |
| 1159 | ||
| 1160 | if (firstParamSRet(cc, return_type, pt, target)) { | |
| 1161 | try temp_params.append(.i32); // memory address is always a 32-bit handle | |
| 1162 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { | |
| 1163 | if (cc == .wasm_watc) { | |
| 1164 | const res_classes = abi.classifyType(return_type, zcu); | |
| 1165 | assert(res_classes[0] == .direct and res_classes[1] == .none); | |
| 1166 | const scalar_type = abi.scalarType(return_type, zcu); | |
| 1167 | try returns.append(typeToValtype(scalar_type, pt, target)); | |
| 1168 | } else { | |
| 1169 | try returns.append(typeToValtype(return_type, pt, target)); | |
| 1148 | pub const Function = extern struct { | |
| 1149 | /// Index into `Wasm.mir_instructions`. | |
| 1150 | mir_off: u32, | |
| 1151 | /// This is unused except for as a safety slice bound and could be removed. | |
| 1152 | mir_len: u32, | |
| 1153 | /// Index into `Wasm.mir_extra`. | |
| 1154 | mir_extra_off: u32, | |
| 1155 | /// This is unused except for as a safety slice bound and could be removed. | |
| 1156 | mir_extra_len: u32, | |
| 1157 | locals_off: u32, | |
| 1158 | locals_len: u32, | |
| 1159 | prologue: Prologue, | |
| 1160 | ||
| 1161 | pub const Prologue = extern struct { | |
| 1162 | flags: Flags, | |
| 1163 | sp_local: u32, | |
| 1164 | stack_size: u32, | |
| 1165 | bottom_stack_local: u32, | |
| 1166 | ||
| 1167 | pub const Flags = packed struct(u32) { | |
| 1168 | stack_alignment: Alignment, | |
| 1169 | padding: u26 = 0, | |
| 1170 | }; | |
| 1171 | ||
| 1172 | pub const none: Prologue = .{ | |
| 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)) { | |
| 1172 | try returns.append(.i32); | |
| 1173 | } | |
| 1174 | ||
| 1175 | // param types | |
| 1176 | for (params) |param_type_ip| { | |
| 1177 | const param_type = Type.fromInterned(param_type_ip); | |
| 1178 | if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; | |
| 1179 | ||
| 1180 | switch (cc) { | |
| 1181 | .wasm_watc => { | |
| 1182 | const param_classes = abi.classifyType(param_type, zcu); | |
| 1183 | if (param_classes[1] == .none) { | |
| 1184 | if (param_classes[0] == .direct) { | |
| 1185 | const scalar_type = abi.scalarType(param_type, zcu); | |
| 1186 | try temp_params.append(typeToValtype(scalar_type, pt, target)); | |
| 1187 | } else { | |
| 1188 | try temp_params.append(typeToValtype(param_type, pt, target)); | |
| 1189 | } | |
| 1190 | } else { | |
| 1191 | // i128/f128 | |
| 1192 | try temp_params.append(.i64); | |
| 1193 | try temp_params.append(.i64); | |
| 1194 | } | |
| 1195 | }, | |
| 1196 | else => try temp_params.append(typeToValtype(param_type, pt, target)), | |
| 1182 | }; | |
| 1183 | ||
| 1184 | pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { | |
| 1185 | const gpa = wasm.base.comp.gpa; | |
| 1186 | ||
| 1187 | // Write the locals in the prologue of the function body. | |
| 1188 | const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len]; | |
| 1189 | try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38); | |
| 1190 | ||
| 1191 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable; | |
| 1192 | for (locals) |local| { | |
| 1193 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; | |
| 1194 | code.appendAssumeCapacity(@intFromEnum(local)); | |
| 1195 | } | |
| 1196 | ||
| 1197 | // Stack management section of function prologue. | |
| 1198 | const stack_alignment = f.prologue.flags.stack_alignment; | |
| 1199 | if (stack_alignment.toByteUnits()) |align_bytes| { | |
| 1200 | const sp_global: Wasm.GlobalIndex = .stack_pointer; | |
| 1201 | // load stack pointer | |
| 1202 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); | |
| 1203 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | |
| 1204 | // store stack pointer so we can restore it when we return from the function | |
| 1205 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); | |
| 1206 | leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable; | |
| 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{ | |
| 1201 | .params = try temp_params.toOwnedSlice(), | |
| 1202 | .returns = try returns.toOwnedSlice(), | |
| 1203 | }; | |
| 1204 | } | |
| 1242 | pub const Error = error{ | |
| 1243 | OutOfMemory, | |
| 1244 | /// Compiler was asked to operate on a number larger than supported. | |
| 1245 | Overflow, | |
| 1246 | /// Indicates the error is already stored in Zcu `failed_codegen`. | |
| 1247 | CodegenFail, | |
| 1248 | }; | |
| 1205 | 1249 | |
| 1206 | pub fn generate( | |
| 1207 | bin_file: *link.File, | |
| 1250 | pub fn function( | |
| 1251 | wasm: *Wasm, | |
| 1208 | 1252 | pt: Zcu.PerThread, |
| 1209 | src_loc: Zcu.LazySrcLoc, | |
| 1210 | 1253 | func_index: InternPool.Index, |
| 1211 | 1254 | air: Air, |
| 1212 | 1255 | liveness: Liveness, |
| 1213 | code: *std.ArrayList(u8), | |
| 1214 | debug_output: link.File.DebugInfoOutput, | |
| 1215 | ) codegen.CodeGenError!codegen.Result { | |
| 1256 | ) Error!Function { | |
| 1216 | 1257 | const zcu = pt.zcu; |
| 1217 | 1258 | const gpa = zcu.gpa; |
| 1218 | const func = zcu.funcInfo(func_index); | |
| 1219 | const file_scope = zcu.navFileScope(func.owner_nav); | |
| 1259 | const cg = zcu.funcInfo(func_index); | |
| 1260 | const file_scope = zcu.navFileScope(cg.owner_nav); | |
| 1220 | 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 | 1272 | var code_gen: CodeGen = .{ |
| 1222 | 1273 | .gpa = gpa, |
| 1223 | 1274 | .pt = pt, |
| 1224 | 1275 | .air = air, |
| 1225 | 1276 | .liveness = liveness, |
| 1226 | .code = code, | |
| 1227 | .owner_nav = func.owner_nav, | |
| 1228 | .src_loc = src_loc, | |
| 1229 | .err_msg = undefined, | |
| 1230 | .locals = .{}, | |
| 1277 | .owner_nav = cg.owner_nav, | |
| 1231 | 1278 | .target = target, |
| 1232 | .bin_file = bin_file.cast(.wasm).?, | |
| 1233 | .debug_output = debug_output, | |
| 1279 | .ptr_size = switch (target.cpu.arch) { | |
| 1280 | .wasm32 => .wasm32, | |
| 1281 | .wasm64 => .wasm64, | |
| 1282 | else => unreachable, | |
| 1283 | }, | |
| 1284 | .wasm = wasm, | |
| 1234 | 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 | 1295 | defer code_gen.deinit(); |
| 1237 | 1296 | |
| 1238 | genFunc(&code_gen) catch |err| switch (err) { | |
| 1239 | error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg }, | |
| 1240 | else => |e| return e, | |
| 1297 | return functionInner(&code_gen, any_returns) catch |err| switch (err) { | |
| 1298 | error.CodegenFail => return error.CodegenFail, | |
| 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 { | |
| 1247 | const pt = func.pt; | |
| 1248 | const zcu = 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; | |
| 1303 | fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { | |
| 1304 | const wasm = cg.wasm; | |
| 1305 | const zcu = cg.pt.zcu; | |
| 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 | 1310 | // clean up outer branch |
| 1266 | 1311 | defer { |
| 1267 | var outer_branch = func.branches.pop(); | |
| 1268 | outer_branch.deinit(func.gpa); | |
| 1269 | assert(func.branches.items.len == 0); // missing branch merge | |
| 1312 | var outer_branch = cg.branches.pop(); | |
| 1313 | outer_branch.deinit(cg.gpa); | |
| 1314 | assert(cg.branches.items.len == 0); // missing branch merge | |
| 1270 | 1315 | } |
| 1271 | 1316 | // Generate MIR for function body |
| 1272 | try func.genBody(func.air.getMainBody()); | |
| 1317 | try cg.genBody(cg.air.getMainBody()); | |
| 1273 | 1318 | |
| 1274 | 1319 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) |
| 1275 | 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) { | |
| 1277 | const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1); | |
| 1278 | const last_inst_ty = func.typeOfIndex(inst); | |
| 1321 | if (any_returns and cg.air.instructions.len > 0) { | |
| 1322 | const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1); | |
| 1323 | const last_inst_ty = cg.typeOfIndex(inst); | |
| 1279 | 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 | 1328 | // End of function body |
| 1284 | try func.addTag(.end); | |
| 1285 | ||
| 1286 | try func.addTag(.dbg_epilogue_begin); | |
| 1287 | ||
| 1288 | // check if we have to initialize and allocate anything into the stack frame. | |
| 1289 | // If so, create enough stack space and insert the instructions at the front of the list. | |
| 1290 | if (func.initial_stack_value != .none) { | |
| 1291 | var prologue = std.ArrayList(Mir.Inst).init(func.gpa); | |
| 1292 | defer prologue.deinit(); | |
| 1293 | ||
| 1294 | const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym); | |
| 1295 | // load stack pointer | |
| 1296 | try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } }); | |
| 1297 | // store stack pointer so we can restore it when we return from the function | |
| 1298 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_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; | |
| 1329 | try cg.addTag(.end); | |
| 1330 | try cg.addTag(.dbg_epilogue_begin); | |
| 1331 | ||
| 1332 | return .{ | |
| 1333 | .mir_off = start_mir_off, | |
| 1334 | .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off), | |
| 1335 | .mir_extra_off = cg.start_mir_extra_off, | |
| 1336 | .mir_extra_len = cg.extraLen(), | |
| 1337 | .locals_off = cg.start_locals_off, | |
| 1338 | .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off), | |
| 1339 | .prologue = if (cg.initial_stack_value == .none) .none else .{ | |
| 1340 | .sp_local = cg.initial_stack_value.local.value, | |
| 1341 | .flags = .{ .stack_alignment = cg.stack_alignment }, | |
| 1342 | .stack_size = cg.stack_size, | |
| 1343 | .bottom_stack_local = cg.bottom_stack_value.local.value, | |
| 1345 | 1344 | }, |
| 1346 | else => |e| return e, | |
| 1347 | 1345 | }; |
| 1348 | 1346 | } |
| 1349 | 1347 | |
| 1350 | 1348 | const CallWValues = struct { |
| 1351 | 1349 | args: []WValue, |
| 1352 | 1350 | return_value: WValue, |
| 1351 | local_index: u32, | |
| 1353 | 1352 | |
| 1354 | 1353 | fn deinit(values: *CallWValues, gpa: Allocator) void { |
| 1355 | 1354 | gpa.free(values.args); |
| ... | ... | @@ -1357,28 +1356,33 @@ const CallWValues = struct { |
| 1357 | 1356 | } |
| 1358 | 1357 | }; |
| 1359 | 1358 | |
| 1360 | fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues { | |
| 1361 | const pt = func.pt; | |
| 1362 | const zcu = pt.zcu; | |
| 1359 | fn resolveCallingConventionValues( | |
| 1360 | zcu: *const Zcu, | |
| 1361 | fn_ty: Type, | |
| 1362 | target: *const std.Target, | |
| 1363 | ) Allocator.Error!CallWValues { | |
| 1364 | const gpa = zcu.gpa; | |
| 1363 | 1365 | const ip = &zcu.intern_pool; |
| 1364 | 1366 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 1365 | 1367 | const cc = fn_info.cc; |
| 1368 | ||
| 1366 | 1369 | var result: CallWValues = .{ |
| 1367 | 1370 | .args = &.{}, |
| 1368 | 1371 | .return_value = .none, |
| 1372 | .local_index = 0, | |
| 1369 | 1373 | }; |
| 1370 | 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 | 1377 | defer args.deinit(); |
| 1374 | 1378 | |
| 1375 | 1379 | // Check if we store the result as a pointer to the stack rather than |
| 1376 | 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 | 1382 | // the sret arg will be passed as first argument, therefore we |
| 1379 | 1383 | // set the `return_value` before allocating locals for regular args. |
| 1380 | result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } }; | |
| 1381 | func.local_index += 1; | |
| 1384 | result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } }; | |
| 1385 | result.local_index += 1; | |
| 1382 | 1386 | } |
| 1383 | 1387 | |
| 1384 | 1388 | switch (cc) { |
| ... | ... | @@ -1388,8 +1392,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV |
| 1388 | 1392 | continue; |
| 1389 | 1393 | } |
| 1390 | 1394 | |
| 1391 | try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } }); | |
| 1392 | func.local_index += 1; | |
| 1395 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | |
| 1396 | result.local_index += 1; | |
| 1393 | 1397 | } |
| 1394 | 1398 | }, |
| 1395 | 1399 | .wasm_watc => { |
| ... | ... | @@ -1397,23 +1401,28 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV |
| 1397 | 1401 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); |
| 1398 | 1402 | for (ty_classes) |class| { |
| 1399 | 1403 | if (class == .none) continue; |
| 1400 | try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } }); | |
| 1401 | func.local_index += 1; | |
| 1404 | try args.append(.{ .local = .{ .value = result.local_index, .references = 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 | 1411 | result.args = try args.toOwnedSlice(); |
| 1408 | 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 | 1421 | switch (cc) { |
| 1413 | 1422 | .@"inline" => unreachable, |
| 1414 | .auto => return isByRef(return_type, pt, target), | |
| 1423 | .auto => return isByRef(return_type, zcu, target), | |
| 1415 | 1424 | .wasm_watc => { |
| 1416 | const ty_classes = abi.classifyType(return_type, pt.zcu); | |
| 1425 | const ty_classes = abi.classifyType(return_type, zcu); | |
| 1417 | 1426 | if (ty_classes[0] == .indirect) return true; |
| 1418 | 1427 | if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; |
| 1419 | 1428 | return false; |
| ... | ... | @@ -1424,94 +1433,88 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu. |
| 1424 | 1433 | |
| 1425 | 1434 | /// Lowers a Zig type and its value based on a given calling convention to ensure |
| 1426 | 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 | 1437 | if (cc != .wasm_watc) { |
| 1429 | return func.lowerToStack(value); | |
| 1438 | return cg.lowerToStack(value); | |
| 1430 | 1439 | } |
| 1431 | 1440 | |
| 1432 | const pt = func.pt; | |
| 1433 | const zcu = pt.zcu; | |
| 1441 | const zcu = cg.pt.zcu; | |
| 1434 | 1442 | const ty_classes = abi.classifyType(ty, zcu); |
| 1435 | 1443 | assert(ty_classes[0] != .none); |
| 1436 | 1444 | switch (ty.zigTypeTag(zcu)) { |
| 1437 | 1445 | .@"struct", .@"union" => { |
| 1438 | 1446 | if (ty_classes[0] == .indirect) { |
| 1439 | return func.lowerToStack(value); | |
| 1447 | return cg.lowerToStack(value); | |
| 1440 | 1448 | } |
| 1441 | 1449 | assert(ty_classes[0] == .direct); |
| 1442 | 1450 | const scalar_type = abi.scalarType(ty, zcu); |
| 1443 | 1451 | switch (value) { |
| 1444 | .memory, | |
| 1445 | .memory_offset, | |
| 1446 | .stack_offset, | |
| 1447 | => _ = try func.load(value, scalar_type, 0), | |
| 1452 | .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0), | |
| 1448 | 1453 | .dead => unreachable, |
| 1449 | else => try func.emitWValue(value), | |
| 1454 | else => try cg.emitWValue(value), | |
| 1450 | 1455 | } |
| 1451 | 1456 | }, |
| 1452 | 1457 | .int, .float => { |
| 1453 | 1458 | if (ty_classes[1] == .none) { |
| 1454 | return func.lowerToStack(value); | |
| 1459 | return cg.lowerToStack(value); | |
| 1455 | 1460 | } |
| 1456 | 1461 | assert(ty_classes[0] == .direct and ty_classes[1] == .direct); |
| 1457 | 1462 | assert(ty.abiSize(zcu) == 16); |
| 1458 | 1463 | // in this case we have an integer or float that must be lowered as 2 i64's. |
| 1459 | try func.emitWValue(value); | |
| 1460 | try func.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 }); | |
| 1461 | try func.emitWValue(value); | |
| 1462 | try func.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); | |
| 1464 | try cg.emitWValue(value); | |
| 1465 | try cg.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 }); | |
| 1466 | try cg.emitWValue(value); | |
| 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 | 1473 | /// Lowers a `WValue` to the stack. This means when the `value` results in |
| 1469 | 1474 | /// `.stack_offset` we calculate the pointer of this offset and use that. |
| 1470 | 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 | 1477 | switch (value) { |
| 1473 | 1478 | .stack_offset => |offset| { |
| 1474 | try func.emitWValue(value); | |
| 1479 | try cg.emitWValue(value); | |
| 1475 | 1480 | if (offset.value > 0) { |
| 1476 | switch (func.arch()) { | |
| 1481 | switch (cg.ptr_size) { | |
| 1477 | 1482 | .wasm32 => { |
| 1478 | try func.addImm32(offset.value); | |
| 1479 | try func.addTag(.i32_add); | |
| 1483 | try cg.addImm32(offset.value); | |
| 1484 | try cg.addTag(.i32_add); | |
| 1480 | 1485 | }, |
| 1481 | 1486 | .wasm64 => { |
| 1482 | try func.addImm64(offset.value); | |
| 1483 | try func.addTag(.i64_add); | |
| 1487 | try cg.addImm64(offset.value); | |
| 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 | 1497 | /// Creates a local for the initial stack value |
| 1494 | 1498 | /// Asserts `initial_stack_value` is `.none` |
| 1495 | fn initializeStack(func: *CodeGen) !void { | |
| 1496 | assert(func.initial_stack_value == .none); | |
| 1499 | fn initializeStack(cg: *CodeGen) !void { | |
| 1500 | assert(cg.initial_stack_value == .none); | |
| 1497 | 1501 | // Reserve a local to store the current stack pointer |
| 1498 | 1502 | // We can later use this local to set the stack pointer back to the value |
| 1499 | 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 | 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 | 1509 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| 1506 | 1510 | /// to the global stack pointer variable |
| 1507 | fn restoreStackPointer(func: *CodeGen) !void { | |
| 1511 | fn restoreStackPointer(cg: *CodeGen) !void { | |
| 1508 | 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 | 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 | |
| 1514 | try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym)); | |
| 1517 | try cg.addTag(.global_set_sp); | |
| 1515 | 1518 | } |
| 1516 | 1519 | |
| 1517 | 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 | 1523 | /// moveStack unless a local was already created to store the pointer. |
| 1521 | 1524 | /// |
| 1522 | 1525 | /// Asserts Type has codegenbits |
| 1523 | fn allocStack(func: *CodeGen, ty: Type) !WValue { | |
| 1524 | const zcu = func.pt.zcu; | |
| 1526 | fn allocStack(cg: *CodeGen, ty: Type) !WValue { | |
| 1527 | const pt = cg.pt; | |
| 1528 | const zcu = pt.zcu; | |
| 1525 | 1529 | assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); |
| 1526 | if (func.initial_stack_value == .none) { | |
| 1527 | try func.initializeStack(); | |
| 1530 | if (cg.initial_stack_value == .none) { | |
| 1531 | try cg.initializeStack(); | |
| 1528 | 1532 | } |
| 1529 | 1533 | |
| 1530 | 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", .{ | |
| 1532 | ty.fmt(func.pt), ty.abiSize(zcu), | |
| 1535 | return cg.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ | |
| 1536 | ty.fmt(pt), ty.abiSize(zcu), | |
| 1533 | 1537 | }); |
| 1534 | 1538 | }; |
| 1535 | 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)); | |
| 1540 | defer func.stack_size = offset + abi_size; | |
| 1543 | const offset: u32 = @intCast(abi_align.forward(cg.stack_size)); | |
| 1544 | defer cg.stack_size = offset + abi_size; | |
| 1541 | 1545 | |
| 1542 | 1546 | return .{ .stack_offset = .{ .value = offset, .references = 1 } }; |
| 1543 | 1547 | } |
| ... | ... | @@ -1546,30 +1550,30 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue { |
| 1546 | 1550 | /// the value of its type will live. |
| 1547 | 1551 | /// This is different from allocStack where this will use the pointer's alignment |
| 1548 | 1552 | /// if it is set, to ensure the stack alignment will be set correctly. |
| 1549 | fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue { | |
| 1550 | const pt = func.pt; | |
| 1553 | fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue { | |
| 1554 | const pt = cg.pt; | |
| 1551 | 1555 | const zcu = pt.zcu; |
| 1552 | const ptr_ty = func.typeOfIndex(inst); | |
| 1556 | const ptr_ty = cg.typeOfIndex(inst); | |
| 1553 | 1557 | const pointee_ty = ptr_ty.childType(zcu); |
| 1554 | 1558 | |
| 1555 | if (func.initial_stack_value == .none) { | |
| 1556 | try func.initializeStack(); | |
| 1559 | if (cg.initial_stack_value == .none) { | |
| 1560 | try cg.initializeStack(); | |
| 1557 | 1561 | } |
| 1558 | 1562 | |
| 1559 | 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 | 1567 | const abi_alignment = ptr_ty.ptrAlignment(zcu); |
| 1564 | 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 | 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)); | |
| 1572 | defer func.stack_size = offset + abi_size; | |
| 1575 | const offset: u32 = @intCast(abi_alignment.forward(cg.stack_size)); | |
| 1576 | defer cg.stack_size = offset + abi_size; | |
| 1573 | 1577 | |
| 1574 | 1578 | return .{ .stack_offset = .{ .value = offset, .references = 1 } }; |
| 1575 | 1579 | } |
| ... | ... | @@ -1583,14 +1587,14 @@ fn toWasmBits(bits: u16) ?u16 { |
| 1583 | 1587 | |
| 1584 | 1588 | /// Performs a copy of bytes for a given type. Copying all bytes |
| 1585 | 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 | 1591 | // When bulk_memory is enabled, we lower it to wasm's memcpy instruction. |
| 1588 | 1592 | // If not, we lower it ourselves manually |
| 1589 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) { | |
| 1590 | try func.lowerToStack(dst); | |
| 1591 | try func.lowerToStack(src); | |
| 1592 | try func.emitWValue(len); | |
| 1593 | try func.addExtended(.memory_copy); | |
| 1593 | if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) { | |
| 1594 | try cg.lowerToStack(dst); | |
| 1595 | try cg.lowerToStack(src); | |
| 1596 | try cg.emitWValue(len); | |
| 1597 | try cg.addExtended(.memory_copy); | |
| 1594 | 1598 | return; |
| 1595 | 1599 | } |
| 1596 | 1600 | |
| ... | ... | @@ -1611,19 +1615,18 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1611 | 1615 | const rhs_base = src.offset(); |
| 1612 | 1616 | while (offset < length) : (offset += 1) { |
| 1613 | 1617 | // get dst's address to store the result |
| 1614 | try func.emitWValue(dst); | |
| 1618 | try cg.emitWValue(dst); | |
| 1615 | 1619 | // load byte from src's address |
| 1616 | try func.emitWValue(src); | |
| 1617 | switch (func.arch()) { | |
| 1620 | try cg.emitWValue(src); | |
| 1621 | switch (cg.ptr_size) { | |
| 1618 | 1622 | .wasm32 => { |
| 1619 | try func.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); | |
| 1620 | try func.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); | |
| 1623 | try cg.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); | |
| 1624 | try cg.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); | |
| 1621 | 1625 | }, |
| 1622 | 1626 | .wasm64 => { |
| 1623 | try func.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); | |
| 1624 | try func.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); | |
| 1627 | try cg.addMemArg(.i64_load8_u, .{ .offset = rhs_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 | 1632 | return; |
| ... | ... | @@ -1633,94 +1636,84 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1633 | 1636 | |
| 1634 | 1637 | // allocate a local for the offset, and set it to 0. |
| 1635 | 1638 | // This to ensure that inside loops we correctly re-set the counter. |
| 1636 | var offset = try func.allocLocal(Type.usize); // local for counter | |
| 1637 | defer offset.free(func); | |
| 1638 | switch (func.arch()) { | |
| 1639 | .wasm32 => try func.addImm32(0), | |
| 1640 | .wasm64 => try func.addImm64(0), | |
| 1641 | else => unreachable, | |
| 1639 | var offset = try cg.allocLocal(Type.usize); // local for counter | |
| 1640 | defer offset.free(cg); | |
| 1641 | switch (cg.ptr_size) { | |
| 1642 | .wasm32 => try cg.addImm32(0), | |
| 1643 | .wasm64 => try cg.addImm64(0), | |
| 1642 | 1644 | } |
| 1643 | try func.addLabel(.local_set, offset.local.value); | |
| 1645 | try cg.addLocal(.local_set, offset.local.value); | |
| 1644 | 1646 | |
| 1645 | 1647 | // outer block to jump to when loop is done |
| 1646 | try func.startBlock(.block, wasm.block_empty); | |
| 1647 | try func.startBlock(.loop, wasm.block_empty); | |
| 1648 | try cg.startBlock(.block, .empty); | |
| 1649 | try cg.startBlock(.loop, .empty); | |
| 1648 | 1650 | |
| 1649 | 1651 | // loop condition (offset == length -> break) |
| 1650 | 1652 | { |
| 1651 | try func.emitWValue(offset); | |
| 1652 | try func.emitWValue(len); | |
| 1653 | switch (func.arch()) { | |
| 1654 | .wasm32 => try func.addTag(.i32_eq), | |
| 1655 | .wasm64 => try func.addTag(.i64_eq), | |
| 1656 | else => unreachable, | |
| 1653 | try cg.emitWValue(offset); | |
| 1654 | try cg.emitWValue(len); | |
| 1655 | switch (cg.ptr_size) { | |
| 1656 | .wasm32 => try cg.addTag(.i32_eq), | |
| 1657 | .wasm64 => try cg.addTag(.i64_eq), | |
| 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 | 1662 | // get dst ptr |
| 1662 | 1663 | { |
| 1663 | try func.emitWValue(dst); | |
| 1664 | try func.emitWValue(offset); | |
| 1665 | switch (func.arch()) { | |
| 1666 | .wasm32 => try func.addTag(.i32_add), | |
| 1667 | .wasm64 => try func.addTag(.i64_add), | |
| 1668 | else => unreachable, | |
| 1664 | try cg.emitWValue(dst); | |
| 1665 | try cg.emitWValue(offset); | |
| 1666 | switch (cg.ptr_size) { | |
| 1667 | .wasm32 => try cg.addTag(.i32_add), | |
| 1668 | .wasm64 => try cg.addTag(.i64_add), | |
| 1669 | 1669 | } |
| 1670 | 1670 | } |
| 1671 | 1671 | |
| 1672 | 1672 | // get src value and also store in dst |
| 1673 | 1673 | { |
| 1674 | try func.emitWValue(src); | |
| 1675 | try func.emitWValue(offset); | |
| 1676 | switch (func.arch()) { | |
| 1674 | try cg.emitWValue(src); | |
| 1675 | try cg.emitWValue(offset); | |
| 1676 | switch (cg.ptr_size) { | |
| 1677 | 1677 | .wasm32 => { |
| 1678 | try func.addTag(.i32_add); | |
| 1679 | try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 }); | |
| 1680 | try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 }); | |
| 1678 | try cg.addTag(.i32_add); | |
| 1679 | try cg.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 }); | |
| 1680 | try cg.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 }); | |
| 1681 | 1681 | }, |
| 1682 | 1682 | .wasm64 => { |
| 1683 | try func.addTag(.i64_add); | |
| 1684 | try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 }); | |
| 1685 | try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 }); | |
| 1683 | try cg.addTag(.i64_add); | |
| 1684 | try cg.addMemArg(.i64_load8_u, .{ .offset = src.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 | 1690 | // increment loop counter |
| 1692 | 1691 | { |
| 1693 | try func.emitWValue(offset); | |
| 1694 | switch (func.arch()) { | |
| 1692 | try cg.emitWValue(offset); | |
| 1693 | switch (cg.ptr_size) { | |
| 1695 | 1694 | .wasm32 => { |
| 1696 | try func.addImm32(1); | |
| 1697 | try func.addTag(.i32_add); | |
| 1695 | try cg.addImm32(1); | |
| 1696 | try cg.addTag(.i32_add); | |
| 1698 | 1697 | }, |
| 1699 | 1698 | .wasm64 => { |
| 1700 | try func.addImm64(1); | |
| 1701 | try func.addTag(.i64_add); | |
| 1699 | try cg.addImm64(1); | |
| 1700 | try cg.addTag(.i64_add); | |
| 1702 | 1701 | }, |
| 1703 | else => unreachable, | |
| 1704 | 1702 | } |
| 1705 | try func.addLabel(.local_set, offset.local.value); | |
| 1706 | try func.addLabel(.br, 0); // jump to start of loop | |
| 1703 | try cg.addLocal(.local_set, offset.local.value); | |
| 1704 | try cg.addLabel(.br, 0); // jump to start of loop | |
| 1707 | 1705 | } |
| 1708 | try func.endBlock(); // close off loop block | |
| 1709 | try func.endBlock(); // close off outer block | |
| 1706 | try cg.endBlock(); // close off loop block | |
| 1707 | try cg.endBlock(); // close off outer block | |
| 1710 | 1708 | } |
| 1711 | 1709 | |
| 1712 | fn ptrSize(func: *const CodeGen) u16 { | |
| 1713 | return @divExact(func.target.ptrBitWidth(), 8); | |
| 1714 | } | |
| 1715 | ||
| 1716 | fn arch(func: *const CodeGen) std.Target.Cpu.Arch { | |
| 1717 | return func.target.cpu.arch; | |
| 1710 | fn ptrSize(cg: *const CodeGen) u16 { | |
| 1711 | return @divExact(cg.target.ptrBitWidth(), 8); | |
| 1718 | 1712 | } |
| 1719 | 1713 | |
| 1720 | 1714 | /// For a given `Type`, will return true when the type will be passed |
| 1721 | 1715 | /// by reference, rather than by value |
| 1722 | fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { | |
| 1723 | const zcu = pt.zcu; | |
| 1716 | fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { | |
| 1724 | 1717 | const ip = &zcu.intern_pool; |
| 1725 | 1718 | switch (ty.zigTypeTag(zcu)) { |
| 1726 | 1719 | .type, |
| ... | ... | @@ -1753,14 +1746,14 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { |
| 1753 | 1746 | }, |
| 1754 | 1747 | .@"struct" => { |
| 1755 | 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 | 1751 | return ty.hasRuntimeBitsIgnoreComptime(zcu); |
| 1759 | 1752 | }, |
| 1760 | 1753 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, |
| 1761 | 1754 | .int => return ty.intInfo(zcu).bits > 64, |
| 1762 | 1755 | .@"enum" => return ty.intInfo(zcu).bits > 64, |
| 1763 | .float => return ty.floatBits(target) > 64, | |
| 1756 | .float => return ty.floatBits(target.*) > 64, | |
| 1764 | 1757 | .error_union => { |
| 1765 | 1758 | const pl_ty = ty.errorUnionPayload(zcu); |
| 1766 | 1759 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| ... | ... | @@ -1791,8 +1784,8 @@ const SimdStoreStrategy = enum { |
| 1791 | 1784 | /// This means when a given type is 128 bits and either the simd128 or relaxed-simd |
| 1792 | 1785 | /// features are enabled, the function will return `.direct`. This would allow to store |
| 1793 | 1786 | /// it using a instruction, rather than an unrolled version. |
| 1794 | fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy { | |
| 1795 | std.debug.assert(ty.zigTypeTag(zcu) == .vector); | |
| 1787 | pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy { | |
| 1788 | assert(ty.zigTypeTag(zcu) == .vector); | |
| 1796 | 1789 | if (ty.bitSize(zcu) != 128) return .unrolled; |
| 1797 | 1790 | const hasFeature = std.Target.wasm.featureSetHas; |
| 1798 | 1791 | const features = target.cpu.features; |
| ... | ... | @@ -1806,215 +1799,214 @@ fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStore |
| 1806 | 1799 | /// This can be used to get a pointer to a struct field, error payload, etc. |
| 1807 | 1800 | /// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new |
| 1808 | 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 | 1803 | // do not perform arithmetic when offset is 0. |
| 1811 | 1804 | if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value; |
| 1812 | 1805 | const result_ptr: WValue = switch (action) { |
| 1813 | .new => try func.ensureAllocLocal(Type.usize), | |
| 1806 | .new => try cg.ensureAllocLocal(Type.usize), | |
| 1814 | 1807 | .modify => ptr_value, |
| 1815 | 1808 | }; |
| 1816 | try func.emitWValue(ptr_value); | |
| 1809 | try cg.emitWValue(ptr_value); | |
| 1817 | 1810 | if (offset + ptr_value.offset() > 0) { |
| 1818 | switch (func.arch()) { | |
| 1811 | switch (cg.ptr_size) { | |
| 1819 | 1812 | .wasm32 => { |
| 1820 | try func.addImm32(@intCast(offset + ptr_value.offset())); | |
| 1821 | try func.addTag(.i32_add); | |
| 1813 | try cg.addImm32(@intCast(offset + ptr_value.offset())); | |
| 1814 | try cg.addTag(.i32_add); | |
| 1822 | 1815 | }, |
| 1823 | 1816 | .wasm64 => { |
| 1824 | try func.addImm64(offset + ptr_value.offset()); | |
| 1825 | try func.addTag(.i64_add); | |
| 1817 | try cg.addImm64(offset + ptr_value.offset()); | |
| 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 | 1823 | return result_ptr; |
| 1832 | 1824 | } |
| 1833 | 1825 | |
| 1834 | fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 1835 | const air_tags = func.air.instructions.items(.tag); | |
| 1826 | fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 1827 | const air_tags = cg.air.instructions.items(.tag); | |
| 1836 | 1828 | return switch (air_tags[@intFromEnum(inst)]) { |
| 1837 | 1829 | .inferred_alloc, .inferred_alloc_comptime => unreachable, |
| 1838 | 1830 | |
| 1839 | .add => func.airBinOp(inst, .add), | |
| 1840 | .add_sat => func.airSatBinOp(inst, .add), | |
| 1841 | .add_wrap => func.airWrapBinOp(inst, .add), | |
| 1842 | .sub => func.airBinOp(inst, .sub), | |
| 1843 | .sub_sat => func.airSatBinOp(inst, .sub), | |
| 1844 | .sub_wrap => func.airWrapBinOp(inst, .sub), | |
| 1845 | .mul => func.airBinOp(inst, .mul), | |
| 1846 | .mul_sat => func.airSatMul(inst), | |
| 1847 | .mul_wrap => func.airWrapBinOp(inst, .mul), | |
| 1848 | .div_float, .div_exact => func.airDiv(inst), | |
| 1849 | .div_trunc => func.airDivTrunc(inst), | |
| 1850 | .div_floor => func.airDivFloor(inst), | |
| 1851 | .bit_and => func.airBinOp(inst, .@"and"), | |
| 1852 | .bit_or => func.airBinOp(inst, .@"or"), | |
| 1853 | .bool_and => func.airBinOp(inst, .@"and"), | |
| 1854 | .bool_or => func.airBinOp(inst, .@"or"), | |
| 1855 | .rem => func.airRem(inst), | |
| 1856 | .mod => func.airMod(inst), | |
| 1857 | .shl => func.airWrapBinOp(inst, .shl), | |
| 1858 | .shl_exact => func.airBinOp(inst, .shl), | |
| 1859 | .shl_sat => func.airShlSat(inst), | |
| 1860 | .shr, .shr_exact => func.airBinOp(inst, .shr), | |
| 1861 | .xor => func.airBinOp(inst, .xor), | |
| 1862 | .max => func.airMaxMin(inst, .max), | |
| 1863 | .min => func.airMaxMin(inst, .min), | |
| 1864 | .mul_add => func.airMulAdd(inst), | |
| 1865 | ||
| 1866 | .sqrt => func.airUnaryFloatOp(inst, .sqrt), | |
| 1867 | .sin => func.airUnaryFloatOp(inst, .sin), | |
| 1868 | .cos => func.airUnaryFloatOp(inst, .cos), | |
| 1869 | .tan => func.airUnaryFloatOp(inst, .tan), | |
| 1870 | .exp => func.airUnaryFloatOp(inst, .exp), | |
| 1871 | .exp2 => func.airUnaryFloatOp(inst, .exp2), | |
| 1872 | .log => func.airUnaryFloatOp(inst, .log), | |
| 1873 | .log2 => func.airUnaryFloatOp(inst, .log2), | |
| 1874 | .log10 => func.airUnaryFloatOp(inst, .log10), | |
| 1875 | .floor => func.airUnaryFloatOp(inst, .floor), | |
| 1876 | .ceil => func.airUnaryFloatOp(inst, .ceil), | |
| 1877 | .round => func.airUnaryFloatOp(inst, .round), | |
| 1878 | .trunc_float => func.airUnaryFloatOp(inst, .trunc), | |
| 1879 | .neg => func.airUnaryFloatOp(inst, .neg), | |
| 1880 | ||
| 1881 | .abs => func.airAbs(inst), | |
| 1882 | ||
| 1883 | .add_with_overflow => func.airAddSubWithOverflow(inst, .add), | |
| 1884 | .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub), | |
| 1885 | .shl_with_overflow => func.airShlWithOverflow(inst), | |
| 1886 | .mul_with_overflow => func.airMulWithOverflow(inst), | |
| 1887 | ||
| 1888 | .clz => func.airClz(inst), | |
| 1889 | .ctz => func.airCtz(inst), | |
| 1890 | ||
| 1891 | .cmp_eq => func.airCmp(inst, .eq), | |
| 1892 | .cmp_gte => func.airCmp(inst, .gte), | |
| 1893 | .cmp_gt => func.airCmp(inst, .gt), | |
| 1894 | .cmp_lte => func.airCmp(inst, .lte), | |
| 1895 | .cmp_lt => func.airCmp(inst, .lt), | |
| 1896 | .cmp_neq => func.airCmp(inst, .neq), | |
| 1897 | ||
| 1898 | .cmp_vector => func.airCmpVector(inst), | |
| 1899 | .cmp_lt_errors_len => func.airCmpLtErrorsLen(inst), | |
| 1900 | ||
| 1901 | .array_elem_val => func.airArrayElemVal(inst), | |
| 1902 | .array_to_slice => func.airArrayToSlice(inst), | |
| 1903 | .alloc => func.airAlloc(inst), | |
| 1904 | .arg => func.airArg(inst), | |
| 1905 | .bitcast => func.airBitcast(inst), | |
| 1906 | .block => func.airBlock(inst), | |
| 1907 | .trap => func.airTrap(inst), | |
| 1908 | .breakpoint => func.airBreakpoint(inst), | |
| 1909 | .br => func.airBr(inst), | |
| 1910 | .repeat => func.airRepeat(inst), | |
| 1911 | .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}), | |
| 1912 | .int_from_bool => func.airIntFromBool(inst), | |
| 1913 | .cond_br => func.airCondBr(inst), | |
| 1914 | .intcast => func.airIntcast(inst), | |
| 1915 | .fptrunc => func.airFptrunc(inst), | |
| 1916 | .fpext => func.airFpext(inst), | |
| 1917 | .int_from_float => func.airIntFromFloat(inst), | |
| 1918 | .float_from_int => func.airFloatFromInt(inst), | |
| 1919 | .get_union_tag => func.airGetUnionTag(inst), | |
| 1920 | ||
| 1921 | .@"try" => func.airTry(inst), | |
| 1922 | .try_cold => func.airTry(inst), | |
| 1923 | .try_ptr => func.airTryPtr(inst), | |
| 1924 | .try_ptr_cold => func.airTryPtr(inst), | |
| 1925 | ||
| 1926 | .dbg_stmt => func.airDbgStmt(inst), | |
| 1927 | .dbg_empty_stmt => try func.finishAir(inst, .none, &.{}), | |
| 1928 | .dbg_inline_block => func.airDbgInlineBlock(inst), | |
| 1929 | .dbg_var_ptr => func.airDbgVar(inst, .local_var, true), | |
| 1930 | .dbg_var_val => func.airDbgVar(inst, .local_var, false), | |
| 1931 | .dbg_arg_inline => func.airDbgVar(inst, .local_arg, false), | |
| 1932 | ||
| 1933 | .call => func.airCall(inst, .auto), | |
| 1934 | .call_always_tail => func.airCall(inst, .always_tail), | |
| 1935 | .call_never_tail => func.airCall(inst, .never_tail), | |
| 1936 | .call_never_inline => func.airCall(inst, .never_inline), | |
| 1937 | ||
| 1938 | .is_err => func.airIsErr(inst, .i32_ne), | |
| 1939 | .is_non_err => func.airIsErr(inst, .i32_eq), | |
| 1940 | ||
| 1941 | .is_null => func.airIsNull(inst, .i32_eq, .value), | |
| 1942 | .is_non_null => func.airIsNull(inst, .i32_ne, .value), | |
| 1943 | .is_null_ptr => func.airIsNull(inst, .i32_eq, .ptr), | |
| 1944 | .is_non_null_ptr => func.airIsNull(inst, .i32_ne, .ptr), | |
| 1945 | ||
| 1946 | .load => func.airLoad(inst), | |
| 1947 | .loop => func.airLoop(inst), | |
| 1948 | .memset => func.airMemset(inst, false), | |
| 1949 | .memset_safe => func.airMemset(inst, true), | |
| 1950 | .not => func.airNot(inst), | |
| 1951 | .optional_payload => func.airOptionalPayload(inst), | |
| 1952 | .optional_payload_ptr => func.airOptionalPayloadPtr(inst), | |
| 1953 | .optional_payload_ptr_set => func.airOptionalPayloadPtrSet(inst), | |
| 1954 | .ptr_add => func.airPtrBinOp(inst, .add), | |
| 1955 | .ptr_sub => func.airPtrBinOp(inst, .sub), | |
| 1956 | .ptr_elem_ptr => func.airPtrElemPtr(inst), | |
| 1957 | .ptr_elem_val => func.airPtrElemVal(inst), | |
| 1958 | .int_from_ptr => func.airIntFromPtr(inst), | |
| 1959 | .ret => func.airRet(inst), | |
| 1960 | .ret_safe => func.airRet(inst), // TODO | |
| 1961 | .ret_ptr => func.airRetPtr(inst), | |
| 1962 | .ret_load => func.airRetLoad(inst), | |
| 1963 | .splat => func.airSplat(inst), | |
| 1964 | .select => func.airSelect(inst), | |
| 1965 | .shuffle => func.airShuffle(inst), | |
| 1966 | .reduce => func.airReduce(inst), | |
| 1967 | .aggregate_init => func.airAggregateInit(inst), | |
| 1968 | .union_init => func.airUnionInit(inst), | |
| 1969 | .prefetch => func.airPrefetch(inst), | |
| 1970 | .popcount => func.airPopcount(inst), | |
| 1971 | .byte_swap => func.airByteSwap(inst), | |
| 1972 | .bit_reverse => func.airBitReverse(inst), | |
| 1973 | ||
| 1974 | .slice => func.airSlice(inst), | |
| 1975 | .slice_len => func.airSliceLen(inst), | |
| 1976 | .slice_elem_val => func.airSliceElemVal(inst), | |
| 1977 | .slice_elem_ptr => func.airSliceElemPtr(inst), | |
| 1978 | .slice_ptr => func.airSlicePtr(inst), | |
| 1979 | .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()), | |
| 1980 | .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0), | |
| 1981 | .store => func.airStore(inst, false), | |
| 1982 | .store_safe => func.airStore(inst, true), | |
| 1983 | ||
| 1984 | .set_union_tag => func.airSetUnionTag(inst), | |
| 1985 | .struct_field_ptr => func.airStructFieldPtr(inst), | |
| 1986 | .struct_field_ptr_index_0 => func.airStructFieldPtrIndex(inst, 0), | |
| 1987 | .struct_field_ptr_index_1 => func.airStructFieldPtrIndex(inst, 1), | |
| 1988 | .struct_field_ptr_index_2 => func.airStructFieldPtrIndex(inst, 2), | |
| 1989 | .struct_field_ptr_index_3 => func.airStructFieldPtrIndex(inst, 3), | |
| 1990 | .struct_field_val => func.airStructFieldVal(inst), | |
| 1991 | .field_parent_ptr => func.airFieldParentPtr(inst), | |
| 1992 | ||
| 1993 | .switch_br => func.airSwitchBr(inst), | |
| 1994 | .loop_switch_br => return func.fail("TODO implement `loop_switch_br`", .{}), | |
| 1995 | .trunc => func.airTrunc(inst), | |
| 1996 | .unreach => func.airUnreachable(inst), | |
| 1997 | ||
| 1998 | .wrap_optional => func.airWrapOptional(inst), | |
| 1999 | .unwrap_errunion_payload => func.airUnwrapErrUnionPayload(inst, false), | |
| 2000 | .unwrap_errunion_payload_ptr => func.airUnwrapErrUnionPayload(inst, true), | |
| 2001 | .unwrap_errunion_err => func.airUnwrapErrUnionError(inst, false), | |
| 2002 | .unwrap_errunion_err_ptr => func.airUnwrapErrUnionError(inst, true), | |
| 2003 | .wrap_errunion_payload => func.airWrapErrUnionPayload(inst), | |
| 2004 | .wrap_errunion_err => func.airWrapErrUnionErr(inst), | |
| 2005 | .errunion_payload_ptr_set => func.airErrUnionPayloadPtrSet(inst), | |
| 2006 | .error_name => func.airErrorName(inst), | |
| 2007 | ||
| 2008 | .wasm_memory_size => func.airWasmMemorySize(inst), | |
| 2009 | .wasm_memory_grow => func.airWasmMemoryGrow(inst), | |
| 2010 | ||
| 2011 | .memcpy => func.airMemcpy(inst), | |
| 2012 | ||
| 2013 | .ret_addr => func.airRetAddr(inst), | |
| 2014 | .tag_name => func.airTagName(inst), | |
| 2015 | ||
| 2016 | .error_set_has_value => func.airErrorSetHasValue(inst), | |
| 2017 | .frame_addr => func.airFrameAddress(inst), | |
| 1831 | .add => cg.airBinOp(inst, .add), | |
| 1832 | .add_sat => cg.airSatBinOp(inst, .add), | |
| 1833 | .add_wrap => cg.airWrapBinOp(inst, .add), | |
| 1834 | .sub => cg.airBinOp(inst, .sub), | |
| 1835 | .sub_sat => cg.airSatBinOp(inst, .sub), | |
| 1836 | .sub_wrap => cg.airWrapBinOp(inst, .sub), | |
| 1837 | .mul => cg.airBinOp(inst, .mul), | |
| 1838 | .mul_sat => cg.airSatMul(inst), | |
| 1839 | .mul_wrap => cg.airWrapBinOp(inst, .mul), | |
| 1840 | .div_float, .div_exact => cg.airDiv(inst), | |
| 1841 | .div_trunc => cg.airDivTrunc(inst), | |
| 1842 | .div_floor => cg.airDivFloor(inst), | |
| 1843 | .bit_and => cg.airBinOp(inst, .@"and"), | |
| 1844 | .bit_or => cg.airBinOp(inst, .@"or"), | |
| 1845 | .bool_and => cg.airBinOp(inst, .@"and"), | |
| 1846 | .bool_or => cg.airBinOp(inst, .@"or"), | |
| 1847 | .rem => cg.airRem(inst), | |
| 1848 | .mod => cg.airMod(inst), | |
| 1849 | .shl => cg.airWrapBinOp(inst, .shl), | |
| 1850 | .shl_exact => cg.airBinOp(inst, .shl), | |
| 1851 | .shl_sat => cg.airShlSat(inst), | |
| 1852 | .shr, .shr_exact => cg.airBinOp(inst, .shr), | |
| 1853 | .xor => cg.airBinOp(inst, .xor), | |
| 1854 | .max => cg.airMaxMin(inst, .fmax, .gt), | |
| 1855 | .min => cg.airMaxMin(inst, .fmin, .lt), | |
| 1856 | .mul_add => cg.airMulAdd(inst), | |
| 1857 | ||
| 1858 | .sqrt => cg.airUnaryFloatOp(inst, .sqrt), | |
| 1859 | .sin => cg.airUnaryFloatOp(inst, .sin), | |
| 1860 | .cos => cg.airUnaryFloatOp(inst, .cos), | |
| 1861 | .tan => cg.airUnaryFloatOp(inst, .tan), | |
| 1862 | .exp => cg.airUnaryFloatOp(inst, .exp), | |
| 1863 | .exp2 => cg.airUnaryFloatOp(inst, .exp2), | |
| 1864 | .log => cg.airUnaryFloatOp(inst, .log), | |
| 1865 | .log2 => cg.airUnaryFloatOp(inst, .log2), | |
| 1866 | .log10 => cg.airUnaryFloatOp(inst, .log10), | |
| 1867 | .floor => cg.airUnaryFloatOp(inst, .floor), | |
| 1868 | .ceil => cg.airUnaryFloatOp(inst, .ceil), | |
| 1869 | .round => cg.airUnaryFloatOp(inst, .round), | |
| 1870 | .trunc_float => cg.airUnaryFloatOp(inst, .trunc), | |
| 1871 | .neg => cg.airUnaryFloatOp(inst, .neg), | |
| 1872 | ||
| 1873 | .abs => cg.airAbs(inst), | |
| 1874 | ||
| 1875 | .add_with_overflow => cg.airAddSubWithOverflow(inst, .add), | |
| 1876 | .sub_with_overflow => cg.airAddSubWithOverflow(inst, .sub), | |
| 1877 | .shl_with_overflow => cg.airShlWithOverflow(inst), | |
| 1878 | .mul_with_overflow => cg.airMulWithOverflow(inst), | |
| 1879 | ||
| 1880 | .clz => cg.airClz(inst), | |
| 1881 | .ctz => cg.airCtz(inst), | |
| 1882 | ||
| 1883 | .cmp_eq => cg.airCmp(inst, .eq), | |
| 1884 | .cmp_gte => cg.airCmp(inst, .gte), | |
| 1885 | .cmp_gt => cg.airCmp(inst, .gt), | |
| 1886 | .cmp_lte => cg.airCmp(inst, .lte), | |
| 1887 | .cmp_lt => cg.airCmp(inst, .lt), | |
| 1888 | .cmp_neq => cg.airCmp(inst, .neq), | |
| 1889 | ||
| 1890 | .cmp_vector => cg.airCmpVector(inst), | |
| 1891 | .cmp_lt_errors_len => cg.airCmpLtErrorsLen(inst), | |
| 1892 | ||
| 1893 | .array_elem_val => cg.airArrayElemVal(inst), | |
| 1894 | .array_to_slice => cg.airArrayToSlice(inst), | |
| 1895 | .alloc => cg.airAlloc(inst), | |
| 1896 | .arg => cg.airArg(inst), | |
| 1897 | .bitcast => cg.airBitcast(inst), | |
| 1898 | .block => cg.airBlock(inst), | |
| 1899 | .trap => cg.airTrap(inst), | |
| 1900 | .breakpoint => cg.airBreakpoint(inst), | |
| 1901 | .br => cg.airBr(inst), | |
| 1902 | .repeat => cg.airRepeat(inst), | |
| 1903 | .switch_dispatch => return cg.fail("TODO implement `switch_dispatch`", .{}), | |
| 1904 | .int_from_bool => cg.airIntFromBool(inst), | |
| 1905 | .cond_br => cg.airCondBr(inst), | |
| 1906 | .intcast => cg.airIntcast(inst), | |
| 1907 | .fptrunc => cg.airFptrunc(inst), | |
| 1908 | .fpext => cg.airFpext(inst), | |
| 1909 | .int_from_float => cg.airIntFromFloat(inst), | |
| 1910 | .float_from_int => cg.airFloatFromInt(inst), | |
| 1911 | .get_union_tag => cg.airGetUnionTag(inst), | |
| 1912 | ||
| 1913 | .@"try" => cg.airTry(inst), | |
| 1914 | .try_cold => cg.airTry(inst), | |
| 1915 | .try_ptr => cg.airTryPtr(inst), | |
| 1916 | .try_ptr_cold => cg.airTryPtr(inst), | |
| 1917 | ||
| 1918 | .dbg_stmt => cg.airDbgStmt(inst), | |
| 1919 | .dbg_empty_stmt => try cg.finishAir(inst, .none, &.{}), | |
| 1920 | .dbg_inline_block => cg.airDbgInlineBlock(inst), | |
| 1921 | .dbg_var_ptr => cg.airDbgVar(inst, .local_var, true), | |
| 1922 | .dbg_var_val => cg.airDbgVar(inst, .local_var, false), | |
| 1923 | .dbg_arg_inline => cg.airDbgVar(inst, .local_arg, false), | |
| 1924 | ||
| 1925 | .call => cg.airCall(inst, .auto), | |
| 1926 | .call_always_tail => cg.airCall(inst, .always_tail), | |
| 1927 | .call_never_tail => cg.airCall(inst, .never_tail), | |
| 1928 | .call_never_inline => cg.airCall(inst, .never_inline), | |
| 1929 | ||
| 1930 | .is_err => cg.airIsErr(inst, .i32_ne), | |
| 1931 | .is_non_err => cg.airIsErr(inst, .i32_eq), | |
| 1932 | ||
| 1933 | .is_null => cg.airIsNull(inst, .i32_eq, .value), | |
| 1934 | .is_non_null => cg.airIsNull(inst, .i32_ne, .value), | |
| 1935 | .is_null_ptr => cg.airIsNull(inst, .i32_eq, .ptr), | |
| 1936 | .is_non_null_ptr => cg.airIsNull(inst, .i32_ne, .ptr), | |
| 1937 | ||
| 1938 | .load => cg.airLoad(inst), | |
| 1939 | .loop => cg.airLoop(inst), | |
| 1940 | .memset => cg.airMemset(inst, false), | |
| 1941 | .memset_safe => cg.airMemset(inst, true), | |
| 1942 | .not => cg.airNot(inst), | |
| 1943 | .optional_payload => cg.airOptionalPayload(inst), | |
| 1944 | .optional_payload_ptr => cg.airOptionalPayloadPtr(inst), | |
| 1945 | .optional_payload_ptr_set => cg.airOptionalPayloadPtrSet(inst), | |
| 1946 | .ptr_add => cg.airPtrBinOp(inst, .add), | |
| 1947 | .ptr_sub => cg.airPtrBinOp(inst, .sub), | |
| 1948 | .ptr_elem_ptr => cg.airPtrElemPtr(inst), | |
| 1949 | .ptr_elem_val => cg.airPtrElemVal(inst), | |
| 1950 | .int_from_ptr => cg.airIntFromPtr(inst), | |
| 1951 | .ret => cg.airRet(inst), | |
| 1952 | .ret_safe => cg.airRet(inst), // TODO | |
| 1953 | .ret_ptr => cg.airRetPtr(inst), | |
| 1954 | .ret_load => cg.airRetLoad(inst), | |
| 1955 | .splat => cg.airSplat(inst), | |
| 1956 | .select => cg.airSelect(inst), | |
| 1957 | .shuffle => cg.airShuffle(inst), | |
| 1958 | .reduce => cg.airReduce(inst), | |
| 1959 | .aggregate_init => cg.airAggregateInit(inst), | |
| 1960 | .union_init => cg.airUnionInit(inst), | |
| 1961 | .prefetch => cg.airPrefetch(inst), | |
| 1962 | .popcount => cg.airPopcount(inst), | |
| 1963 | .byte_swap => cg.airByteSwap(inst), | |
| 1964 | .bit_reverse => cg.airBitReverse(inst), | |
| 1965 | ||
| 1966 | .slice => cg.airSlice(inst), | |
| 1967 | .slice_len => cg.airSliceLen(inst), | |
| 1968 | .slice_elem_val => cg.airSliceElemVal(inst), | |
| 1969 | .slice_elem_ptr => cg.airSliceElemPtr(inst), | |
| 1970 | .slice_ptr => cg.airSlicePtr(inst), | |
| 1971 | .ptr_slice_len_ptr => cg.airPtrSliceFieldPtr(inst, cg.ptrSize()), | |
| 1972 | .ptr_slice_ptr_ptr => cg.airPtrSliceFieldPtr(inst, 0), | |
| 1973 | .store => cg.airStore(inst, false), | |
| 1974 | .store_safe => cg.airStore(inst, true), | |
| 1975 | ||
| 1976 | .set_union_tag => cg.airSetUnionTag(inst), | |
| 1977 | .struct_field_ptr => cg.airStructFieldPtr(inst), | |
| 1978 | .struct_field_ptr_index_0 => cg.airStructFieldPtrIndex(inst, 0), | |
| 1979 | .struct_field_ptr_index_1 => cg.airStructFieldPtrIndex(inst, 1), | |
| 1980 | .struct_field_ptr_index_2 => cg.airStructFieldPtrIndex(inst, 2), | |
| 1981 | .struct_field_ptr_index_3 => cg.airStructFieldPtrIndex(inst, 3), | |
| 1982 | .struct_field_val => cg.airStructFieldVal(inst), | |
| 1983 | .field_parent_ptr => cg.airFieldParentPtr(inst), | |
| 1984 | ||
| 1985 | .switch_br => cg.airSwitchBr(inst), | |
| 1986 | .loop_switch_br => return cg.fail("TODO implement `loop_switch_br`", .{}), | |
| 1987 | .trunc => cg.airTrunc(inst), | |
| 1988 | .unreach => cg.airUnreachable(inst), | |
| 1989 | ||
| 1990 | .wrap_optional => cg.airWrapOptional(inst), | |
| 1991 | .unwrap_errunion_payload => cg.airUnwrapErrUnionPayload(inst, false), | |
| 1992 | .unwrap_errunion_payload_ptr => cg.airUnwrapErrUnionPayload(inst, true), | |
| 1993 | .unwrap_errunion_err => cg.airUnwrapErrUnionError(inst, false), | |
| 1994 | .unwrap_errunion_err_ptr => cg.airUnwrapErrUnionError(inst, true), | |
| 1995 | .wrap_errunion_payload => cg.airWrapErrUnionPayload(inst), | |
| 1996 | .wrap_errunion_err => cg.airWrapErrUnionErr(inst), | |
| 1997 | .errunion_payload_ptr_set => cg.airErrUnionPayloadPtrSet(inst), | |
| 1998 | .error_name => cg.airErrorName(inst), | |
| 1999 | ||
| 2000 | .wasm_memory_size => cg.airWasmMemorySize(inst), | |
| 2001 | .wasm_memory_grow => cg.airWasmMemoryGrow(inst), | |
| 2002 | ||
| 2003 | .memcpy => cg.airMemcpy(inst), | |
| 2004 | ||
| 2005 | .ret_addr => cg.airRetAddr(inst), | |
| 2006 | .tag_name => cg.airTagName(inst), | |
| 2007 | ||
| 2008 | .error_set_has_value => cg.airErrorSetHasValue(inst), | |
| 2009 | .frame_addr => cg.airFrameAddress(inst), | |
| 2018 | 2010 | |
| 2019 | 2011 | .assembly, |
| 2020 | 2012 | .is_err_ptr, |
| ... | ... | @@ -2030,18 +2022,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2030 | 2022 | .c_va_copy, |
| 2031 | 2023 | .c_va_end, |
| 2032 | 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 | 2028 | .atomic_store_unordered, |
| 2037 | 2029 | .atomic_store_monotonic, |
| 2038 | 2030 | .atomic_store_release, |
| 2039 | 2031 | .atomic_store_seq_cst, |
| 2040 | 2032 | // in WebAssembly, all atomic instructions are sequentially ordered. |
| 2041 | => func.airAtomicStore(inst), | |
| 2042 | .atomic_rmw => func.airAtomicRmw(inst), | |
| 2043 | .cmpxchg_weak => func.airCmpxchg(inst), | |
| 2044 | .cmpxchg_strong => func.airCmpxchg(inst), | |
| 2033 | => cg.airAtomicStore(inst), | |
| 2034 | .atomic_rmw => cg.airAtomicRmw(inst), | |
| 2035 | .cmpxchg_weak => cg.airCmpxchg(inst), | |
| 2036 | .cmpxchg_strong => cg.airCmpxchg(inst), | |
| 2045 | 2037 | |
| 2046 | 2038 | .add_optimized, |
| 2047 | 2039 | .sub_optimized, |
| ... | ... | @@ -2062,12 +2054,12 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2062 | 2054 | .cmp_vector_optimized, |
| 2063 | 2055 | .reduce_optimized, |
| 2064 | 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 | 2059 | .add_safe, |
| 2068 | 2060 | .sub_safe, |
| 2069 | 2061 | .mul_safe, |
| 2070 | => return func.fail("TODO implement safety_checked_instructions", .{}), | |
| 2062 | => return cg.fail("TODO implement safety_checked_instructions", .{}), | |
| 2071 | 2063 | |
| 2072 | 2064 | .work_item_id, |
| 2073 | 2065 | .work_group_size, |
| ... | ... | @@ -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 { | |
| 2080 | const pt = func.pt; | |
| 2081 | const zcu = pt.zcu; | |
| 2071 | fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { | |
| 2072 | const zcu = cg.pt.zcu; | |
| 2082 | 2073 | const ip = &zcu.intern_pool; |
| 2083 | 2074 | |
| 2084 | 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 | 2077 | continue; |
| 2087 | 2078 | } |
| 2088 | const old_bookkeeping_value = func.air_bookkeeping; | |
| 2089 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi); | |
| 2090 | try func.genInst(inst); | |
| 2079 | const old_bookkeeping_value = cg.air_bookkeeping; | |
| 2080 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, Liveness.bpi); | |
| 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 | 2084 | std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{ |
| 2094 | 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 { | |
| 2102 | const pt = func.pt; | |
| 2103 | const zcu = pt.zcu; | |
| 2104 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 2105 | const operand = try func.resolveInst(un_op); | |
| 2106 | const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?; | |
| 2092 | fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2093 | const zcu = cg.pt.zcu; | |
| 2094 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 2095 | const operand = try cg.resolveInst(un_op); | |
| 2096 | const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; | |
| 2107 | 2097 | const ret_ty = Type.fromInterned(fn_info.return_type); |
| 2108 | 2098 | |
| 2109 | 2099 | // result must be stored in the stack and we return a pointer |
| 2110 | 2100 | // to the stack instead |
| 2111 | if (func.return_value != .none) { | |
| 2112 | try func.store(func.return_value, operand, ret_ty, 0); | |
| 2101 | if (cg.return_value != .none) { | |
| 2102 | try cg.store(cg.return_value, operand, ret_ty, 0); | |
| 2113 | 2103 | } else if (fn_info.cc == .wasm_watc and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2114 | 2104 | switch (ret_ty.zigTypeTag(zcu)) { |
| 2115 | 2105 | // Aggregate types can be lowered as a singular value |
| 2116 | 2106 | .@"struct", .@"union" => { |
| 2117 | 2107 | const scalar_type = abi.scalarType(ret_ty, zcu); |
| 2118 | try func.emitWValue(operand); | |
| 2108 | try cg.emitWValue(operand); | |
| 2119 | 2109 | const opcode = buildOpcode(.{ |
| 2120 | 2110 | .op = .load, |
| 2121 | 2111 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), |
| 2122 | 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 | 2116 | .offset = operand.offset(), |
| 2127 | 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 | 2122 | } else { |
| 2133 | 2123 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) { |
| 2134 | try func.addImm32(0); | |
| 2124 | try cg.addImm32(0); | |
| 2135 | 2125 | } else { |
| 2136 | try func.emitWValue(operand); | |
| 2126 | try cg.emitWValue(operand); | |
| 2137 | 2127 | } |
| 2138 | 2128 | } |
| 2139 | try func.restoreStackPointer(); | |
| 2140 | try func.addTag(.@"return"); | |
| 2129 | try cg.restoreStackPointer(); | |
| 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 { | |
| 2146 | const pt = func.pt; | |
| 2147 | const zcu = pt.zcu; | |
| 2148 | const child_type = func.typeOfIndex(inst).childType(zcu); | |
| 2135 | fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2136 | const zcu = cg.pt.zcu; | |
| 2137 | const child_type = cg.typeOfIndex(inst).childType(zcu); | |
| 2149 | 2138 | |
| 2150 | 2139 | const result = result: { |
| 2151 | 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)).?; | |
| 2156 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | |
| 2157 | break :result func.return_value; | |
| 2144 | const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; | |
| 2145 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) { | |
| 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 { | |
| 2167 | const pt = func.pt; | |
| 2168 | const zcu = pt.zcu; | |
| 2169 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 2170 | const operand = try func.resolveInst(un_op); | |
| 2171 | const ret_ty = func.typeOf(un_op).childType(zcu); | |
| 2155 | fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2156 | const zcu = cg.pt.zcu; | |
| 2157 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 2158 | const operand = try cg.resolveInst(un_op); | |
| 2159 | const ret_ty = cg.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 | 2162 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2175 | 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 | 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(); | |
| 2184 | try func.addTag(.@"return"); | |
| 2185 | return func.finishAir(inst, .none, &.{un_op}); | |
| 2171 | try cg.restoreStackPointer(); | |
| 2172 | try cg.addTag(.@"return"); | |
| 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 { | |
| 2189 | if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{}); | |
| 2190 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 2191 | const extra = func.air.extraData(Air.Call, pl_op.payload); | |
| 2192 | const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len])); | |
| 2193 | const ty = func.typeOf(pl_op.operand); | |
| 2176 | fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { | |
| 2177 | const wasm = cg.wasm; | |
| 2178 | if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); | |
| 2179 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 2180 | const extra = cg.air.extraData(Air.Call, pl_op.payload); | |
| 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 | 2185 | const zcu = pt.zcu; |
| 2197 | 2186 | const ip = &zcu.intern_pool; |
| 2198 | 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 | 2191 | }; |
| 2203 | 2192 | const ret_ty = fn_ty.fnReturnType(zcu); |
| 2204 | 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 | 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 | 2199 | switch (ip.indexToKey(func_val.toIntern())) { |
| 2211 | .func => |function| { | |
| 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 | }, | |
| 2200 | inline .func, .@"extern" => |x| break :blk x.owner_nav, | |
| 2238 | 2201 | .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { |
| 2239 | .nav => |nav| { | |
| 2240 | _ = try func.bin_file.getOrCreateAtomForNav(pt, nav); | |
| 2241 | break :blk nav; | |
| 2242 | }, | |
| 2202 | .nav => |nav| break :blk nav, | |
| 2243 | 2203 | else => {}, |
| 2244 | 2204 | }, |
| 2245 | 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 | 2210 | const sret: WValue = if (first_param_sret) blk: { |
| 2251 | const sret_local = try func.allocStack(ret_ty); | |
| 2252 | try func.lowerToStack(sret_local); | |
| 2211 | const sret_local = try cg.allocStack(ret_ty); | |
| 2212 | try cg.lowerToStack(sret_local); | |
| 2253 | 2213 | break :blk sret_local; |
| 2254 | 2214 | } else .none; |
| 2255 | 2215 | |
| 2256 | 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 | 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| { | |
| 2266 | const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom; | |
| 2267 | try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index)); | |
| 2225 | if (callee) |nav_index| { | |
| 2226 | try cg.addInst(.{ .tag = .call_nav, .data = .{ .nav_index = nav_index } }); | |
| 2268 | 2227 | } else { |
| 2269 | 2228 | // in this case we call a function pointer |
| 2270 | 2229 | // so load its value onto the stack |
| 2271 | std.debug.assert(ty.zigTypeTag(zcu) == .pointer); | |
| 2272 | const operand = try func.resolveInst(pl_op.operand); | |
| 2273 | try func.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); | |
| 2230 | assert(ty.zigTypeTag(zcu) == .pointer); | |
| 2231 | const operand = try cg.resolveInst(pl_op.operand); | |
| 2232 | try cg.emitWValue(operand); | |
| 2277 | 2233 | |
| 2278 | const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type); | |
| 2279 | try func.addLabel(.call_indirect, fn_type_index); | |
| 2234 | const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target); | |
| 2235 | try cg.addFuncTy(.call_indirect, fn_type_index); | |
| 2280 | 2236 | } |
| 2281 | 2237 | |
| 2282 | 2238 | const result_value = result_value: { |
| 2283 | 2239 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { |
| 2284 | 2240 | break :result_value .none; |
| 2285 | 2241 | } else if (ret_ty.isNoReturn(zcu)) { |
| 2286 | try func.addTag(.@"unreachable"); | |
| 2242 | try cg.addTag(.@"unreachable"); | |
| 2287 | 2243 | break :result_value .none; |
| 2288 | 2244 | } else if (first_param_sret) { |
| 2289 | 2245 | break :result_value sret; |
| 2290 | 2246 | // TODO: Make this less fragile and optimize |
| 2291 | 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); | |
| 2293 | try func.addLabel(.local_set, result_local.local.value); | |
| 2248 | const result_local = try cg.allocLocal(ret_ty); | |
| 2249 | try cg.addLocal(.local_set, result_local.local.value); | |
| 2294 | 2250 | const scalar_type = abi.scalarType(ret_ty, zcu); |
| 2295 | const result = try func.allocStack(scalar_type); | |
| 2296 | try func.store(result, result_local, scalar_type, 0); | |
| 2251 | const result = try cg.allocStack(scalar_type); | |
| 2252 | try cg.store(result, result_local, scalar_type, 0); | |
| 2297 | 2253 | break :result_value result; |
| 2298 | 2254 | } else { |
| 2299 | const result_local = try func.allocLocal(ret_ty); | |
| 2300 | try func.addLabel(.local_set, result_local.local.value); | |
| 2255 | const result_local = try cg.allocLocal(ret_ty); | |
| 2256 | try cg.addLocal(.local_set, result_local.local.value); | |
| 2301 | 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 | 2262 | bt.feed(pl_op.operand); |
| 2307 | 2263 | for (args) |arg| bt.feed(arg); |
| 2308 | 2264 | return bt.finishAir(result_value); |
| 2309 | 2265 | } |
| 2310 | 2266 | |
| 2311 | fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2312 | const value = try func.allocStackPtr(inst); | |
| 2313 | return func.finishAir(inst, value, &.{}); | |
| 2267 | fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2268 | const value = try cg.allocStackPtr(inst); | |
| 2269 | return cg.finishAir(inst, value, &.{}); | |
| 2314 | 2270 | } |
| 2315 | 2271 | |
| 2316 | fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { | |
| 2317 | const pt = func.pt; | |
| 2272 | fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { | |
| 2273 | const pt = cg.pt; | |
| 2318 | 2274 | const zcu = pt.zcu; |
| 2319 | 2275 | if (safety) { |
| 2320 | 2276 | // TODO if the value is undef, write 0xaa bytes to dest |
| 2321 | 2277 | } else { |
| 2322 | 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); | |
| 2327 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 2328 | const ptr_ty = func.typeOf(bin_op.lhs); | |
| 2282 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 2283 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 2284 | const ptr_ty = cg.typeOf(bin_op.lhs); | |
| 2329 | 2285 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 2330 | 2286 | const ty = ptr_ty.childType(zcu); |
| 2331 | 2287 | |
| 2332 | 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 | 2290 | } else { |
| 2335 | 2291 | // at this point we have a non-natural alignment, we must |
| 2336 | 2292 | // load the value, and then shift+or the rhs into the result location. |
| 2337 | 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.*)) { | |
| 2340 | return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{}); | |
| 2295 | if (isByRef(int_elem_ty, zcu, cg.target)) { | |
| 2296 | return cg.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{}); | |
| 2341 | 2297 | } |
| 2342 | 2298 | |
| 2343 | 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 | 2312 | else |
| 2357 | 2313 | .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) }; |
| 2358 | 2314 | |
| 2359 | try func.emitWValue(lhs); | |
| 2360 | const loaded = try func.load(lhs, int_elem_ty, 0); | |
| 2361 | const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and"); | |
| 2362 | const extended_value = try func.intcast(rhs, ty, int_elem_ty); | |
| 2363 | const masked_value = try func.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and"); | |
| 2315 | try cg.emitWValue(lhs); | |
| 2316 | const loaded = try cg.load(lhs, int_elem_ty, 0); | |
| 2317 | const anded = try cg.binOp(loaded, mask_val, int_elem_ty, .@"and"); | |
| 2318 | const extended_value = try cg.intcast(rhs, ty, int_elem_ty); | |
| 2319 | const masked_value = try cg.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and"); | |
| 2364 | 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 | 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 | 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 | 2332 | assert(!(lhs != .stack and rhs == .stack)); |
| 2377 | const pt = func.pt; | |
| 2333 | const pt = cg.pt; | |
| 2378 | 2334 | const zcu = pt.zcu; |
| 2379 | 2335 | const abi_size = ty.abiSize(zcu); |
| 2380 | 2336 | switch (ty.zigTypeTag(zcu)) { |
| 2381 | 2337 | .error_union => { |
| 2382 | 2338 | const pl_ty = ty.errorUnionPayload(zcu); |
| 2383 | 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 | 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 | 2346 | .optional => { |
| 2391 | 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 | 2350 | const pl_ty = ty.optionalChild(zcu); |
| 2395 | 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 | 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 | 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 | 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 | 2366 | .unrolled => { |
| 2411 | 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 | 2370 | .direct => { |
| 2415 | try func.emitWValue(lhs); | |
| 2416 | try func.lowerToStack(rhs); | |
| 2371 | try cg.emitWValue(lhs); | |
| 2372 | try cg.lowerToStack(rhs); | |
| 2417 | 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 | 2375 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2420 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ | |
| 2421 | std.wasm.simdOpcode(.v128_store), | |
| 2376 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ | |
| 2377 | @intFromEnum(std.wasm.SimdOpcode.v128_store), | |
| 2422 | 2378 | offset + lhs.offset(), |
| 2423 | 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 | 2384 | .pointer => { |
| 2429 | 2385 | if (ty.isSlice(zcu)) { |
| 2430 | 2386 | // store pointer first |
| 2431 | 2387 | // lower it to the stack so we do not have to store rhs into a local first |
| 2432 | try func.emitWValue(lhs); | |
| 2433 | const ptr_local = try func.load(rhs, Type.usize, 0); | |
| 2434 | try func.store(.stack, ptr_local, Type.usize, 0 + lhs.offset()); | |
| 2388 | try cg.emitWValue(lhs); | |
| 2389 | const ptr_local = try cg.load(rhs, Type.usize, 0); | |
| 2390 | try cg.store(.stack, ptr_local, Type.usize, 0 + lhs.offset()); | |
| 2435 | 2391 | |
| 2436 | 2392 | // retrieve length from rhs, and store that alongside lhs as well |
| 2437 | try func.emitWValue(lhs); | |
| 2438 | const len_local = try func.load(rhs, Type.usize, func.ptrSize()); | |
| 2439 | try func.store(.stack, len_local, Type.usize, func.ptrSize() + lhs.offset()); | |
| 2393 | try cg.emitWValue(lhs); | |
| 2394 | const len_local = try cg.load(rhs, Type.usize, cg.ptrSize()); | |
| 2395 | try cg.store(.stack, len_local, Type.usize, cg.ptrSize() + lhs.offset()); | |
| 2440 | 2396 | return; |
| 2441 | 2397 | } |
| 2442 | 2398 | }, |
| 2443 | 2399 | .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) { |
| 2444 | try func.emitWValue(lhs); | |
| 2445 | const lsb = try func.load(rhs, Type.u64, 0); | |
| 2446 | try func.store(.stack, lsb, Type.u64, 0 + lhs.offset()); | |
| 2400 | try cg.emitWValue(lhs); | |
| 2401 | const lsb = try cg.load(rhs, Type.u64, 0); | |
| 2402 | try cg.store(.stack, lsb, Type.u64, 0 + lhs.offset()); | |
| 2447 | 2403 | |
| 2448 | try func.emitWValue(lhs); | |
| 2449 | const msb = try func.load(rhs, Type.u64, 8); | |
| 2450 | try func.store(.stack, msb, Type.u64, 8 + lhs.offset()); | |
| 2404 | try cg.emitWValue(lhs); | |
| 2405 | const msb = try cg.load(rhs, Type.u64, 8); | |
| 2406 | try cg.store(.stack, msb, Type.u64, 8 + lhs.offset()); | |
| 2451 | 2407 | return; |
| 2452 | 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 | 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 | 2413 | ty.fmt(pt), |
| 2458 | 2414 | abi_size, |
| 2459 | 2415 | }); |
| 2460 | 2416 | }, |
| 2461 | 2417 | } |
| 2462 | try func.emitWValue(lhs); | |
| 2418 | try cg.emitWValue(lhs); | |
| 2463 | 2419 | // In this case we're actually interested in storing the stack position |
| 2464 | 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 | 2424 | const opcode = buildOpcode(.{ |
| 2469 | 2425 | .valtype1 = valtype, |
| 2470 | 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 | 2428 | }); |
| 2473 | 2429 | |
| 2474 | 2430 | // store rhs value at stack pointer's location in memory |
| 2475 | try func.addMemArg( | |
| 2431 | try cg.addMemArg( | |
| 2476 | 2432 | Mir.Inst.Tag.fromOpcode(opcode), |
| 2477 | 2433 | .{ |
| 2478 | 2434 | .offset = offset + lhs.offset(), |
| ... | ... | @@ -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 { | |
| 2485 | const pt = func.pt; | |
| 2440 | fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2441 | const pt = cg.pt; | |
| 2486 | 2442 | const zcu = pt.zcu; |
| 2487 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 2488 | const operand = try func.resolveInst(ty_op.operand); | |
| 2443 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 2444 | const operand = try cg.resolveInst(ty_op.operand); | |
| 2489 | 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 | 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 | 2451 | const result = result: { |
| 2496 | if (isByRef(ty, pt, func.target.*)) { | |
| 2497 | const new_local = try func.allocStack(ty); | |
| 2498 | try func.store(new_local, operand, ty, 0); | |
| 2452 | if (isByRef(ty, zcu, cg.target)) { | |
| 2453 | const new_local = try cg.allocStack(ty); | |
| 2454 | try cg.store(new_local, operand, ty, 0); | |
| 2499 | 2455 | break :result new_local; |
| 2500 | 2456 | } |
| 2501 | 2457 | |
| 2502 | 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 | 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 | 2467 | else if (ptr_info.packed_offset.host_size <= 8) |
| 2512 | 2468 | .{ .imm64 = ptr_info.packed_offset.bit_offset } |
| 2513 | 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); | |
| 2517 | const shifted = try func.binOp(stack_loaded, shift_val, int_elem_ty, .shr); | |
| 2518 | break :result try func.trunc(shifted, ty, int_elem_ty); | |
| 2472 | const stack_loaded = try cg.load(operand, int_elem_ty, 0); | |
| 2473 | const shifted = try cg.binOp(stack_loaded, shift_val, int_elem_ty, .shr); | |
| 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 | 2479 | /// Loads an operand from the linear memory section. |
| 2524 | 2480 | /// NOTE: Leaves the value on the stack. |
| 2525 | fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | |
| 2526 | const pt = func.pt; | |
| 2527 | const zcu = pt.zcu; | |
| 2481 | fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | |
| 2482 | const zcu = cg.pt.zcu; | |
| 2528 | 2483 | // load local's value from memory by its stack position |
| 2529 | try func.emitWValue(operand); | |
| 2484 | try cg.emitWValue(operand); | |
| 2530 | 2485 | |
| 2531 | 2486 | if (ty.zigTypeTag(zcu) == .vector) { |
| 2532 | 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 | 2489 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2535 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ | |
| 2536 | std.wasm.simdOpcode(.v128_load), | |
| 2490 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ | |
| 2491 | @intFromEnum(std.wasm.SimdOpcode.v128_load), | |
| 2537 | 2492 | offset + operand.offset(), |
| 2538 | 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 | 2496 | return .stack; |
| 2542 | 2497 | } |
| 2543 | 2498 | |
| 2544 | 2499 | const abi_size: u8 = @intCast(ty.abiSize(zcu)); |
| 2545 | 2500 | const opcode = buildOpcode(.{ |
| 2546 | .valtype1 = typeToValtype(ty, pt, func.target.*), | |
| 2501 | .valtype1 = typeToValtype(ty, zcu, cg.target), | |
| 2547 | 2502 | .width = abi_size * 8, |
| 2548 | 2503 | .op = .load, |
| 2549 | 2504 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, |
| 2550 | 2505 | }); |
| 2551 | 2506 | |
| 2552 | try func.addMemArg( | |
| 2507 | try cg.addMemArg( | |
| 2553 | 2508 | Mir.Inst.Tag.fromOpcode(opcode), |
| 2554 | 2509 | .{ |
| 2555 | 2510 | .offset = offset + operand.offset(), |
| ... | ... | @@ -2560,18 +2515,18 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu |
| 2560 | 2515 | return .stack; |
| 2561 | 2516 | } |
| 2562 | 2517 | |
| 2563 | fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2564 | const pt = func.pt; | |
| 2518 | fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2519 | const pt = cg.pt; | |
| 2565 | 2520 | const zcu = pt.zcu; |
| 2566 | const arg_index = func.arg_index; | |
| 2567 | const arg = func.args[arg_index]; | |
| 2568 | const cc = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?.cc; | |
| 2569 | const arg_ty = func.typeOfIndex(inst); | |
| 2521 | const arg_index = cg.arg_index; | |
| 2522 | const arg = cg.args[arg_index]; | |
| 2523 | const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc; | |
| 2524 | const arg_ty = cg.typeOfIndex(inst); | |
| 2570 | 2525 | if (cc == .wasm_watc) { |
| 2571 | 2526 | const arg_classes = abi.classifyType(arg_ty, zcu); |
| 2572 | 2527 | for (arg_classes) |class| { |
| 2573 | 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 | 2534 | // we combine them into a single stack value |
| 2580 | 2535 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { |
| 2581 | 2536 | if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) { |
| 2582 | return func.fail( | |
| 2537 | return cg.fail( | |
| 2583 | 2538 | "TODO: Implement C-ABI argument for type '{}'", |
| 2584 | 2539 | .{arg_ty.fmt(pt)}, |
| 2585 | 2540 | ); |
| 2586 | 2541 | } |
| 2587 | const result = try func.allocStack(arg_ty); | |
| 2588 | try func.store(result, arg, Type.u64, 0); | |
| 2589 | try func.store(result, func.args[arg_index + 1], Type.u64, 8); | |
| 2590 | return func.finishAir(inst, result, &.{}); | |
| 2542 | const result = try cg.allocStack(arg_ty); | |
| 2543 | try cg.store(result, arg, Type.u64, 0); | |
| 2544 | try cg.store(result, cg.args[arg_index + 1], Type.u64, 8); | |
| 2545 | return cg.finishAir(inst, result, &.{}); | |
| 2591 | 2546 | } |
| 2592 | 2547 | } else { |
| 2593 | func.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 => {}, | |
| 2548 | cg.arg_index += 1; | |
| 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 { | |
| 2613 | const pt = func.pt; | |
| 2614 | const zcu = pt.zcu; | |
| 2615 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 2616 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 2617 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 2618 | const lhs_ty = func.typeOf(bin_op.lhs); | |
| 2619 | const rhs_ty = func.typeOf(bin_op.rhs); | |
| 2554 | fn airBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | |
| 2555 | const zcu = cg.pt.zcu; | |
| 2556 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 2557 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 2558 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 2559 | const lhs_ty = cg.typeOf(bin_op.lhs); | |
| 2560 | const rhs_ty = cg.typeOf(bin_op.rhs); | |
| 2620 | 2561 | |
| 2621 | 2562 | // For certain operations, such as shifting, the types are different. |
| 2622 | 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 | 2567 | const result = switch (op) { |
| 2627 | 2568 | .shr, .shl => result: { |
| 2628 | 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 | 2572 | const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?; |
| 2632 | 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 | 2575 | else |
| 2635 | 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 | 2585 | /// Performs a binary operation on the given `WValue`'s |
| 2645 | 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 { | |
| 2647 | const pt = func.pt; | |
| 2587 | fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | |
| 2588 | const pt = cg.pt; | |
| 2648 | 2589 | const zcu = pt.zcu; |
| 2649 | 2590 | assert(!(lhs != .stack and rhs == .stack)); |
| 2650 | 2591 | |
| 2651 | 2592 | if (ty.isAnyFloat()) { |
| 2652 | 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 | 2598 | if (ty.zigTypeTag(zcu) == .int) { |
| 2658 | return func.binOpBigInt(lhs, rhs, ty, op); | |
| 2599 | return cg.binOpBigInt(lhs, rhs, ty, op); | |
| 2659 | 2600 | } else { |
| 2660 | return func.fail( | |
| 2601 | return cg.fail( | |
| 2661 | 2602 | "TODO: Implement binary operation for type: {}", |
| 2662 | 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 | 2609 | .op = op, |
| 2669 | .valtype1 = typeToValtype(ty, pt, func.target.*), | |
| 2610 | .valtype1 = typeToValtype(ty, zcu, cg.target), | |
| 2670 | 2611 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, |
| 2671 | 2612 | }); |
| 2672 | try func.emitWValue(lhs); | |
| 2673 | try func.emitWValue(rhs); | |
| 2613 | try cg.emitWValue(lhs); | |
| 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 | 2618 | return .stack; |
| 2678 | 2619 | } |
| 2679 | 2620 | |
| 2680 | fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | |
| 2681 | const pt = func.pt; | |
| 2682 | const zcu = pt.zcu; | |
| 2621 | fn binOpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | |
| 2622 | const zcu = cg.pt.zcu; | |
| 2683 | 2623 | const int_info = ty.intInfo(zcu); |
| 2684 | 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 | 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 | 2630 | .div => switch (int_info.signedness) { |
| 2691 | .signed => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2692 | .unsigned => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2631 | .signed => return cg.callIntrinsic(.__divti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2632 | .unsigned => return cg.callIntrinsic(.__udivti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2693 | 2633 | }, |
| 2694 | 2634 | .rem => switch (int_info.signedness) { |
| 2695 | .signed => return func.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2696 | .unsigned => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2635 | .signed => return cg.callIntrinsic(.__modti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2636 | .unsigned => return cg.callIntrinsic(.__umodti3, &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }), | |
| 2697 | 2637 | }, |
| 2698 | 2638 | .shr => switch (int_info.signedness) { |
| 2699 | .signed => return func.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), | |
| 2700 | .unsigned => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }), | |
| 2639 | .signed => return cg.callIntrinsic(.__ashrti3, &.{ 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 | 2643 | .@"and", .@"or", .xor => { |
| 2704 | const result = try func.allocStack(ty); | |
| 2705 | try func.emitWValue(result); | |
| 2706 | const lhs_lsb = try func.load(lhs, Type.u64, 0); | |
| 2707 | const rhs_lsb = try func.load(rhs, Type.u64, 0); | |
| 2708 | const op_lsb = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op); | |
| 2709 | try func.store(.stack, op_lsb, Type.u64, result.offset()); | |
| 2710 | ||
| 2711 | try func.emitWValue(result); | |
| 2712 | const lhs_msb = try func.load(lhs, Type.u64, 8); | |
| 2713 | const rhs_msb = try func.load(rhs, Type.u64, 8); | |
| 2714 | const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op); | |
| 2715 | try func.store(.stack, op_msb, Type.u64, result.offset() + 8); | |
| 2644 | const result = try cg.allocStack(ty); | |
| 2645 | try cg.emitWValue(result); | |
| 2646 | const lhs_lsb = try cg.load(lhs, Type.u64, 0); | |
| 2647 | const rhs_lsb = try cg.load(rhs, Type.u64, 0); | |
| 2648 | const op_lsb = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op); | |
| 2649 | try cg.store(.stack, op_lsb, Type.u64, result.offset()); | |
| 2650 | ||
| 2651 | try cg.emitWValue(result); | |
| 2652 | const lhs_msb = try cg.load(lhs, Type.u64, 8); | |
| 2653 | const rhs_msb = try cg.load(rhs, Type.u64, 8); | |
| 2654 | const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op); | |
| 2655 | try cg.store(.stack, op_msb, Type.u64, result.offset() + 8); | |
| 2716 | 2656 | return result; |
| 2717 | 2657 | }, |
| 2718 | 2658 | .add, .sub => { |
| 2719 | const result = try func.allocStack(ty); | |
| 2720 | var lhs_lsb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); | |
| 2721 | defer lhs_lsb.free(func); | |
| 2722 | var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); | |
| 2723 | defer rhs_lsb.free(func); | |
| 2724 | var op_lsb = try (try func.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(func, Type.u64); | |
| 2725 | defer op_lsb.free(func); | |
| 2726 | ||
| 2727 | const lhs_msb = try func.load(lhs, Type.u64, 8); | |
| 2728 | const rhs_msb = try func.load(rhs, Type.u64, 8); | |
| 2729 | const op_msb = try func.binOp(lhs_msb, rhs_msb, Type.u64, op); | |
| 2659 | const result = try cg.allocStack(ty); | |
| 2660 | var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 2661 | defer lhs_lsb.free(cg); | |
| 2662 | var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 2663 | defer rhs_lsb.free(cg); | |
| 2664 | var op_lsb = try (try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, op)).toLocal(cg, Type.u64); | |
| 2665 | defer op_lsb.free(cg); | |
| 2666 | ||
| 2667 | const lhs_msb = try cg.load(lhs, Type.u64, 8); | |
| 2668 | const rhs_msb = try cg.load(rhs, Type.u64, 8); | |
| 2669 | const op_msb = try cg.binOp(lhs_msb, rhs_msb, Type.u64, op); | |
| 2730 | 2670 | |
| 2731 | 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 | 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 | 2675 | } else unreachable; |
| 2736 | const tmp = try func.intcast(lt, Type.u32, Type.u64); | |
| 2737 | var tmp_op = try (try func.binOp(op_msb, tmp, Type.u64, op)).toLocal(func, Type.u64); | |
| 2738 | defer tmp_op.free(func); | |
| 2676 | const tmp = try cg.intcast(lt, Type.u32, Type.u64); | |
| 2677 | var tmp_op = try (try cg.binOp(op_msb, tmp, Type.u64, op)).toLocal(cg, Type.u64); | |
| 2678 | defer tmp_op.free(cg); | |
| 2739 | 2679 | |
| 2740 | try func.store(result, op_lsb, Type.u64, 0); | |
| 2741 | try func.store(result, tmp_op, Type.u64, 8); | |
| 2680 | try cg.store(result, op_lsb, Type.u64, 0); | |
| 2681 | try cg.store(result, tmp_op, Type.u64, 8); | |
| 2742 | 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 | 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 { | |
| 2825 | const pt = func.pt; | |
| 2812 | fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2813 | const pt = cg.pt; | |
| 2826 | 2814 | const zcu = pt.zcu; |
| 2827 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 2828 | const operand = try func.resolveInst(ty_op.operand); | |
| 2829 | const ty = func.typeOf(ty_op.operand); | |
| 2815 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 2816 | const operand = try cg.resolveInst(ty_op.operand); | |
| 2817 | const ty = cg.typeOf(ty_op.operand); | |
| 2830 | 2818 | const scalar_ty = ty.scalarType(zcu); |
| 2831 | 2819 | |
| 2832 | 2820 | switch (scalar_ty.zigTypeTag(zcu)) { |
| 2833 | 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 | 2823 | } else { |
| 2836 | 2824 | const int_bits = ty.intInfo(zcu).bits; |
| 2837 | 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 | 2829 | switch (wasm_bits) { |
| 2842 | 2830 | 32 => { |
| 2843 | try func.emitWValue(operand); | |
| 2831 | try cg.emitWValue(operand); | |
| 2844 | 2832 | |
| 2845 | try func.addImm32(31); | |
| 2846 | try func.addTag(.i32_shr_s); | |
| 2833 | try cg.addImm32(31); | |
| 2834 | try cg.addTag(.i32_shr_s); | |
| 2847 | 2835 | |
| 2848 | var tmp = try func.allocLocal(ty); | |
| 2849 | defer tmp.free(func); | |
| 2850 | try func.addLabel(.local_tee, tmp.local.value); | |
| 2836 | var tmp = try cg.allocLocal(ty); | |
| 2837 | defer tmp.free(cg); | |
| 2838 | try cg.addLocal(.local_tee, tmp.local.value); | |
| 2851 | 2839 | |
| 2852 | try func.emitWValue(operand); | |
| 2853 | try func.addTag(.i32_xor); | |
| 2854 | try func.emitWValue(tmp); | |
| 2855 | try func.addTag(.i32_sub); | |
| 2856 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 2840 | try cg.emitWValue(operand); | |
| 2841 | try cg.addTag(.i32_xor); | |
| 2842 | try cg.emitWValue(tmp); | |
| 2843 | try cg.addTag(.i32_sub); | |
| 2844 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 2857 | 2845 | }, |
| 2858 | 2846 | 64 => { |
| 2859 | try func.emitWValue(operand); | |
| 2847 | try cg.emitWValue(operand); | |
| 2860 | 2848 | |
| 2861 | try func.addImm64(63); | |
| 2862 | try func.addTag(.i64_shr_s); | |
| 2849 | try cg.addImm64(63); | |
| 2850 | try cg.addTag(.i64_shr_s); | |
| 2863 | 2851 | |
| 2864 | var tmp = try func.allocLocal(ty); | |
| 2865 | defer tmp.free(func); | |
| 2866 | try func.addLabel(.local_tee, tmp.local.value); | |
| 2852 | var tmp = try cg.allocLocal(ty); | |
| 2853 | defer tmp.free(cg); | |
| 2854 | try cg.addLocal(.local_tee, tmp.local.value); | |
| 2867 | 2855 | |
| 2868 | try func.emitWValue(operand); | |
| 2869 | try func.addTag(.i64_xor); | |
| 2870 | try func.emitWValue(tmp); | |
| 2871 | try func.addTag(.i64_sub); | |
| 2872 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 2856 | try cg.emitWValue(operand); | |
| 2857 | try cg.addTag(.i64_xor); | |
| 2858 | try cg.emitWValue(tmp); | |
| 2859 | try cg.addTag(.i64_sub); | |
| 2860 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 2873 | 2861 | }, |
| 2874 | 2862 | 128 => { |
| 2875 | const mask = try func.allocStack(Type.u128); | |
| 2876 | try func.emitWValue(mask); | |
| 2877 | try func.emitWValue(mask); | |
| 2863 | const mask = try cg.allocStack(Type.u128); | |
| 2864 | try cg.emitWValue(mask); | |
| 2865 | try cg.emitWValue(mask); | |
| 2878 | 2866 | |
| 2879 | _ = try func.load(operand, Type.u64, 8); | |
| 2880 | try func.addImm64(63); | |
| 2881 | try func.addTag(.i64_shr_s); | |
| 2867 | _ = try cg.load(operand, Type.u64, 8); | |
| 2868 | try cg.addImm64(63); | |
| 2869 | try cg.addTag(.i64_shr_s); | |
| 2882 | 2870 | |
| 2883 | var tmp = try func.allocLocal(Type.u64); | |
| 2884 | defer tmp.free(func); | |
| 2885 | try func.addLabel(.local_tee, tmp.local.value); | |
| 2886 | try func.store(.stack, .stack, Type.u64, mask.offset() + 0); | |
| 2887 | try func.emitWValue(tmp); | |
| 2888 | try func.store(.stack, .stack, Type.u64, mask.offset() + 8); | |
| 2871 | var tmp = try cg.allocLocal(Type.u64); | |
| 2872 | defer tmp.free(cg); | |
| 2873 | try cg.addLocal(.local_tee, tmp.local.value); | |
| 2874 | try cg.store(.stack, .stack, Type.u64, mask.offset() + 0); | |
| 2875 | try cg.emitWValue(tmp); | |
| 2876 | try cg.store(.stack, .stack, Type.u64, mask.offset() + 8); | |
| 2889 | 2877 | |
| 2890 | const a = try func.binOpBigInt(operand, mask, Type.u128, .xor); | |
| 2891 | const b = try func.binOpBigInt(a, mask, Type.u128, .sub); | |
| 2878 | const a = try cg.binOpBigInt(operand, mask, Type.u128, .xor); | |
| 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 | 2883 | else => unreachable, |
| 2896 | 2884 | } |
| 2897 | 2885 | }, |
| 2898 | 2886 | .float => { |
| 2899 | const result = try func.floatOp(.fabs, ty, &.{operand}); | |
| 2900 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 2887 | const result = try cg.floatOp(.fabs, ty, &.{operand}); | |
| 2888 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 2901 | 2889 | }, |
| 2902 | 2890 | else => unreachable, |
| 2903 | 2891 | } |
| 2904 | 2892 | } |
| 2905 | 2893 | |
| 2906 | fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { | |
| 2907 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 2908 | const operand = try func.resolveInst(un_op); | |
| 2909 | const ty = func.typeOf(un_op); | |
| 2894 | fn airUnaryFloatOp(cg: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { | |
| 2895 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 2896 | const operand = try cg.resolveInst(un_op); | |
| 2897 | const ty = cg.typeOf(un_op); | |
| 2910 | 2898 | |
| 2911 | const result = try func.floatOp(op, ty, &.{operand}); | |
| 2912 | return func.finishAir(inst, result, &.{un_op}); | |
| 2899 | const result = try cg.floatOp(op, ty, &.{operand}); | |
| 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 { | |
| 2916 | const pt = func.pt; | |
| 2917 | const zcu = pt.zcu; | |
| 2903 | fn floatOp(cg: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue { | |
| 2904 | const zcu = cg.pt.zcu; | |
| 2918 | 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 | 2911 | if (float_op == .neg) { |
| 2925 | return func.floatNeg(ty, args[0]); | |
| 2912 | return cg.floatNeg(ty, args[0]); | |
| 2926 | 2913 | } |
| 2927 | 2914 | |
| 2928 | 2915 | if (float_bits == 32 or float_bits == 64) { |
| 2929 | 2916 | if (float_op.toOp()) |op| { |
| 2930 | 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.*) }); | |
| 2934 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 2920 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, zcu, cg.target) }); | |
| 2921 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 2935 | 2922 | return .stack; |
| 2936 | 2923 | } |
| 2937 | 2924 | } |
| 2938 | 2925 | |
| 2939 | var fn_name_buf: [64]u8 = undefined; | |
| 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 | }; | |
| 2926 | const intrinsic = float_op.intrinsic(float_bits); | |
| 2972 | 2927 | |
| 2973 | 2928 | // fma requires three operands |
| 2974 | 2929 | var param_types_buffer: [3]InternPool.Index = .{ ty.ip_index, ty.ip_index, ty.ip_index }; |
| 2975 | 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 | 2934 | /// NOTE: The result value remains on top of the stack. |
| 2980 | fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { | |
| 2981 | const float_bits = ty.floatBits(func.target.*); | |
| 2935 | fn floatNeg(cg: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { | |
| 2936 | const float_bits = ty.floatBits(cg.target.*); | |
| 2982 | 2937 | switch (float_bits) { |
| 2983 | 2938 | 16 => { |
| 2984 | try func.emitWValue(arg); | |
| 2985 | try func.addImm32(0x8000); | |
| 2986 | try func.addTag(.i32_xor); | |
| 2939 | try cg.emitWValue(arg); | |
| 2940 | try cg.addImm32(0x8000); | |
| 2941 | try cg.addTag(.i32_xor); | |
| 2987 | 2942 | return .stack; |
| 2988 | 2943 | }, |
| 2989 | 2944 | 32, 64 => { |
| 2990 | try func.emitWValue(arg); | |
| 2991 | const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64; | |
| 2945 | try cg.emitWValue(arg); | |
| 2946 | const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64; | |
| 2992 | 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 | 2949 | return .stack; |
| 2995 | 2950 | }, |
| 2996 | 2951 | 80, 128 => { |
| 2997 | const result = try func.allocStack(ty); | |
| 2998 | try func.emitWValue(result); | |
| 2999 | try func.emitWValue(arg); | |
| 3000 | try func.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 }); | |
| 3001 | try func.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 }); | |
| 2952 | const result = try cg.allocStack(ty); | |
| 2953 | try cg.emitWValue(result); | |
| 2954 | try cg.emitWValue(arg); | |
| 2955 | try cg.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 }); | |
| 2956 | try cg.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 }); | |
| 3002 | 2957 | |
| 3003 | try func.emitWValue(result); | |
| 3004 | try func.emitWValue(arg); | |
| 3005 | try func.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 }); | |
| 2958 | try cg.emitWValue(result); | |
| 2959 | try cg.emitWValue(arg); | |
| 2960 | try cg.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 }); | |
| 3006 | 2961 | |
| 3007 | 2962 | if (float_bits == 80) { |
| 3008 | try func.addImm64(0x8000); | |
| 3009 | try func.addTag(.i64_xor); | |
| 3010 | try func.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 }); | |
| 2963 | try cg.addImm64(0x8000); | |
| 2964 | try cg.addTag(.i64_xor); | |
| 2965 | try cg.addMemArg(.i64_store16, .{ .offset = 8 + result.offset(), .alignment = 2 }); | |
| 3011 | 2966 | } else { |
| 3012 | try func.addImm64(0x8000000000000000); | |
| 3013 | try func.addTag(.i64_xor); | |
| 3014 | try func.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 }); | |
| 2967 | try cg.addImm64(0x8000000000000000); | |
| 2968 | try cg.addTag(.i64_xor); | |
| 2969 | try cg.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 }); | |
| 3015 | 2970 | } |
| 3016 | 2971 | return result; |
| 3017 | 2972 | }, |
| ... | ... | @@ -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 { | |
| 3023 | const pt = func.pt; | |
| 3024 | const zcu = pt.zcu; | |
| 3025 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 2977 | fn airWrapBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | |
| 2978 | const zcu = cg.pt.zcu; | |
| 2979 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 3026 | 2980 | |
| 3027 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 3028 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 3029 | const lhs_ty = func.typeOf(bin_op.lhs); | |
| 3030 | const rhs_ty = func.typeOf(bin_op.rhs); | |
| 2981 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 2982 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 2983 | const lhs_ty = cg.typeOf(bin_op.lhs); | |
| 2984 | const rhs_ty = cg.typeOf(bin_op.rhs); | |
| 3031 | 2985 | |
| 3032 | 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 | 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 | 2995 | const result = switch (op) { |
| 3042 | 2996 | .shr, .shl => result: { |
| 3043 | 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 | 3000 | const rhs_wasm_bits = toWasmBits(@intCast(rhs_ty.bitSize(zcu))).?; |
| 3047 | 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 | 3003 | else |
| 3050 | 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 | 3013 | /// Performs a wrapping binary operation. |
| 3060 | 3014 | /// Asserts rhs is not a stack value when lhs also isn't. |
| 3061 | 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 { | |
| 3063 | const bin_local = try func.binOp(lhs, rhs, ty, op); | |
| 3064 | return func.wrapOperand(bin_local, ty); | |
| 3016 | fn wrapBinOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | |
| 3017 | const bin_local = try cg.binOp(lhs, rhs, ty, op); | |
| 3018 | return cg.wrapOperand(bin_local, ty); | |
| 3065 | 3019 | } |
| 3066 | 3020 | |
| 3067 | 3021 | /// Wraps an operand based on a given type's bitsize. |
| 3068 | 3022 | /// Asserts `Type` is <= 128 bits. |
| 3069 | 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 { | |
| 3071 | const pt = func.pt; | |
| 3072 | const zcu = pt.zcu; | |
| 3024 | fn wrapOperand(cg: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { | |
| 3025 | const zcu = cg.pt.zcu; | |
| 3073 | 3026 | assert(ty.abiSize(zcu) <= 16); |
| 3074 | 3027 | const int_bits: u16 = @intCast(ty.bitSize(zcu)); // TODO use ty.intInfo(zcu).bits |
| 3075 | 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 | 3032 | if (wasm_bits == int_bits) return operand; |
| 3080 | 3033 | |
| 3081 | 3034 | switch (wasm_bits) { |
| 3082 | 3035 | 32 => { |
| 3083 | try func.emitWValue(operand); | |
| 3036 | try cg.emitWValue(operand); | |
| 3084 | 3037 | if (ty.isSignedInt(zcu)) { |
| 3085 | try func.addImm32(32 - int_bits); | |
| 3086 | try func.addTag(.i32_shl); | |
| 3087 | try func.addImm32(32 - int_bits); | |
| 3088 | try func.addTag(.i32_shr_s); | |
| 3038 | try cg.addImm32(32 - int_bits); | |
| 3039 | try cg.addTag(.i32_shl); | |
| 3040 | try cg.addImm32(32 - int_bits); | |
| 3041 | try cg.addTag(.i32_shr_s); | |
| 3089 | 3042 | } else { |
| 3090 | try func.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits)); | |
| 3091 | try func.addTag(.i32_and); | |
| 3043 | try cg.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits)); | |
| 3044 | try cg.addTag(.i32_and); | |
| 3092 | 3045 | } |
| 3093 | 3046 | return .stack; |
| 3094 | 3047 | }, |
| 3095 | 3048 | 64 => { |
| 3096 | try func.emitWValue(operand); | |
| 3049 | try cg.emitWValue(operand); | |
| 3097 | 3050 | if (ty.isSignedInt(zcu)) { |
| 3098 | try func.addImm64(64 - int_bits); | |
| 3099 | try func.addTag(.i64_shl); | |
| 3100 | try func.addImm64(64 - int_bits); | |
| 3101 | try func.addTag(.i64_shr_s); | |
| 3051 | try cg.addImm64(64 - int_bits); | |
| 3052 | try cg.addTag(.i64_shl); | |
| 3053 | try cg.addImm64(64 - int_bits); | |
| 3054 | try cg.addTag(.i64_shr_s); | |
| 3102 | 3055 | } else { |
| 3103 | try func.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits)); | |
| 3104 | try func.addTag(.i64_and); | |
| 3056 | try cg.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits)); | |
| 3057 | try cg.addTag(.i64_and); | |
| 3105 | 3058 | } |
| 3106 | 3059 | return .stack; |
| 3107 | 3060 | }, |
| 3108 | 3061 | 128 => { |
| 3109 | 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); | |
| 3113 | _ = try func.load(operand, Type.u64, 0); | |
| 3114 | try func.store(.stack, .stack, Type.u64, result.offset()); | |
| 3065 | try cg.emitWValue(result); | |
| 3066 | _ = try cg.load(operand, Type.u64, 0); | |
| 3067 | try cg.store(.stack, .stack, Type.u64, result.offset()); | |
| 3115 | 3068 | |
| 3116 | try func.emitWValue(result); | |
| 3117 | _ = try func.load(operand, Type.u64, 8); | |
| 3069 | try cg.emitWValue(result); | |
| 3070 | _ = try cg.load(operand, Type.u64, 8); | |
| 3118 | 3071 | if (ty.isSignedInt(zcu)) { |
| 3119 | try func.addImm64(128 - int_bits); | |
| 3120 | try func.addTag(.i64_shl); | |
| 3121 | try func.addImm64(128 - int_bits); | |
| 3122 | try func.addTag(.i64_shr_s); | |
| 3072 | try cg.addImm64(128 - int_bits); | |
| 3073 | try cg.addTag(.i64_shl); | |
| 3074 | try cg.addImm64(128 - int_bits); | |
| 3075 | try cg.addTag(.i64_shr_s); | |
| 3123 | 3076 | } else { |
| 3124 | try func.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits)); | |
| 3125 | try func.addTag(.i64_and); | |
| 3077 | try cg.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits)); | |
| 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 | 3082 | return result; |
| 3130 | 3083 | }, |
| ... | ... | @@ -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 { | |
| 3136 | const pt = func.pt; | |
| 3088 | fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerError!WValue { | |
| 3089 | const pt = cg.pt; | |
| 3137 | 3090 | const zcu = pt.zcu; |
| 3138 | 3091 | const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; |
| 3139 | 3092 | const offset: u64 = prev_offset + ptr.byte_offset; |
| 3140 | 3093 | return switch (ptr.base_addr) { |
| 3141 | .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)), | |
| 3142 | .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)), | |
| 3143 | .int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize), | |
| 3144 | .eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}), | |
| 3145 | .opt_payload => |opt_ptr| return func.lowerPtr(opt_ptr, offset), | |
| 3094 | .nav => |nav| return .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } }, | |
| 3095 | .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset), .orig_ptr_ty = uav.orig_ty } }, | |
| 3096 | .int => return cg.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize), | |
| 3097 | .eu_payload => return cg.fail("Wasm TODO: lower error union payload pointer", .{}), | |
| 3098 | .opt_payload => |opt_ptr| return cg.lowerPtr(opt_ptr, offset), | |
| 3146 | 3099 | .field => |field| { |
| 3147 | 3100 | const base_ptr = Value.fromInterned(field.base); |
| 3148 | 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 | 3104 | assert(base_ty.isSlice(zcu)); |
| 3152 | 3105 | break :off switch (field.index) { |
| 3153 | 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 | 3108 | else => unreachable, |
| 3156 | 3109 | }; |
| 3157 | 3110 | }, |
| ... | ... | @@ -3177,70 +3130,19 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr |
| 3177 | 3130 | }, |
| 3178 | 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 | 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 | 3139 | /// Asserts that `isByRef` returns `false` for `ty`. |
| 3238 | fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | |
| 3239 | const pt = func.pt; | |
| 3140 | fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { | |
| 3141 | const pt = cg.pt; | |
| 3240 | 3142 | const zcu = pt.zcu; |
| 3241 | assert(!isByRef(ty, pt, func.target.*)); | |
| 3143 | assert(!isByRef(ty, zcu, cg.target)); | |
| 3242 | 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 | 3147 | switch (ip.indexToKey(val.ip_index)) { |
| 3246 | 3148 | .int_type, |
| ... | ... | @@ -3319,14 +3221,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3319 | 3221 | const payload_type = ty.errorUnionPayload(zcu); |
| 3320 | 3222 | if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 3321 | 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 | 3229 | .enum_tag => |enum_tag| { |
| 3328 | 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 | 3233 | .float => |float| switch (float.storage) { |
| 3332 | 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 | 3236 | .f64 => |f64_val| return .{ .float64 = f64_val }, |
| 3335 | 3237 | else => unreachable, |
| 3336 | 3238 | }, |
| 3337 | .slice => switch (try func.bin_file.lowerUav(pt, val.toIntern(), .none, func.src_loc)) { | |
| 3338 | .mcv => |mcv| return .{ .memory = mcv.load_symbol }, | |
| 3339 | .fail => |err_msg| { | |
| 3340 | func.err_msg = err_msg; | |
| 3341 | return error.CodegenFail; | |
| 3342 | }, | |
| 3343 | }, | |
| 3344 | .ptr => return func.lowerPtr(val.toIntern(), 0), | |
| 3239 | .slice => unreachable, // isByRef == true | |
| 3240 | .ptr => return cg.lowerPtr(val.toIntern(), 0), | |
| 3345 | 3241 | .opt => if (ty.optionalReprIsPayload(zcu)) { |
| 3346 | 3242 | const pl_ty = ty.optionalChild(zcu); |
| 3347 | 3243 | if (val.optionalValue(zcu)) |payload| { |
| 3348 | return func.lowerConstant(payload, pl_ty); | |
| 3244 | return cg.lowerConstant(payload, pl_ty); | |
| 3349 | 3245 | } else { |
| 3350 | 3246 | return .{ .imm32 = 0 }; |
| 3351 | 3247 | } |
| ... | ... | @@ -3353,12 +3249,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3353 | 3249 | return .{ .imm32 = @intFromBool(!val.isNull(zcu)) }; |
| 3354 | 3250 | }, |
| 3355 | 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 | 3253 | .vector_type => { |
| 3358 | assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct); | |
| 3254 | assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct); | |
| 3359 | 3255 | var buf: [16]u8 = undefined; |
| 3360 | 3256 | val.writeToMemory(pt, &buf) catch unreachable; |
| 3361 | return func.storeSimdImmd(buf); | |
| 3257 | return cg.storeSimdImmd(buf); | |
| 3362 | 3258 | }, |
| 3363 | 3259 | .struct_type => { |
| 3364 | 3260 | const struct_type = ip.loadStructType(ty.toIntern()); |
| ... | ... | @@ -3372,7 +3268,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3372 | 3268 | backing_int_ty, |
| 3373 | 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 | 3273 | else => unreachable, |
| 3378 | 3274 | }, |
| ... | ... | @@ -3385,7 +3281,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3385 | 3281 | const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; |
| 3386 | 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 | 3286 | .memoized_call => unreachable, |
| 3391 | 3287 | } |
| ... | ... | @@ -3393,15 +3289,14 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3393 | 3289 | |
| 3394 | 3290 | /// Stores the value as a 128bit-immediate value by storing it inside |
| 3395 | 3291 | /// the list and returning the index into this list as `WValue`. |
| 3396 | fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue { | |
| 3397 | const index = @as(u32, @intCast(func.simd_immediates.items.len)); | |
| 3398 | try func.simd_immediates.append(func.gpa, value); | |
| 3292 | fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue { | |
| 3293 | const index = @as(u32, @intCast(cg.simd_immediates.items.len)); | |
| 3294 | try cg.simd_immediates.append(cg.gpa, value); | |
| 3399 | 3295 | return .{ .imm128 = index }; |
| 3400 | 3296 | } |
| 3401 | 3297 | |
| 3402 | fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { | |
| 3403 | const pt = func.pt; | |
| 3404 | const zcu = pt.zcu; | |
| 3298 | fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { | |
| 3299 | const zcu = cg.pt.zcu; | |
| 3405 | 3300 | const ip = &zcu.intern_pool; |
| 3406 | 3301 | switch (ty.zigTypeTag(zcu)) { |
| 3407 | 3302 | .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa }, |
| ... | ... | @@ -3410,21 +3305,20 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { |
| 3410 | 3305 | 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, |
| 3411 | 3306 | else => unreachable, |
| 3412 | 3307 | }, |
| 3413 | .float => switch (ty.floatBits(func.target.*)) { | |
| 3308 | .float => switch (ty.floatBits(cg.target.*)) { | |
| 3414 | 3309 | 16 => return .{ .imm32 = 0xaaaaaaaa }, |
| 3415 | 3310 | 32 => return .{ .float32 = @as(f32, @bitCast(@as(u32, 0xaaaaaaaa))) }, |
| 3416 | 3311 | 64 => return .{ .float64 = @as(f64, @bitCast(@as(u64, 0xaaaaaaaaaaaaaaaa))) }, |
| 3417 | 3312 | else => unreachable, |
| 3418 | 3313 | }, |
| 3419 | .pointer => switch (func.arch()) { | |
| 3314 | .pointer => switch (cg.ptr_size) { | |
| 3420 | 3315 | .wasm32 => return .{ .imm32 = 0xaaaaaaaa }, |
| 3421 | 3316 | .wasm64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, |
| 3422 | else => unreachable, | |
| 3423 | 3317 | }, |
| 3424 | 3318 | .optional => { |
| 3425 | 3319 | const pl_ty = ty.optionalChild(zcu); |
| 3426 | 3320 | if (ty.optionalReprIsPayload(zcu)) { |
| 3427 | return func.emitUndefined(pl_ty); | |
| 3321 | return cg.emitUndefined(pl_ty); | |
| 3428 | 3322 | } |
| 3429 | 3323 | return .{ .imm32 = 0xaaaaaaaa }; |
| 3430 | 3324 | }, |
| ... | ... | @@ -3433,26 +3327,25 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { |
| 3433 | 3327 | }, |
| 3434 | 3328 | .@"struct" => { |
| 3435 | 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 | 3336 | /// Returns a `Value` as a signed 32 bit value. |
| 3443 | 3337 | /// It's illegal to provide a value with a type that cannot be represented |
| 3444 | 3338 | /// as an integer value. |
| 3445 | fn valueAsI32(func: *const CodeGen, val: Value) i32 { | |
| 3446 | const pt = func.pt; | |
| 3447 | const zcu = pt.zcu; | |
| 3339 | fn valueAsI32(cg: *const CodeGen, val: Value) i32 { | |
| 3340 | const zcu = cg.pt.zcu; | |
| 3448 | 3341 | const ip = &zcu.intern_pool; |
| 3449 | 3342 | |
| 3450 | 3343 | switch (val.toIntern()) { |
| 3451 | 3344 | .bool_true => return 1, |
| 3452 | 3345 | .bool_false => return 0, |
| 3453 | 3346 | else => return switch (ip.indexToKey(val.ip_index)) { |
| 3454 | .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, pt), | |
| 3455 | .int => |int| intStorageAsI32(int.storage, pt), | |
| 3347 | .enum_tag => |enum_tag| intIndexAsI32(ip, enum_tag.int, zcu), | |
| 3348 | .int => |int| intStorageAsI32(int.storage, zcu), | |
| 3456 | 3349 | .ptr => |ptr| { |
| 3457 | 3350 | assert(ptr.base_addr == .int); |
| 3458 | 3351 | return @intCast(ptr.byte_offset); |
| ... | ... | @@ -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 { | |
| 3467 | return intStorageAsI32(ip.indexToKey(int).int.storage, pt); | |
| 3359 | fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, zcu: *const Zcu) i32 { | |
| 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 { | |
| 3471 | const zcu = pt.zcu; | |
| 3363 | fn intStorageAsI32(storage: InternPool.Key.Int.Storage, zcu: *const Zcu) i32 { | |
| 3472 | 3364 | return switch (storage) { |
| 3473 | 3365 | .i64 => |x| @as(i32, @intCast(x)), |
| 3474 | 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 | 3370 | }; |
| 3479 | 3371 | } |
| 3480 | 3372 | |
| 3481 | fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3482 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3483 | const extra = func.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])); | |
| 3373 | fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3374 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3375 | const extra = cg.air.extraData(Air.Block, ty_pl.payload); | |
| 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 { | |
| 3488 | const pt = func.pt; | |
| 3489 | const wasm_block_ty = genBlockType(block_ty, pt, func.target.*); | |
| 3379 | fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { | |
| 3380 | const zcu = cg.pt.zcu; | |
| 3381 | const wasm_block_ty = genBlockType(block_ty, zcu, cg.target); | |
| 3490 | 3382 | |
| 3491 | 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: { | |
| 3493 | const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty; | |
| 3494 | break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten | |
| 3384 | const block_result: WValue = if (wasm_block_ty != .empty) blk: { | |
| 3385 | const ty: Type = if (isByRef(block_ty, zcu, cg.target)) Type.u32 else block_ty; | |
| 3386 | break :blk try cg.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten | |
| 3495 | 3387 | } else .none; |
| 3496 | 3388 | |
| 3497 | try func.startBlock(.block, wasm.block_empty); | |
| 3389 | try cg.startBlock(.block, .empty); | |
| 3498 | 3390 | // Here we set the current block idx, so breaks know the depth to jump |
| 3499 | 3391 | // to when breaking out. |
| 3500 | try func.blocks.putNoClobber(func.gpa, inst, .{ | |
| 3501 | .label = func.block_depth, | |
| 3392 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ | |
| 3393 | .label = cg.block_depth, | |
| 3502 | 3394 | .value = block_result, |
| 3503 | 3395 | }); |
| 3504 | 3396 | |
| 3505 | try func.genBody(body); | |
| 3506 | try func.endBlock(); | |
| 3397 | try cg.genBody(body); | |
| 3398 | try cg.endBlock(); | |
| 3507 | 3399 | |
| 3508 | const liveness = func.liveness.getBlock(inst); | |
| 3509 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths.len); | |
| 3400 | const liveness = cg.liveness.getBlock(inst); | |
| 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 | 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 { | |
| 3516 | func.block_depth += 1; | |
| 3517 | try func.addInst(.{ | |
| 3407 | fn startBlock(cg: *CodeGen, block_tag: std.wasm.Opcode, block_type: std.wasm.BlockType) !void { | |
| 3408 | cg.block_depth += 1; | |
| 3409 | try cg.addInst(.{ | |
| 3518 | 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 | 3415 | /// Ends the current wasm block and decreases the `block_depth` by 1 |
| 3524 | fn endBlock(func: *CodeGen) !void { | |
| 3525 | try func.addTag(.end); | |
| 3526 | func.block_depth -= 1; | |
| 3416 | fn endBlock(cg: *CodeGen) !void { | |
| 3417 | try cg.addTag(.end); | |
| 3418 | cg.block_depth -= 1; | |
| 3527 | 3419 | } |
| 3528 | 3420 | |
| 3529 | fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3530 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3531 | const loop = func.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]); | |
| 3421 | fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3422 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3423 | const loop = cg.air.extraData(Air.Block, ty_pl.payload); | |
| 3424 | const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[loop.end..][0..loop.data.body_len]); | |
| 3533 | 3425 | |
| 3534 | 3426 | // result type of loop is always 'noreturn', meaning we can always |
| 3535 | 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); | |
| 3539 | defer assert(func.loops.remove(inst)); | |
| 3430 | try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth); | |
| 3431 | defer assert(cg.loops.remove(inst)); | |
| 3540 | 3432 | |
| 3541 | try func.genBody(body); | |
| 3542 | try func.endBlock(); | |
| 3433 | try cg.genBody(body); | |
| 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 { | |
| 3548 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 3549 | const condition = try func.resolveInst(pl_op.operand); | |
| 3550 | const extra = func.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]); | |
| 3552 | const else_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); | |
| 3553 | const liveness_condbr = func.liveness.getCondBr(inst); | |
| 3439 | fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3440 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 3441 | const condition = try cg.resolveInst(pl_op.operand); | |
| 3442 | const extra = cg.air.extraData(Air.CondBr, pl_op.payload); | |
| 3443 | const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.then_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]); | |
| 3445 | const liveness_condbr = cg.liveness.getCondBr(inst); | |
| 3554 | 3446 | |
| 3555 | 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 | 3449 | // emit the conditional value |
| 3558 | try func.emitWValue(condition); | |
| 3450 | try cg.emitWValue(condition); | |
| 3559 | 3451 | |
| 3560 | 3452 | // we inserted the block in front of the condition |
| 3561 | 3453 | // so now check if condition matches. If not, break outside this block |
| 3562 | 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(.{}); | |
| 3568 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len))); | |
| 3459 | cg.branches.appendAssumeCapacity(.{}); | |
| 3460 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.else_deaths.len))); | |
| 3569 | 3461 | defer { |
| 3570 | var else_stack = func.branches.pop(); | |
| 3571 | else_stack.deinit(func.gpa); | |
| 3462 | var else_stack = cg.branches.pop(); | |
| 3463 | else_stack.deinit(cg.gpa); | |
| 3572 | 3464 | } |
| 3573 | try func.genBody(else_body); | |
| 3574 | try func.endBlock(); | |
| 3465 | try cg.genBody(else_body); | |
| 3466 | try cg.endBlock(); | |
| 3575 | 3467 | } |
| 3576 | 3468 | |
| 3577 | 3469 | // Outer block that matches the condition |
| 3578 | 3470 | { |
| 3579 | func.branches.appendAssumeCapacity(.{}); | |
| 3580 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len))); | |
| 3471 | cg.branches.appendAssumeCapacity(.{}); | |
| 3472 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, @as(u32, @intCast(liveness_condbr.then_deaths.len))); | |
| 3581 | 3473 | defer { |
| 3582 | var then_stack = func.branches.pop(); | |
| 3583 | then_stack.deinit(func.gpa); | |
| 3474 | var then_stack = cg.branches.pop(); | |
| 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 { | |
| 3592 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 3483 | fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void { | |
| 3484 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 3593 | 3485 | |
| 3594 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 3595 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 3596 | const operand_ty = func.typeOf(bin_op.lhs); | |
| 3597 | const result = try func.cmp(lhs, rhs, operand_ty, op); | |
| 3598 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3486 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 3487 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 3488 | const operand_ty = cg.typeOf(bin_op.lhs); | |
| 3489 | const result = try cg.cmp(lhs, rhs, operand_ty, op); | |
| 3490 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3599 | 3491 | } |
| 3600 | 3492 | |
| 3601 | 3493 | /// Compares two operands. |
| 3602 | 3494 | /// Asserts rhs is not a stack value when the lhs isn't a stack value either |
| 3603 | 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 | 3497 | assert(!(lhs != .stack and rhs == .stack)); |
| 3606 | const pt = func.pt; | |
| 3607 | const zcu = pt.zcu; | |
| 3498 | const zcu = cg.pt.zcu; | |
| 3608 | 3499 | if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) { |
| 3609 | 3500 | const payload_ty = ty.optionalChild(zcu); |
| 3610 | 3501 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 3611 | 3502 | // When we hit this case, we must check the value of optionals |
| 3612 | 3503 | // that are not pointers. This means first checking against non-null for |
| 3613 | 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 | 3507 | } else if (ty.isAnyFloat()) { |
| 3617 | return func.cmpFloat(ty, lhs, rhs, op); | |
| 3618 | } else if (isByRef(ty, pt, func.target.*)) { | |
| 3619 | return func.cmpBigInt(lhs, rhs, ty, op); | |
| 3508 | return cg.cmpFloat(ty, lhs, rhs, op); | |
| 3509 | } else if (isByRef(ty, zcu, cg.target)) { | |
| 3510 | return cg.cmpBigInt(lhs, rhs, ty, op); | |
| 3620 | 3511 | } |
| 3621 | 3512 | |
| 3622 | 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 | 3520 | |
| 3630 | 3521 | // ensure that when we compare pointers, we emit |
| 3631 | 3522 | // the true pointer of a stack value, rather than the stack pointer. |
| 3632 | try func.lowerToStack(lhs); | |
| 3633 | try func.lowerToStack(rhs); | |
| 3523 | try cg.lowerToStack(lhs); | |
| 3524 | try cg.lowerToStack(rhs); | |
| 3634 | 3525 | |
| 3635 | const opcode: wasm.Opcode = buildOpcode(.{ | |
| 3636 | .valtype1 = typeToValtype(ty, pt, func.target.*), | |
| 3526 | const opcode: std.wasm.Opcode = buildOpcode(.{ | |
| 3527 | .valtype1 = typeToValtype(ty, zcu, cg.target), | |
| 3637 | 3528 | .op = switch (op) { |
| 3638 | 3529 | .lt => .lt, |
| 3639 | 3530 | .lte => .le, |
| ... | ... | @@ -3644,15 +3535,15 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO |
| 3644 | 3535 | }, |
| 3645 | 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 | 3540 | return .stack; |
| 3650 | 3541 | } |
| 3651 | 3542 | |
| 3652 | 3543 | /// Compares two floats. |
| 3653 | 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 { | |
| 3655 | const float_bits = ty.floatBits(func.target.*); | |
| 3545 | fn cmpFloat(cg: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math.CompareOperator) InnerError!WValue { | |
| 3546 | const float_bits = ty.floatBits(cg.target.*); | |
| 3656 | 3547 | |
| 3657 | 3548 | const op: Op = switch (cmp_op) { |
| 3658 | 3549 | .lt => .lt, |
| ... | ... | @@ -3665,143 +3556,137 @@ fn cmpFloat(func: *CodeGen, ty: Type, lhs: WValue, rhs: WValue, cmp_op: std.math |
| 3665 | 3556 | |
| 3666 | 3557 | switch (float_bits) { |
| 3667 | 3558 | 16 => { |
| 3668 | _ = try func.fpext(lhs, Type.f16, Type.f32); | |
| 3669 | _ = try func.fpext(rhs, Type.f16, Type.f32); | |
| 3559 | _ = try cg.fpext(lhs, Type.f16, Type.f32); | |
| 3560 | _ = try cg.fpext(rhs, Type.f16, Type.f32); | |
| 3670 | 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 | 3563 | return .stack; |
| 3673 | 3564 | }, |
| 3674 | 3565 | 32, 64 => { |
| 3675 | try func.emitWValue(lhs); | |
| 3676 | try func.emitWValue(rhs); | |
| 3677 | const val_type: wasm.Valtype = if (float_bits == 32) .f32 else .f64; | |
| 3566 | try cg.emitWValue(lhs); | |
| 3567 | try cg.emitWValue(rhs); | |
| 3568 | const val_type: std.wasm.Valtype = if (float_bits == 32) .f32 else .f64; | |
| 3678 | 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 | 3571 | return .stack; |
| 3681 | 3572 | }, |
| 3682 | 3573 | 80, 128 => { |
| 3683 | var fn_name_buf: [32]u8 = undefined; | |
| 3684 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__{s}{s}f2", .{ | |
| 3685 | @tagName(op), target_util.compilerRtFloatAbbrev(float_bits), | |
| 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); | |
| 3574 | const intrinsic = floatCmpIntrinsic(cmp_op, float_bits); | |
| 3575 | const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, Type.bool, &.{ lhs, rhs }); | |
| 3576 | return cg.cmp(result, .{ .imm32 = 0 }, Type.i32, cmp_op); | |
| 3690 | 3577 | }, |
| 3691 | 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 | 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 { | |
| 3701 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 3702 | const operand = try func.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) }; | |
| 3587 | fn airCmpLtErrorsLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3588 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 3589 | const operand = try cg.resolveInst(un_op); | |
| 3705 | 3590 | |
| 3706 | try func.emitWValue(operand); | |
| 3707 | const pt = func.pt; | |
| 3591 | try cg.emitWValue(operand); | |
| 3592 | const pt = cg.pt; | |
| 3708 | 3593 | const err_int_ty = try pt.errorIntType(); |
| 3709 | const errors_len_val = try func.load(errors_len, err_int_ty, 0); | |
| 3710 | const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt); | |
| 3594 | try cg.addTag(.errors_len); | |
| 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 { | |
| 3716 | const zcu = func.pt.zcu; | |
| 3717 | const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br; | |
| 3718 | const block = func.blocks.get(br.block_inst).?; | |
| 3600 | fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3601 | const zcu = cg.pt.zcu; | |
| 3602 | const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br; | |
| 3603 | const block = cg.blocks.get(br.block_inst).?; | |
| 3719 | 3604 | |
| 3720 | 3605 | // if operand has codegen bits we should break with a value |
| 3721 | if (func.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) { | |
| 3722 | const operand = try func.resolveInst(br.operand); | |
| 3723 | try func.lowerToStack(operand); | |
| 3606 | if (cg.typeOf(br.operand).hasRuntimeBitsIgnoreComptime(zcu)) { | |
| 3607 | const operand = try cg.resolveInst(br.operand); | |
| 3608 | try cg.lowerToStack(operand); | |
| 3724 | 3609 | |
| 3725 | 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 | 3615 | // We map every block to its block index. |
| 3731 | 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; | |
| 3733 | try func.addLabel(.br, idx); | |
| 3617 | const idx: u32 = cg.block_depth - block.label; | |
| 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 { | |
| 3739 | const repeat = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat; | |
| 3740 | const loop_label = func.loops.get(repeat.loop_inst).?; | |
| 3623 | fn airRepeat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3624 | const repeat = cg.air.instructions.items(.data)[@intFromEnum(inst)].repeat; | |
| 3625 | const loop_label = cg.loops.get(repeat.loop_inst).?; | |
| 3741 | 3626 | |
| 3742 | const idx: u32 = func.block_depth - loop_label; | |
| 3743 | try func.addLabel(.br, idx); | |
| 3627 | const idx: u32 = cg.block_depth - loop_label; | |
| 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 { | |
| 3749 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 3633 | fn airNot(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3634 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 3750 | 3635 | |
| 3751 | const operand = try func.resolveInst(ty_op.operand); | |
| 3752 | const operand_ty = func.typeOf(ty_op.operand); | |
| 3753 | const pt = func.pt; | |
| 3636 | const operand = try cg.resolveInst(ty_op.operand); | |
| 3637 | const operand_ty = cg.typeOf(ty_op.operand); | |
| 3638 | const pt = cg.pt; | |
| 3754 | 3639 | const zcu = pt.zcu; |
| 3755 | 3640 | |
| 3756 | 3641 | const result = result: { |
| 3757 | 3642 | if (operand_ty.zigTypeTag(zcu) == .bool) { |
| 3758 | try func.emitWValue(operand); | |
| 3759 | try func.addTag(.i32_eqz); | |
| 3760 | const not_tmp = try func.allocLocal(operand_ty); | |
| 3761 | try func.addLabel(.local_set, not_tmp.local.value); | |
| 3643 | try cg.emitWValue(operand); | |
| 3644 | try cg.addTag(.i32_eqz); | |
| 3645 | const not_tmp = try cg.allocLocal(operand_ty); | |
| 3646 | try cg.addLocal(.local_set, not_tmp.local.value); | |
| 3762 | 3647 | break :result not_tmp; |
| 3763 | 3648 | } else { |
| 3764 | 3649 | const int_info = operand_ty.intInfo(zcu); |
| 3765 | 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 | 3654 | switch (wasm_bits) { |
| 3770 | 3655 | 32 => { |
| 3771 | try func.emitWValue(operand); | |
| 3772 | try func.addImm32(switch (int_info.signedness) { | |
| 3656 | try cg.emitWValue(operand); | |
| 3657 | try cg.addImm32(switch (int_info.signedness) { | |
| 3773 | 3658 | .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits), |
| 3774 | 3659 | .signed => ~@as(u32, 0), |
| 3775 | 3660 | }); |
| 3776 | try func.addTag(.i32_xor); | |
| 3661 | try cg.addTag(.i32_xor); | |
| 3777 | 3662 | break :result .stack; |
| 3778 | 3663 | }, |
| 3779 | 3664 | 64 => { |
| 3780 | try func.emitWValue(operand); | |
| 3781 | try func.addImm64(switch (int_info.signedness) { | |
| 3665 | try cg.emitWValue(operand); | |
| 3666 | try cg.addImm64(switch (int_info.signedness) { | |
| 3782 | 3667 | .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits), |
| 3783 | 3668 | .signed => ~@as(u64, 0), |
| 3784 | 3669 | }); |
| 3785 | try func.addTag(.i64_xor); | |
| 3670 | try cg.addTag(.i64_xor); | |
| 3786 | 3671 | break :result .stack; |
| 3787 | 3672 | }, |
| 3788 | 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); | |
| 3792 | _ = try func.load(operand, Type.u64, 0); | |
| 3793 | try func.addImm64(~@as(u64, 0)); | |
| 3794 | try func.addTag(.i64_xor); | |
| 3795 | try func.store(.stack, .stack, Type.u64, ptr.offset()); | |
| 3676 | try cg.emitWValue(ptr); | |
| 3677 | _ = try cg.load(operand, Type.u64, 0); | |
| 3678 | try cg.addImm64(~@as(u64, 0)); | |
| 3679 | try cg.addTag(.i64_xor); | |
| 3680 | try cg.store(.stack, .stack, Type.u64, ptr.offset()); | |
| 3796 | 3681 | |
| 3797 | try func.emitWValue(ptr); | |
| 3798 | _ = try func.load(operand, Type.u64, 8); | |
| 3799 | try func.addImm64(switch (int_info.signedness) { | |
| 3682 | try cg.emitWValue(ptr); | |
| 3683 | _ = try cg.load(operand, Type.u64, 8); | |
| 3684 | try cg.addImm64(switch (int_info.signedness) { | |
| 3800 | 3685 | .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits), |
| 3801 | 3686 | .signed => ~@as(u64, 0), |
| 3802 | 3687 | }); |
| 3803 | try func.addTag(.i64_xor); | |
| 3804 | try func.store(.stack, .stack, Type.u64, ptr.offset() + 8); | |
| 3688 | try cg.addTag(.i64_xor); | |
| 3689 | try cg.store(.stack, .stack, Type.u64, ptr.offset() + 8); | |
| 3805 | 3690 | |
| 3806 | 3691 | break :result ptr; |
| 3807 | 3692 | }, |
| ... | ... | @@ -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 { | |
| 3816 | try func.addTag(.@"unreachable"); | |
| 3817 | return func.finishAir(inst, .none, &.{}); | |
| 3700 | fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3701 | try cg.addTag(.@"unreachable"); | |
| 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 | 3706 | // unsupported by wasm itfunc. Can be implemented once we support DWARF |
| 3822 | 3707 | // for wasm |
| 3823 | try func.addTag(.@"unreachable"); | |
| 3824 | return func.finishAir(inst, .none, &.{}); | |
| 3708 | try cg.addTag(.@"unreachable"); | |
| 3709 | return cg.finishAir(inst, .none, &.{}); | |
| 3825 | 3710 | } |
| 3826 | 3711 | |
| 3827 | fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3828 | try func.addTag(.@"unreachable"); | |
| 3829 | return func.finishAir(inst, .none, &.{}); | |
| 3712 | fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3713 | try cg.addTag(.@"unreachable"); | |
| 3714 | return cg.finishAir(inst, .none, &.{}); | |
| 3830 | 3715 | } |
| 3831 | 3716 | |
| 3832 | fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3833 | const pt = func.pt; | |
| 3834 | const zcu = pt.zcu; | |
| 3835 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 3836 | const operand = try func.resolveInst(ty_op.operand); | |
| 3837 | const wanted_ty = func.typeOfIndex(inst); | |
| 3838 | const given_ty = func.typeOf(ty_op.operand); | |
| 3717 | fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3718 | const zcu = cg.pt.zcu; | |
| 3719 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 3720 | const operand = try cg.resolveInst(ty_op.operand); | |
| 3721 | const wanted_ty = cg.typeOfIndex(inst); | |
| 3722 | const given_ty = cg.typeOf(ty_op.operand); | |
| 3839 | 3723 | |
| 3840 | 3724 | const bit_size = given_ty.bitSize(zcu); |
| 3841 | 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 | 3727 | |
| 3844 | 3728 | const result = result: { |
| 3845 | 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.*)) { | |
| 3850 | const loaded_memory = try func.load(operand, wanted_ty, 0); | |
| 3733 | if (isByRef(given_ty, zcu, cg.target) and !isByRef(wanted_ty, zcu, cg.target)) { | |
| 3734 | const loaded_memory = try cg.load(operand, wanted_ty, 0); | |
| 3851 | 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 | 3737 | } else { |
| 3854 | 3738 | break :result loaded_memory; |
| 3855 | 3739 | } |
| 3856 | 3740 | } |
| 3857 | if (!isByRef(given_ty, pt, func.target.*) and isByRef(wanted_ty, pt, func.target.*)) { | |
| 3858 | const stack_memory = try func.allocStack(wanted_ty); | |
| 3859 | try func.store(stack_memory, operand, given_ty, 0); | |
| 3741 | if (!isByRef(given_ty, zcu, cg.target) and isByRef(wanted_ty, zcu, cg.target)) { | |
| 3742 | const stack_memory = try cg.allocStack(wanted_ty); | |
| 3743 | try cg.store(stack_memory, operand, given_ty, 0); | |
| 3860 | 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 | 3746 | } else { |
| 3863 | 3747 | break :result stack_memory; |
| 3864 | 3748 | } |
| 3865 | 3749 | } |
| 3866 | 3750 | |
| 3867 | 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 { | |
| 3877 | const pt = func.pt; | |
| 3878 | const zcu = pt.zcu; | |
| 3760 | fn bitcast(cg: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { | |
| 3761 | const zcu = cg.pt.zcu; | |
| 3879 | 3762 | // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction |
| 3880 | 3763 | if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand; |
| 3881 | 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 | 3767 | |
| 3885 | 3768 | const opcode = buildOpcode(.{ |
| 3886 | 3769 | .op = .reinterpret, |
| 3887 | .valtype1 = typeToValtype(wanted_ty, pt, func.target.*), | |
| 3888 | .valtype2 = typeToValtype(given_ty, pt, func.target.*), | |
| 3770 | .valtype1 = typeToValtype(wanted_ty, zcu, cg.target), | |
| 3771 | .valtype2 = typeToValtype(given_ty, zcu, cg.target), | |
| 3889 | 3772 | }); |
| 3890 | try func.emitWValue(operand); | |
| 3891 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 3773 | try cg.emitWValue(operand); | |
| 3774 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 3892 | 3775 | return .stack; |
| 3893 | 3776 | } |
| 3894 | 3777 | |
| 3895 | fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3896 | const pt = func.pt; | |
| 3897 | const zcu = pt.zcu; | |
| 3898 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3899 | const extra = func.air.extraData(Air.StructField, ty_pl.payload); | |
| 3778 | fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3779 | const zcu = cg.pt.zcu; | |
| 3780 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3781 | const extra = cg.air.extraData(Air.StructField, ty_pl.payload); | |
| 3900 | 3782 | |
| 3901 | const struct_ptr = try func.resolveInst(extra.data.struct_operand); | |
| 3902 | const struct_ptr_ty = func.typeOf(extra.data.struct_operand); | |
| 3783 | const struct_ptr = try cg.resolveInst(extra.data.struct_operand); | |
| 3784 | const struct_ptr_ty = cg.typeOf(extra.data.struct_operand); | |
| 3903 | 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); | |
| 3905 | return func.finishAir(inst, result, &.{extra.data.struct_operand}); | |
| 3786 | const result = try cg.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index); | |
| 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 { | |
| 3909 | const pt = func.pt; | |
| 3910 | const zcu = pt.zcu; | |
| 3911 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 3912 | const struct_ptr = try func.resolveInst(ty_op.operand); | |
| 3913 | const struct_ptr_ty = func.typeOf(ty_op.operand); | |
| 3790 | fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void { | |
| 3791 | const zcu = cg.pt.zcu; | |
| 3792 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 3793 | const struct_ptr = try cg.resolveInst(ty_op.operand); | |
| 3794 | const struct_ptr_ty = cg.typeOf(ty_op.operand); | |
| 3914 | 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); | |
| 3917 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 3797 | const result = try cg.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index); | |
| 3798 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 3918 | 3799 | } |
| 3919 | 3800 | |
| 3920 | 3801 | fn structFieldPtr( |
| 3921 | func: *CodeGen, | |
| 3802 | cg: *CodeGen, | |
| 3922 | 3803 | inst: Air.Inst.Index, |
| 3923 | 3804 | ref: Air.Inst.Ref, |
| 3924 | 3805 | struct_ptr: WValue, |
| ... | ... | @@ -3926,9 +3807,9 @@ fn structFieldPtr( |
| 3926 | 3807 | struct_ty: Type, |
| 3927 | 3808 | index: u32, |
| 3928 | 3809 | ) InnerError!WValue { |
| 3929 | const pt = func.pt; | |
| 3810 | const pt = cg.pt; | |
| 3930 | 3811 | const zcu = pt.zcu; |
| 3931 | const result_ty = func.typeOfIndex(inst); | |
| 3812 | const result_ty = cg.typeOfIndex(inst); | |
| 3932 | 3813 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); |
| 3933 | 3814 | |
| 3934 | 3815 | const offset = switch (struct_ty.containerLayout(zcu)) { |
| ... | ... | @@ -3947,28 +3828,28 @@ fn structFieldPtr( |
| 3947 | 3828 | }; |
| 3948 | 3829 | // save a load and store when we can simply reuse the operand |
| 3949 | 3830 | if (offset == 0) { |
| 3950 | return func.reuseOperand(ref, struct_ptr); | |
| 3831 | return cg.reuseOperand(ref, struct_ptr); | |
| 3951 | 3832 | } |
| 3952 | 3833 | switch (struct_ptr) { |
| 3953 | 3834 | .stack_offset => |stack_offset| { |
| 3954 | 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 { | |
| 3961 | const pt = func.pt; | |
| 3841 | fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3842 | const pt = cg.pt; | |
| 3962 | 3843 | const zcu = pt.zcu; |
| 3963 | 3844 | const ip = &zcu.intern_pool; |
| 3964 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 3965 | const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data; | |
| 3845 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 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); | |
| 3968 | const operand = try func.resolveInst(struct_field.struct_operand); | |
| 3848 | const struct_ty = cg.typeOf(struct_field.struct_operand); | |
| 3849 | const operand = try cg.resolveInst(struct_field.struct_operand); | |
| 3969 | 3850 | const field_index = struct_field.field_index; |
| 3970 | 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 | 3854 | const result: WValue = switch (struct_ty.containerLayout(zcu)) { |
| 3974 | 3855 | .@"packed" => switch (struct_ty.zigTypeTag(zcu)) { |
| ... | ... | @@ -3977,42 +3858,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3977 | 3858 | const offset = pt.structPackedFieldBitOffset(packed_struct, field_index); |
| 3978 | 3859 | const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); |
| 3979 | 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 | 3863 | const const_wvalue: WValue = if (wasm_bits == 32) |
| 3983 | 3864 | .{ .imm32 = offset } |
| 3984 | 3865 | else if (wasm_bits == 64) |
| 3985 | 3866 | .{ .imm64 = offset } |
| 3986 | 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 | 3870 | // for first field we don't require any shifting |
| 3990 | 3871 | const shifted_value = if (offset == 0) |
| 3991 | 3872 | operand |
| 3992 | 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 | 3876 | if (field_ty.zigTypeTag(zcu) == .float) { |
| 3996 | 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); | |
| 3998 | break :result try func.bitcast(field_ty, int_type, truncated); | |
| 3878 | const truncated = try cg.trunc(shifted_value, int_type, backing_ty); | |
| 3879 | break :result try cg.bitcast(field_ty, int_type, truncated); | |
| 3999 | 3880 | } else if (field_ty.isPtrAtRuntime(zcu) and packed_struct.field_types.len == 1) { |
| 4000 | 3881 | // In this case we do not have to perform any transformations, |
| 4001 | 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 | 3884 | } else if (field_ty.isPtrAtRuntime(zcu)) { |
| 4004 | 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 | 3890 | .@"union" => result: { |
| 4010 | if (isByRef(struct_ty, pt, func.target.*)) { | |
| 4011 | if (!isByRef(field_ty, pt, func.target.*)) { | |
| 4012 | break :result try func.load(operand, field_ty, 0); | |
| 3891 | if (isByRef(struct_ty, zcu, cg.target)) { | |
| 3892 | if (!isByRef(field_ty, zcu, cg.target)) { | |
| 3893 | break :result try cg.load(operand, field_ty, 0); | |
| 4013 | 3894 | } else { |
| 4014 | const new_stack_val = try func.allocStack(field_ty); | |
| 4015 | try func.store(new_stack_val, operand, field_ty, 0); | |
| 3895 | const new_stack_val = try cg.allocStack(field_ty); | |
| 3896 | try cg.store(new_stack_val, operand, field_ty, 0); | |
| 4016 | 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 | 3901 | const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(struct_ty.bitSize(zcu)))); |
| 4021 | 3902 | if (field_ty.zigTypeTag(zcu) == .float) { |
| 4022 | 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); | |
| 4024 | break :result try func.bitcast(field_ty, int_type, truncated); | |
| 3904 | const truncated = try cg.trunc(operand, int_type, union_int_type); | |
| 3905 | break :result try cg.bitcast(field_ty, int_type, truncated); | |
| 4025 | 3906 | } else if (field_ty.isPtrAtRuntime(zcu)) { |
| 4026 | 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 | 3912 | else => unreachable, |
| 4032 | 3913 | }, |
| 4033 | 3914 | else => result: { |
| 4034 | 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 | 3919 | switch (operand) { |
| 4039 | 3920 | .stack_offset => |stack_offset| { |
| 4040 | 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 { | |
| 4053 | const pt = func.pt; | |
| 3933 | fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 3934 | const pt = cg.pt; | |
| 4054 | 3935 | const zcu = pt.zcu; |
| 4055 | 3936 | // result type is always 'noreturn' |
| 4056 | const blocktype = wasm.block_empty; | |
| 4057 | const switch_br = func.air.unwrapSwitch(inst); | |
| 4058 | const target = try func.resolveInst(switch_br.operand); | |
| 4059 | const target_ty = func.typeOf(switch_br.operand); | |
| 4060 | const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1); | |
| 4061 | defer func.gpa.free(liveness.deaths); | |
| 3937 | const blocktype: std.wasm.BlockType = .empty; | |
| 3938 | const switch_br = cg.air.unwrapSwitch(inst); | |
| 3939 | const target = try cg.resolveInst(switch_br.operand); | |
| 3940 | const target_ty = cg.typeOf(switch_br.operand); | |
| 3941 | const liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1); | |
| 3942 | defer cg.gpa.free(liveness.deaths); | |
| 4062 | 3943 | |
| 4063 | 3944 | // a list that maps each value with its value and body based on the order inside the list. |
| 4064 | 3945 | const CaseValue = union(enum) { |
| ... | ... | @@ -4068,21 +3949,21 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4068 | 3949 | var case_list = try std.ArrayList(struct { |
| 4069 | 3950 | values: []const CaseValue, |
| 4070 | 3951 | body: []const Air.Inst.Index, |
| 4071 | }).initCapacity(func.gpa, switch_br.cases_len); | |
| 3952 | }).initCapacity(cg.gpa, switch_br.cases_len); | |
| 4072 | 3953 | defer for (case_list.items) |case| { |
| 4073 | func.gpa.free(case.values); | |
| 3954 | cg.gpa.free(case.values); | |
| 4074 | 3955 | } else case_list.deinit(); |
| 4075 | 3956 | |
| 4076 | 3957 | var lowest_maybe: ?i32 = null; |
| 4077 | 3958 | var highest_maybe: ?i32 = null; |
| 4078 | 3959 | var it = switch_br.iterateCases(); |
| 4079 | 3960 | while (it.next()) |case| { |
| 4080 | const values = try func.gpa.alloc(CaseValue, case.items.len + case.ranges.len); | |
| 4081 | errdefer func.gpa.free(values); | |
| 3961 | const values = try cg.gpa.alloc(CaseValue, case.items.len + case.ranges.len); | |
| 3962 | errdefer cg.gpa.free(values); | |
| 4082 | 3963 | |
| 4083 | 3964 | for (case.items, 0..) |ref, i| { |
| 4084 | const item_val = (try func.air.value(ref, pt)).?; | |
| 4085 | const int_val = func.valueAsI32(item_val); | |
| 3965 | const item_val = (try cg.air.value(ref, pt)).?; | |
| 3966 | const int_val = cg.valueAsI32(item_val); | |
| 4086 | 3967 | if (lowest_maybe == null or int_val < lowest_maybe.?) { |
| 4087 | 3968 | lowest_maybe = int_val; |
| 4088 | 3969 | } |
| ... | ... | @@ -4093,15 +3974,15 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4093 | 3974 | } |
| 4094 | 3975 | |
| 4095 | 3976 | for (case.ranges, 0..) |range, i| { |
| 4096 | const min_val = (try func.air.value(range[0], pt)).?; | |
| 4097 | const int_min_val = func.valueAsI32(min_val); | |
| 3977 | const min_val = (try cg.air.value(range[0], pt)).?; | |
| 3978 | const int_min_val = cg.valueAsI32(min_val); | |
| 4098 | 3979 | |
| 4099 | 3980 | if (lowest_maybe == null or int_min_val < lowest_maybe.?) { |
| 4100 | 3981 | lowest_maybe = int_min_val; |
| 4101 | 3982 | } |
| 4102 | 3983 | |
| 4103 | const max_val = (try func.air.value(range[1], pt)).?; | |
| 4104 | const int_max_val = func.valueAsI32(max_val); | |
| 3984 | const max_val = (try cg.air.value(range[1], pt)).?; | |
| 3985 | const int_max_val = cg.valueAsI32(max_val); | |
| 4105 | 3986 | |
| 4106 | 3987 | if (highest_maybe == null or int_max_val > highest_maybe.?) { |
| 4107 | 3988 | highest_maybe = int_max_val; |
| ... | ... | @@ -4116,7 +3997,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4116 | 3997 | } |
| 4117 | 3998 | |
| 4118 | 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 | 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 | 4013 | const else_body = it.elseBody(); |
| 4133 | 4014 | const has_else_body = else_body.len != 0; |
| 4134 | 4015 | if (has_else_body) { |
| 4135 | try func.startBlock(.block, blocktype); | |
| 4016 | try cg.startBlock(.block, blocktype); | |
| 4136 | 4017 | } |
| 4137 | 4018 | |
| 4138 | 4019 | if (!is_sparse) { |
| ... | ... | @@ -4140,25 +4021,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4140 | 4021 | // The value 'target' represents the index into the table. |
| 4141 | 4022 | // Each index in the table represents a label to the branch |
| 4142 | 4023 | // to jump to. |
| 4143 | try func.startBlock(.block, blocktype); | |
| 4144 | try func.emitWValue(target); | |
| 4024 | try cg.startBlock(.block, blocktype); | |
| 4025 | try cg.emitWValue(target); | |
| 4145 | 4026 | if (lowest < 0) { |
| 4146 | 4027 | // since br_table works using indexes, starting from '0', we must ensure all values |
| 4147 | 4028 | // we put inside, are atleast 0. |
| 4148 | try func.addImm32(@bitCast(lowest * -1)); | |
| 4149 | try func.addTag(.i32_add); | |
| 4029 | try cg.addImm32(@bitCast(lowest * -1)); | |
| 4030 | try cg.addTag(.i32_add); | |
| 4150 | 4031 | } else if (lowest > 0) { |
| 4151 | 4032 | // make the index start from 0 by substracting the lowest value |
| 4152 | try func.addImm32(@bitCast(lowest)); | |
| 4153 | try func.addTag(.i32_sub); | |
| 4033 | try cg.addImm32(@bitCast(lowest)); | |
| 4034 | try cg.addTag(.i32_sub); | |
| 4154 | 4035 | } |
| 4155 | 4036 | |
| 4156 | 4037 | // Account for default branch so always add '1' |
| 4157 | 4038 | const depth = @as(u32, @intCast(highest - lowest + @intFromBool(has_else_body))) + 1; |
| 4158 | 4039 | const jump_table: Mir.JumpTable = .{ .length = depth }; |
| 4159 | const table_extra_index = try func.addExtra(jump_table); | |
| 4160 | try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | |
| 4161 | try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); | |
| 4040 | const table_extra_index = try cg.addExtra(jump_table); | |
| 4041 | try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | |
| 4042 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth); | |
| 4162 | 4043 | var value = lowest; |
| 4163 | 4044 | while (value <= highest) : (value += 1) { |
| 4164 | 4045 | // idx represents the branch we jump to |
| ... | ... | @@ -4179,78 +4060,77 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4179 | 4060 | // by using a jump table for this instead of if-else chains. |
| 4180 | 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 | 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 | 4071 | for (case_list.items, 0..) |case, index| { |
| 4191 | 4072 | // when sparse, we use if/else-chain, so emit conditional checks |
| 4192 | 4073 | if (is_sparse) { |
| 4193 | 4074 | // for single value prong we can emit a simple condition |
| 4194 | 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 | 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); | |
| 4198 | try func.addLabel(.br_if, 0); | |
| 4078 | _ = try cg.cmp(target, val, target_ty, .neq); | |
| 4079 | try cg.addLabel(.br_if, 0); | |
| 4199 | 4080 | } else { |
| 4200 | 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 | 4083 | for (case.values) |value| { |
| 4203 | 4084 | switch (value) { |
| 4204 | 4085 | .singular => |single_val| { |
| 4205 | const val = try func.lowerConstant(single_val.value, target_ty); | |
| 4206 | _ = try func.cmp(target, val, target_ty, .eq); | |
| 4086 | const val = try cg.lowerConstant(single_val.value, target_ty); | |
| 4087 | _ = try cg.cmp(target, val, target_ty, .eq); | |
| 4207 | 4088 | }, |
| 4208 | 4089 | .range => |range| { |
| 4209 | const min_val = try func.lowerConstant(range.min_value, target_ty); | |
| 4210 | const max_val = try func.lowerConstant(range.max_value, target_ty); | |
| 4090 | const min_val = try cg.lowerConstant(range.min_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); | |
| 4213 | const lte = try func.cmp(target, max_val, target_ty, .lte); | |
| 4214 | _ = try func.binOp(gte, lte, Type.bool, .@"and"); | |
| 4093 | const gte = try cg.cmp(target, min_val, target_ty, .gte); | |
| 4094 | const lte = try cg.cmp(target, max_val, target_ty, .lte); | |
| 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 | 4100 | // value did not match any of the prong values |
| 4220 | try func.addLabel(.br, 1); | |
| 4221 | try func.endBlock(); | |
| 4101 | try cg.addLabel(.br, 1); | |
| 4102 | try cg.endBlock(); | |
| 4222 | 4103 | } |
| 4223 | 4104 | } |
| 4224 | func.branches.appendAssumeCapacity(.{}); | |
| 4225 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len); | |
| 4105 | cg.branches.appendAssumeCapacity(.{}); | |
| 4106 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.deaths[index].len); | |
| 4226 | 4107 | defer { |
| 4227 | var case_branch = func.branches.pop(); | |
| 4228 | case_branch.deinit(func.gpa); | |
| 4108 | var case_branch = cg.branches.pop(); | |
| 4109 | case_branch.deinit(cg.gpa); | |
| 4229 | 4110 | } |
| 4230 | try func.genBody(case.body); | |
| 4231 | try func.endBlock(); | |
| 4111 | try cg.genBody(case.body); | |
| 4112 | try cg.endBlock(); | |
| 4232 | 4113 | } |
| 4233 | 4114 | |
| 4234 | 4115 | if (has_else_body) { |
| 4235 | func.branches.appendAssumeCapacity(.{}); | |
| 4116 | cg.branches.appendAssumeCapacity(.{}); | |
| 4236 | 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 | 4119 | defer { |
| 4239 | var else_branch = func.branches.pop(); | |
| 4240 | else_branch.deinit(func.gpa); | |
| 4120 | var else_branch = cg.branches.pop(); | |
| 4121 | else_branch.deinit(cg.gpa); | |
| 4241 | 4122 | } |
| 4242 | try func.genBody(else_body); | |
| 4243 | try func.endBlock(); | |
| 4123 | try cg.genBody(else_body); | |
| 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 { | |
| 4249 | const pt = func.pt; | |
| 4250 | const zcu = pt.zcu; | |
| 4251 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4252 | const operand = try func.resolveInst(un_op); | |
| 4253 | const err_union_ty = func.typeOf(un_op); | |
| 4129 | fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void { | |
| 4130 | const zcu = cg.pt.zcu; | |
| 4131 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4132 | const operand = try cg.resolveInst(un_op); | |
| 4133 | const err_union_ty = cg.typeOf(un_op); | |
| 4254 | 4134 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| 4255 | 4135 | |
| 4256 | 4136 | const result: WValue = result: { |
| ... | ... | @@ -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 | 4146 | if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4267 | try func.addMemArg(.i32_load16_u, .{ | |
| 4147 | try cg.addMemArg(.i32_load16_u, .{ | |
| 4268 | 4148 | .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), |
| 4269 | 4149 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), |
| 4270 | 4150 | }); |
| 4271 | 4151 | } |
| 4272 | 4152 | |
| 4273 | 4153 | // Compare the error value with '0' |
| 4274 | try func.addImm32(0); | |
| 4275 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 4154 | try cg.addImm32(0); | |
| 4155 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 4276 | 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 { | |
| 4282 | const pt = func.pt; | |
| 4283 | const zcu = pt.zcu; | |
| 4284 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4161 | fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { | |
| 4162 | const zcu = cg.pt.zcu; | |
| 4163 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4285 | 4164 | |
| 4286 | const operand = try func.resolveInst(ty_op.operand); | |
| 4287 | const op_ty = func.typeOf(ty_op.operand); | |
| 4165 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4166 | const op_ty = cg.typeOf(ty_op.operand); | |
| 4288 | 4167 | const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty; |
| 4289 | 4168 | const payload_ty = err_ty.errorUnionPayload(zcu); |
| 4290 | 4169 | |
| 4291 | 4170 | const result: WValue = result: { |
| 4292 | 4171 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4293 | 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 | 4175 | break :result .none; |
| 4297 | 4176 | } |
| 4298 | 4177 | |
| 4299 | 4178 | const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))); |
| 4300 | if (op_is_ptr or isByRef(payload_ty, pt, func.target.*)) { | |
| 4301 | break :result try func.buildPointerOffset(operand, pl_offset, .new); | |
| 4179 | if (op_is_ptr or isByRef(payload_ty, zcu, cg.target)) { | |
| 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 { | |
| 4310 | const pt = func.pt; | |
| 4311 | const zcu = pt.zcu; | |
| 4312 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4188 | fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { | |
| 4189 | const zcu = cg.pt.zcu; | |
| 4190 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4313 | 4191 | |
| 4314 | const operand = try func.resolveInst(ty_op.operand); | |
| 4315 | const op_ty = func.typeOf(ty_op.operand); | |
| 4192 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4193 | const op_ty = cg.typeOf(ty_op.operand); | |
| 4316 | 4194 | const err_ty = if (op_is_ptr) op_ty.childType(zcu) else op_ty; |
| 4317 | 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 | 4200 | } |
| 4323 | 4201 | |
| 4324 | 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 { | |
| 4334 | const zcu = func.pt.zcu; | |
| 4335 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4211 | fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4212 | const zcu = cg.pt.zcu; | |
| 4213 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4336 | 4214 | |
| 4337 | const operand = try func.resolveInst(ty_op.operand); | |
| 4338 | const err_ty = func.typeOfIndex(inst); | |
| 4215 | const operand = try cg.resolveInst(ty_op.operand); | |
| 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 | 4219 | const result = result: { |
| 4342 | 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); | |
| 4347 | const payload_ptr = try func.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new); | |
| 4348 | try func.store(payload_ptr, operand, pl_ty, 0); | |
| 4224 | const err_union = try cg.allocStack(err_ty); | |
| 4225 | const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new); | |
| 4226 | try cg.store(payload_ptr, operand, pl_ty, 0); | |
| 4349 | 4227 | |
| 4350 | 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); | |
| 4352 | try func.addImm32(0); | |
| 4229 | try cg.emitWValue(err_union); | |
| 4230 | try cg.addImm32(0); | |
| 4353 | 4231 | const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); |
| 4354 | try func.addMemArg(.i32_store16, .{ | |
| 4232 | try cg.addMemArg(.i32_store16, .{ | |
| 4355 | 4233 | .offset = err_union.offset() + err_val_offset, |
| 4356 | 4234 | .alignment = 2, |
| 4357 | 4235 | }); |
| 4358 | 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 { | |
| 4364 | const pt = func.pt; | |
| 4365 | const zcu = pt.zcu; | |
| 4366 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4241 | fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4242 | const zcu = cg.pt.zcu; | |
| 4243 | const ty_op = cg.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 | 4246 | const err_ty = ty_op.ty.toType(); |
| 4370 | 4247 | const pl_ty = err_ty.errorUnionPayload(zcu); |
| 4371 | 4248 | |
| 4372 | 4249 | const result = result: { |
| 4373 | 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 | 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 | 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 | 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 | 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 { | |
| 4392 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4268 | fn airIntcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4269 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4393 | 4270 | |
| 4394 | 4271 | const ty = ty_op.ty.toType(); |
| 4395 | const operand = try func.resolveInst(ty_op.operand); | |
| 4396 | const operand_ty = func.typeOf(ty_op.operand); | |
| 4397 | const pt = func.pt; | |
| 4398 | const zcu = pt.zcu; | |
| 4272 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4273 | const operand_ty = cg.typeOf(ty_op.operand); | |
| 4274 | const zcu = cg.pt.zcu; | |
| 4399 | 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 | 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 | 4282 | const op_bits = toWasmBits(@intCast(operand_ty.bitSize(zcu))).?; |
| 4407 | 4283 | const wanted_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; |
| 4408 | 4284 | const result = if (op_bits == wanted_bits) |
| 4409 | func.reuseOperand(ty_op.operand, operand) | |
| 4285 | cg.reuseOperand(ty_op.operand, operand) | |
| 4410 | 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 | 4292 | /// Upcasts or downcasts an integer based on the given and wanted types, |
| 4417 | 4293 | /// and stores the result in a new operand. |
| 4418 | 4294 | /// Asserts type's bitsize <= 128 |
| 4419 | 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 { | |
| 4421 | const pt = func.pt; | |
| 4422 | const zcu = pt.zcu; | |
| 4296 | fn intcast(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | |
| 4297 | const zcu = cg.pt.zcu; | |
| 4423 | 4298 | const given_bitsize = @as(u16, @intCast(given.bitSize(zcu))); |
| 4424 | 4299 | const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(zcu))); |
| 4425 | 4300 | assert(given_bitsize <= 128); |
| ... | ... | @@ -4432,470 +4307,456 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4432 | 4307 | } |
| 4433 | 4308 | |
| 4434 | 4309 | if (op_bits == 64 and wanted_bits == 32) { |
| 4435 | try func.emitWValue(operand); | |
| 4436 | try func.addTag(.i32_wrap_i64); | |
| 4310 | try cg.emitWValue(operand); | |
| 4311 | try cg.addTag(.i32_wrap_i64); | |
| 4437 | 4312 | return .stack; |
| 4438 | 4313 | } else if (op_bits == 32 and wanted_bits == 64) { |
| 4439 | try func.emitWValue(operand); | |
| 4440 | try func.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u); | |
| 4314 | try cg.emitWValue(operand); | |
| 4315 | try cg.addTag(if (wanted.isSignedInt(zcu)) .i64_extend_i32_s else .i64_extend_i32_u); | |
| 4441 | 4316 | return .stack; |
| 4442 | 4317 | } else if (wanted_bits == 128) { |
| 4443 | 4318 | // for 128bit integers we store the integer in the virtual stack, rather than a local |
| 4444 | const stack_ptr = try func.allocStack(wanted); | |
| 4445 | try func.emitWValue(stack_ptr); | |
| 4319 | const stack_ptr = try cg.allocStack(wanted); | |
| 4320 | try cg.emitWValue(stack_ptr); | |
| 4446 | 4321 | |
| 4447 | 4322 | // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it |
| 4448 | 4323 | // meaning less store operations are required. |
| 4449 | 4324 | const lhs = if (op_bits == 32) blk: { |
| 4450 | 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 | 4327 | } else operand; |
| 4453 | 4328 | |
| 4454 | 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 | 4332 | // For signed integers we shift lsb by 63 (64bit integer - 1 sign bit) and store remaining value |
| 4458 | 4333 | if (wanted.isSignedInt(zcu)) { |
| 4459 | try func.emitWValue(stack_ptr); | |
| 4460 | const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); | |
| 4461 | try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); | |
| 4334 | try cg.emitWValue(stack_ptr); | |
| 4335 | const shr = try cg.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); | |
| 4336 | try cg.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); | |
| 4462 | 4337 | } else { |
| 4463 | 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 | 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 { | |
| 4471 | const pt = func.pt; | |
| 4472 | const zcu = pt.zcu; | |
| 4473 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4474 | const operand = try func.resolveInst(un_op); | |
| 4345 | fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { | |
| 4346 | const zcu = cg.pt.zcu; | |
| 4347 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4348 | const operand = try cg.resolveInst(un_op); | |
| 4475 | 4349 | |
| 4476 | const op_ty = func.typeOf(un_op); | |
| 4350 | const op_ty = cg.typeOf(un_op); | |
| 4477 | 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); | |
| 4479 | return func.finishAir(inst, result, &.{un_op}); | |
| 4352 | const result = try cg.isNull(operand, optional_ty, opcode); | |
| 4353 | return cg.finishAir(inst, result, &.{un_op}); | |
| 4480 | 4354 | } |
| 4481 | 4355 | |
| 4482 | 4356 | /// For a given type and operand, checks if it's considered `null`. |
| 4483 | 4357 | /// NOTE: Leaves the result on the stack |
| 4484 | fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { | |
| 4485 | const pt = func.pt; | |
| 4358 | fn isNull(cg: *CodeGen, operand: WValue, optional_ty: Type, opcode: std.wasm.Opcode) InnerError!WValue { | |
| 4359 | const pt = cg.pt; | |
| 4486 | 4360 | const zcu = pt.zcu; |
| 4487 | try func.emitWValue(operand); | |
| 4361 | try cg.emitWValue(operand); | |
| 4488 | 4362 | const payload_ty = optional_ty.optionalChild(zcu); |
| 4489 | 4363 | if (!optional_ty.optionalReprIsPayload(zcu)) { |
| 4490 | 4364 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 4491 | 4365 | // a pointer to the stack value |
| 4492 | 4366 | if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4493 | 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 | 4372 | } else if (payload_ty.isSlice(zcu)) { |
| 4499 | switch (func.arch()) { | |
| 4500 | .wasm32 => try func.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }), | |
| 4501 | .wasm64 => try func.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }), | |
| 4502 | else => unreachable, | |
| 4373 | switch (cg.ptr_size) { | |
| 4374 | .wasm32 => try cg.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }), | |
| 4375 | .wasm64 => try cg.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }), | |
| 4503 | 4376 | } |
| 4504 | 4377 | } |
| 4505 | 4378 | |
| 4506 | 4379 | // Compare the null value with '0' |
| 4507 | try func.addImm32(0); | |
| 4508 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 4380 | try cg.addImm32(0); | |
| 4381 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 4509 | 4382 | |
| 4510 | 4383 | return .stack; |
| 4511 | 4384 | } |
| 4512 | 4385 | |
| 4513 | fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4514 | const pt = func.pt; | |
| 4515 | const zcu = pt.zcu; | |
| 4516 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4517 | const opt_ty = func.typeOf(ty_op.operand); | |
| 4518 | const payload_ty = func.typeOfIndex(inst); | |
| 4386 | fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4387 | const zcu = cg.pt.zcu; | |
| 4388 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4389 | const opt_ty = cg.typeOf(ty_op.operand); | |
| 4390 | const payload_ty = cg.typeOfIndex(inst); | |
| 4519 | 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 | 4395 | const result = result: { |
| 4524 | const operand = try func.resolveInst(ty_op.operand); | |
| 4525 | if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand); | |
| 4396 | const operand = try cg.resolveInst(ty_op.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.*)) { | |
| 4528 | break :result try func.buildPointerOffset(operand, 0, .new); | |
| 4399 | if (isByRef(payload_ty, zcu, cg.target)) { | |
| 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 { | |
| 4537 | const pt = func.pt; | |
| 4538 | const zcu = pt.zcu; | |
| 4539 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4540 | const operand = try func.resolveInst(ty_op.operand); | |
| 4541 | const opt_ty = func.typeOf(ty_op.operand).childType(zcu); | |
| 4408 | fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4409 | const zcu = cg.pt.zcu; | |
| 4410 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4411 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4412 | const opt_ty = cg.typeOf(ty_op.operand).childType(zcu); | |
| 4542 | 4413 | |
| 4543 | 4414 | const result = result: { |
| 4544 | 4415 | const payload_ty = opt_ty.optionalChild(zcu); |
| 4545 | 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 { | |
| 4555 | const pt = func.pt; | |
| 4425 | fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4426 | const pt = cg.pt; | |
| 4556 | 4427 | const zcu = pt.zcu; |
| 4557 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4558 | const operand = try func.resolveInst(ty_op.operand); | |
| 4559 | const opt_ty = func.typeOf(ty_op.operand).childType(zcu); | |
| 4428 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4429 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4430 | const opt_ty = cg.typeOf(ty_op.operand).childType(zcu); | |
| 4560 | 4431 | const payload_ty = opt_ty.optionalChild(zcu); |
| 4561 | 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 | 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 | 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); | |
| 4574 | try func.addImm32(1); | |
| 4575 | try func.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 }); | |
| 4444 | try cg.emitWValue(operand); | |
| 4445 | try cg.addImm32(1); | |
| 4446 | try cg.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 }); | |
| 4576 | 4447 | |
| 4577 | const result = try func.buildPointerOffset(operand, 0, .new); | |
| 4578 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 4448 | const result = try cg.buildPointerOffset(operand, 0, .new); | |
| 4449 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 4579 | 4450 | } |
| 4580 | 4451 | |
| 4581 | fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4582 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4583 | const payload_ty = func.typeOf(ty_op.operand); | |
| 4584 | const pt = func.pt; | |
| 4452 | fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4453 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4454 | const payload_ty = cg.typeOf(ty_op.operand); | |
| 4455 | const pt = cg.pt; | |
| 4585 | 4456 | const zcu = pt.zcu; |
| 4586 | 4457 | |
| 4587 | 4458 | const result = result: { |
| 4588 | 4459 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4589 | const non_null_bit = try func.allocStack(Type.u1); | |
| 4590 | try func.emitWValue(non_null_bit); | |
| 4591 | try func.addImm32(1); | |
| 4592 | try func.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 }); | |
| 4460 | const non_null_bit = try cg.allocStack(Type.u1); | |
| 4461 | try cg.emitWValue(non_null_bit); | |
| 4462 | try cg.addImm32(1); | |
| 4463 | try cg.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 }); | |
| 4593 | 4464 | break :result non_null_bit; |
| 4594 | 4465 | } |
| 4595 | 4466 | |
| 4596 | const operand = try func.resolveInst(ty_op.operand); | |
| 4597 | const op_ty = func.typeOfIndex(inst); | |
| 4467 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4468 | const op_ty = cg.typeOfIndex(inst); | |
| 4598 | 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 | 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 | 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); | |
| 4607 | try func.emitWValue(result_ptr); | |
| 4608 | try func.addImm32(1); | |
| 4609 | try func.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 }); | |
| 4477 | const result_ptr = try cg.allocStack(op_ty); | |
| 4478 | try cg.emitWValue(result_ptr); | |
| 4479 | try cg.addImm32(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); | |
| 4612 | try func.store(payload_ptr, operand, payload_ty, 0); | |
| 4482 | const payload_ptr = try cg.buildPointerOffset(result_ptr, 0, .new); | |
| 4483 | try cg.store(payload_ptr, operand, payload_ty, 0); | |
| 4613 | 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 { | |
| 4620 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4621 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4490 | fn airSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4491 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4492 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4622 | 4493 | |
| 4623 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 4624 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 4625 | const slice_ty = func.typeOfIndex(inst); | |
| 4494 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 4495 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 4496 | const slice_ty = cg.typeOfIndex(inst); | |
| 4626 | 4497 | |
| 4627 | const slice = try func.allocStack(slice_ty); | |
| 4628 | try func.store(slice, lhs, Type.usize, 0); | |
| 4629 | try func.store(slice, rhs, Type.usize, func.ptrSize()); | |
| 4498 | const slice = try cg.allocStack(slice_ty); | |
| 4499 | try cg.store(slice, lhs, Type.usize, 0); | |
| 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 { | |
| 4635 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4505 | fn airSliceLen(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4506 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4636 | 4507 | |
| 4637 | const operand = try func.resolveInst(ty_op.operand); | |
| 4638 | return func.finishAir(inst, try func.sliceLen(operand), &.{ty_op.operand}); | |
| 4508 | const operand = try cg.resolveInst(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 { | |
| 4642 | const pt = func.pt; | |
| 4643 | const zcu = pt.zcu; | |
| 4644 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 4512 | fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4513 | const zcu = cg.pt.zcu; | |
| 4514 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 4645 | 4515 | |
| 4646 | const slice_ty = func.typeOf(bin_op.lhs); | |
| 4647 | const slice = try func.resolveInst(bin_op.lhs); | |
| 4648 | const index = try func.resolveInst(bin_op.rhs); | |
| 4516 | const slice_ty = cg.typeOf(bin_op.lhs); | |
| 4517 | const slice = try cg.resolveInst(bin_op.lhs); | |
| 4518 | const index = try cg.resolveInst(bin_op.rhs); | |
| 4649 | 4519 | const elem_ty = slice_ty.childType(zcu); |
| 4650 | 4520 | const elem_size = elem_ty.abiSize(zcu); |
| 4651 | 4521 | |
| 4652 | 4522 | // load pointer onto stack |
| 4653 | _ = try func.load(slice, Type.usize, 0); | |
| 4523 | _ = try cg.load(slice, Type.usize, 0); | |
| 4654 | 4524 | |
| 4655 | 4525 | // calculate index into slice |
| 4656 | try func.emitWValue(index); | |
| 4657 | try func.addImm32(@intCast(elem_size)); | |
| 4658 | try func.addTag(.i32_mul); | |
| 4659 | try func.addTag(.i32_add); | |
| 4526 | try cg.emitWValue(index); | |
| 4527 | try cg.addImm32(@intCast(elem_size)); | |
| 4528 | try cg.addTag(.i32_mul); | |
| 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 | 4532 | .stack |
| 4663 | 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 { | |
| 4670 | const pt = func.pt; | |
| 4671 | const zcu = pt.zcu; | |
| 4672 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4673 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4539 | fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4540 | const zcu = cg.pt.zcu; | |
| 4541 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4542 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4674 | 4543 | |
| 4675 | 4544 | const elem_ty = ty_pl.ty.toType().childType(zcu); |
| 4676 | 4545 | const elem_size = elem_ty.abiSize(zcu); |
| 4677 | 4546 | |
| 4678 | const slice = try func.resolveInst(bin_op.lhs); | |
| 4679 | const index = try func.resolveInst(bin_op.rhs); | |
| 4547 | const slice = try cg.resolveInst(bin_op.lhs); | |
| 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 | 4552 | // calculate index into slice |
| 4684 | try func.emitWValue(index); | |
| 4685 | try func.addImm32(@intCast(elem_size)); | |
| 4686 | try func.addTag(.i32_mul); | |
| 4687 | try func.addTag(.i32_add); | |
| 4553 | try cg.emitWValue(index); | |
| 4554 | try cg.addImm32(@intCast(elem_size)); | |
| 4555 | try cg.addTag(.i32_mul); | |
| 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 { | |
| 4693 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4694 | const operand = try func.resolveInst(ty_op.operand); | |
| 4695 | return func.finishAir(inst, try func.slicePtr(operand), &.{ty_op.operand}); | |
| 4561 | fn airSlicePtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4562 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4563 | const operand = try cg.resolveInst(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 { | |
| 4699 | const ptr = try func.load(operand, Type.usize, 0); | |
| 4700 | return ptr.toLocal(func, Type.usize); | |
| 4567 | fn slicePtr(cg: *CodeGen, operand: WValue) InnerError!WValue { | |
| 4568 | const ptr = try cg.load(operand, Type.usize, 0); | |
| 4569 | return ptr.toLocal(cg, Type.usize); | |
| 4701 | 4570 | } |
| 4702 | 4571 | |
| 4703 | fn sliceLen(func: *CodeGen, operand: WValue) InnerError!WValue { | |
| 4704 | const len = try func.load(operand, Type.usize, func.ptrSize()); | |
| 4705 | return len.toLocal(func, Type.usize); | |
| 4572 | fn sliceLen(cg: *CodeGen, operand: WValue) InnerError!WValue { | |
| 4573 | const len = try cg.load(operand, Type.usize, cg.ptrSize()); | |
| 4574 | return len.toLocal(cg, Type.usize); | |
| 4706 | 4575 | } |
| 4707 | 4576 | |
| 4708 | fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4709 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4577 | fn airTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 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 | 4581 | const wanted_ty: Type = ty_op.ty.toType(); |
| 4713 | const op_ty = func.typeOf(ty_op.operand); | |
| 4714 | const pt = func.pt; | |
| 4715 | const zcu = pt.zcu; | |
| 4582 | const op_ty = cg.typeOf(ty_op.operand); | |
| 4583 | const zcu = cg.pt.zcu; | |
| 4716 | 4584 | |
| 4717 | 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 | 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 | 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 | 4597 | /// Truncates a given operand to a given type, discarding any overflown bits. |
| 4730 | 4598 | /// NOTE: Resulting value is left on the stack. |
| 4731 | fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue { | |
| 4732 | const pt = func.pt; | |
| 4733 | const zcu = pt.zcu; | |
| 4599 | fn trunc(cg: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue { | |
| 4600 | const zcu = cg.pt.zcu; | |
| 4734 | 4601 | const given_bits = @as(u16, @intCast(given_ty.bitSize(zcu))); |
| 4735 | 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 | 4607 | const wanted_bits = @as(u16, @intCast(wanted_ty.bitSize(zcu))); |
| 4741 | 4608 | const wasm_bits = toWasmBits(wanted_bits).?; |
| 4742 | 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 | 4612 | return result; |
| 4746 | 4613 | } |
| 4747 | 4614 | |
| 4748 | fn airIntFromBool(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4749 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4750 | const operand = try func.resolveInst(un_op); | |
| 4751 | const result = func.reuseOperand(un_op, operand); | |
| 4615 | fn airIntFromBool(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4616 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4617 | const operand = try cg.resolveInst(un_op); | |
| 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 { | |
| 4757 | const pt = func.pt; | |
| 4758 | const zcu = pt.zcu; | |
| 4759 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4623 | fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4624 | const zcu = cg.pt.zcu; | |
| 4625 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4760 | 4626 | |
| 4761 | const operand = try func.resolveInst(ty_op.operand); | |
| 4762 | const array_ty = func.typeOf(ty_op.operand).childType(zcu); | |
| 4627 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4628 | const array_ty = cg.typeOf(ty_op.operand).childType(zcu); | |
| 4763 | 4629 | const slice_ty = ty_op.ty.toType(); |
| 4764 | 4630 | |
| 4765 | 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 | 4634 | // store the array ptr in the slice |
| 4769 | 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 | 4639 | // store the length of the array in the slice |
| 4774 | 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 { | |
| 4781 | const pt = func.pt; | |
| 4782 | const zcu = pt.zcu; | |
| 4783 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4784 | const operand = try func.resolveInst(un_op); | |
| 4785 | const ptr_ty = func.typeOf(un_op); | |
| 4646 | fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4647 | const zcu = cg.pt.zcu; | |
| 4648 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 4649 | const operand = try cg.resolveInst(un_op); | |
| 4650 | const ptr_ty = cg.typeOf(un_op); | |
| 4786 | 4651 | const result = if (ptr_ty.isSlice(zcu)) |
| 4787 | try func.slicePtr(operand) | |
| 4652 | try cg.slicePtr(operand) | |
| 4788 | 4653 | else switch (operand) { |
| 4789 | 4654 | // for stack offset, return a pointer to this offset. |
| 4790 | .stack_offset => try func.buildPointerOffset(operand, 0, .new), | |
| 4791 | else => func.reuseOperand(un_op, operand), | |
| 4655 | .stack_offset => try cg.buildPointerOffset(operand, 0, .new), | |
| 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 { | |
| 4797 | const pt = func.pt; | |
| 4798 | const zcu = pt.zcu; | |
| 4799 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 4661 | fn airPtrElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4662 | const zcu = cg.pt.zcu; | |
| 4663 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 4800 | 4664 | |
| 4801 | const ptr_ty = func.typeOf(bin_op.lhs); | |
| 4802 | const ptr = try func.resolveInst(bin_op.lhs); | |
| 4803 | const index = try func.resolveInst(bin_op.rhs); | |
| 4665 | const ptr_ty = cg.typeOf(bin_op.lhs); | |
| 4666 | const ptr = try cg.resolveInst(bin_op.lhs); | |
| 4667 | const index = try cg.resolveInst(bin_op.rhs); | |
| 4804 | 4668 | const elem_ty = ptr_ty.childType(zcu); |
| 4805 | 4669 | const elem_size = elem_ty.abiSize(zcu); |
| 4806 | 4670 | |
| 4807 | 4671 | // load pointer onto the stack |
| 4808 | 4672 | if (ptr_ty.isSlice(zcu)) { |
| 4809 | _ = try func.load(ptr, Type.usize, 0); | |
| 4673 | _ = try cg.load(ptr, Type.usize, 0); | |
| 4810 | 4674 | } else { |
| 4811 | try func.lowerToStack(ptr); | |
| 4675 | try cg.lowerToStack(ptr); | |
| 4812 | 4676 | } |
| 4813 | 4677 | |
| 4814 | 4678 | // calculate index into slice |
| 4815 | try func.emitWValue(index); | |
| 4816 | try func.addImm32(@intCast(elem_size)); | |
| 4817 | try func.addTag(.i32_mul); | |
| 4818 | try func.addTag(.i32_add); | |
| 4679 | try cg.emitWValue(index); | |
| 4680 | try cg.addImm32(@intCast(elem_size)); | |
| 4681 | try cg.addTag(.i32_mul); | |
| 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 | 4685 | .stack |
| 4822 | 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 { | |
| 4829 | const pt = func.pt; | |
| 4830 | const zcu = pt.zcu; | |
| 4831 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4832 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4692 | fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4693 | const zcu = cg.pt.zcu; | |
| 4694 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4695 | const bin_op = cg.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 | 4698 | const elem_ty = ty_pl.ty.toType().childType(zcu); |
| 4836 | 4699 | const elem_size = elem_ty.abiSize(zcu); |
| 4837 | 4700 | |
| 4838 | const ptr = try func.resolveInst(bin_op.lhs); | |
| 4839 | const index = try func.resolveInst(bin_op.rhs); | |
| 4701 | const ptr = try cg.resolveInst(bin_op.lhs); | |
| 4702 | const index = try cg.resolveInst(bin_op.rhs); | |
| 4840 | 4703 | |
| 4841 | 4704 | // load pointer onto the stack |
| 4842 | 4705 | if (ptr_ty.isSlice(zcu)) { |
| 4843 | _ = try func.load(ptr, Type.usize, 0); | |
| 4706 | _ = try cg.load(ptr, Type.usize, 0); | |
| 4844 | 4707 | } else { |
| 4845 | try func.lowerToStack(ptr); | |
| 4708 | try cg.lowerToStack(ptr); | |
| 4846 | 4709 | } |
| 4847 | 4710 | |
| 4848 | 4711 | // calculate index into ptr |
| 4849 | try func.emitWValue(index); | |
| 4850 | try func.addImm32(@intCast(elem_size)); | |
| 4851 | try func.addTag(.i32_mul); | |
| 4852 | try func.addTag(.i32_add); | |
| 4712 | try cg.emitWValue(index); | |
| 4713 | try cg.addImm32(@intCast(elem_size)); | |
| 4714 | try cg.addTag(.i32_mul); | |
| 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 { | |
| 4858 | const pt = func.pt; | |
| 4859 | const zcu = pt.zcu; | |
| 4860 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4861 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4720 | fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | |
| 4721 | const zcu = cg.pt.zcu; | |
| 4722 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 4723 | const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 4862 | 4724 | |
| 4863 | const ptr = try func.resolveInst(bin_op.lhs); | |
| 4864 | const offset = try func.resolveInst(bin_op.rhs); | |
| 4865 | const ptr_ty = func.typeOf(bin_op.lhs); | |
| 4725 | const ptr = try cg.resolveInst(bin_op.lhs); | |
| 4726 | const offset = try cg.resolveInst(bin_op.rhs); | |
| 4727 | const ptr_ty = cg.typeOf(bin_op.lhs); | |
| 4866 | 4728 | const pointee_ty = switch (ptr_ty.ptrSize(zcu)) { |
| 4867 | 4729 | .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type |
| 4868 | 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 | 4734 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); |
| 4873 | 4735 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); |
| 4874 | 4736 | |
| 4875 | try func.lowerToStack(ptr); | |
| 4876 | try func.emitWValue(offset); | |
| 4877 | try func.addImm32(@intCast(pointee_ty.abiSize(zcu))); | |
| 4878 | try func.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); | |
| 4879 | try func.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode)); | |
| 4737 | try cg.lowerToStack(ptr); | |
| 4738 | try cg.emitWValue(offset); | |
| 4739 | try cg.addImm32(@intCast(pointee_ty.abiSize(zcu))); | |
| 4740 | try cg.addTag(Mir.Inst.Tag.fromOpcode(mul_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 { | |
| 4885 | const pt = func.pt; | |
| 4886 | const zcu = pt.zcu; | |
| 4746 | fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { | |
| 4747 | const zcu = cg.pt.zcu; | |
| 4887 | 4748 | if (safety) { |
| 4888 | 4749 | // TODO if the value is undef, write 0xaa bytes to dest |
| 4889 | 4750 | } else { |
| 4890 | 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); | |
| 4895 | const ptr_ty = func.typeOf(bin_op.lhs); | |
| 4896 | const value = try func.resolveInst(bin_op.rhs); | |
| 4755 | const ptr = try cg.resolveInst(bin_op.lhs); | |
| 4756 | const ptr_ty = cg.typeOf(bin_op.lhs); | |
| 4757 | const value = try cg.resolveInst(bin_op.rhs); | |
| 4897 | 4758 | const len = switch (ptr_ty.ptrSize(zcu)) { |
| 4898 | .Slice => try func.sliceLen(ptr), | |
| 4759 | .Slice => try cg.sliceLen(ptr), | |
| 4899 | 4760 | .One => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }), |
| 4900 | 4761 | .C, .Many => unreachable, |
| 4901 | 4762 | }; |
| ... | ... | @@ -4905,27 +4766,27 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 4905 | 4766 | else |
| 4906 | 4767 | ptr_ty.childType(zcu); |
| 4907 | 4768 | |
| 4908 | const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty); | |
| 4909 | try func.memset(elem_ty, dst_ptr, len, value); | |
| 4769 | const dst_ptr = try cg.sliceOrArrayPtr(ptr, ptr_ty); | |
| 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 | 4775 | /// Sets a region of memory at `ptr` to the value of `value` |
| 4915 | 4776 | /// When the user has enabled the bulk_memory feature, we lower |
| 4916 | 4777 | /// this to wasm's memset instruction. When the feature is not present, |
| 4917 | 4778 | /// we implement it manually. |
| 4918 | fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void { | |
| 4919 | const pt = func.pt; | |
| 4920 | const abi_size = @as(u32, @intCast(elem_ty.abiSize(pt.zcu))); | |
| 4779 | fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void { | |
| 4780 | const zcu = cg.pt.zcu; | |
| 4781 | const abi_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); | |
| 4921 | 4782 | |
| 4922 | 4783 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| 4923 | 4784 | // If not, we lower it ourselves. |
| 4924 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) { | |
| 4925 | try func.lowerToStack(ptr); | |
| 4926 | try func.emitWValue(value); | |
| 4927 | try func.emitWValue(len); | |
| 4928 | try func.addExtended(.memory_fill); | |
| 4785 | if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) { | |
| 4786 | try cg.lowerToStack(ptr); | |
| 4787 | try cg.emitWValue(value); | |
| 4788 | try cg.emitWValue(len); | |
| 4789 | try cg.addExtended(.memory_fill); | |
| 4929 | 4790 | return; |
| 4930 | 4791 | } |
| 4931 | 4792 | |
| ... | ... | @@ -4933,100 +4794,95 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue |
| 4933 | 4794 | .imm32 => |val| .{ .imm32 = val * abi_size }, |
| 4934 | 4795 | .imm64 => |val| .{ .imm64 = val * abi_size }, |
| 4935 | 4796 | else => if (abi_size != 1) blk: { |
| 4936 | const new_len = try func.ensureAllocLocal(Type.usize); | |
| 4937 | try func.emitWValue(len); | |
| 4938 | switch (func.arch()) { | |
| 4797 | const new_len = try cg.ensureAllocLocal(Type.usize); | |
| 4798 | try cg.emitWValue(len); | |
| 4799 | switch (cg.ptr_size) { | |
| 4939 | 4800 | .wasm32 => { |
| 4940 | try func.emitWValue(.{ .imm32 = abi_size }); | |
| 4941 | try func.addTag(.i32_mul); | |
| 4801 | try cg.emitWValue(.{ .imm32 = abi_size }); | |
| 4802 | try cg.addTag(.i32_mul); | |
| 4942 | 4803 | }, |
| 4943 | 4804 | .wasm64 => { |
| 4944 | try func.emitWValue(.{ .imm64 = abi_size }); | |
| 4945 | try func.addTag(.i64_mul); | |
| 4805 | try cg.emitWValue(.{ .imm64 = abi_size }); | |
| 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 | 4810 | break :blk new_len; |
| 4951 | 4811 | } else len, |
| 4952 | 4812 | }; |
| 4953 | 4813 | |
| 4954 | var end_ptr = try func.allocLocal(Type.usize); | |
| 4955 | defer end_ptr.free(func); | |
| 4956 | var new_ptr = try func.buildPointerOffset(ptr, 0, .new); | |
| 4957 | defer new_ptr.free(func); | |
| 4814 | var end_ptr = try cg.allocLocal(Type.usize); | |
| 4815 | defer end_ptr.free(cg); | |
| 4816 | var new_ptr = try cg.buildPointerOffset(ptr, 0, .new); | |
| 4817 | defer new_ptr.free(cg); | |
| 4958 | 4818 | |
| 4959 | 4819 | // get the loop conditional: if current pointer address equals final pointer's address |
| 4960 | try func.lowerToStack(ptr); | |
| 4961 | try func.emitWValue(final_len); | |
| 4962 | switch (func.arch()) { | |
| 4963 | .wasm32 => try func.addTag(.i32_add), | |
| 4964 | .wasm64 => try func.addTag(.i64_add), | |
| 4965 | else => unreachable, | |
| 4820 | try cg.lowerToStack(ptr); | |
| 4821 | try cg.emitWValue(final_len); | |
| 4822 | switch (cg.ptr_size) { | |
| 4823 | .wasm32 => try cg.addTag(.i32_add), | |
| 4824 | .wasm64 => try cg.addTag(.i64_add), | |
| 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 | 4828 | // outer block to jump to when loop is done |
| 4970 | try func.startBlock(.block, wasm.block_empty); | |
| 4971 | try func.startBlock(.loop, wasm.block_empty); | |
| 4829 | try cg.startBlock(.block, .empty); | |
| 4830 | try cg.startBlock(.loop, .empty); | |
| 4972 | 4831 | |
| 4973 | 4832 | // check for condition for loop end |
| 4974 | try func.emitWValue(new_ptr); | |
| 4975 | try func.emitWValue(end_ptr); | |
| 4976 | switch (func.arch()) { | |
| 4977 | .wasm32 => try func.addTag(.i32_eq), | |
| 4978 | .wasm64 => try func.addTag(.i64_eq), | |
| 4979 | else => unreachable, | |
| 4833 | try cg.emitWValue(new_ptr); | |
| 4834 | try cg.emitWValue(end_ptr); | |
| 4835 | switch (cg.ptr_size) { | |
| 4836 | .wasm32 => try cg.addTag(.i32_eq), | |
| 4837 | .wasm64 => try cg.addTag(.i64_eq), | |
| 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 | 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 | 4844 | // move the pointer to the next element |
| 4987 | try func.emitWValue(new_ptr); | |
| 4988 | switch (func.arch()) { | |
| 4845 | try cg.emitWValue(new_ptr); | |
| 4846 | switch (cg.ptr_size) { | |
| 4989 | 4847 | .wasm32 => { |
| 4990 | try func.emitWValue(.{ .imm32 = abi_size }); | |
| 4991 | try func.addTag(.i32_add); | |
| 4848 | try cg.emitWValue(.{ .imm32 = abi_size }); | |
| 4849 | try cg.addTag(.i32_add); | |
| 4992 | 4850 | }, |
| 4993 | 4851 | .wasm64 => { |
| 4994 | try func.emitWValue(.{ .imm64 = abi_size }); | |
| 4995 | try func.addTag(.i64_add); | |
| 4852 | try cg.emitWValue(.{ .imm64 = abi_size }); | |
| 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 | 4858 | // end of loop |
| 5002 | try func.addLabel(.br, 0); // jump to start of loop | |
| 5003 | try func.endBlock(); | |
| 5004 | try func.endBlock(); | |
| 4859 | try cg.addLabel(.br, 0); // jump to start of loop | |
| 4860 | try cg.endBlock(); | |
| 4861 | try cg.endBlock(); | |
| 5005 | 4862 | } |
| 5006 | 4863 | |
| 5007 | fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5008 | const pt = func.pt; | |
| 5009 | const zcu = pt.zcu; | |
| 5010 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 4864 | fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4865 | const zcu = cg.pt.zcu; | |
| 4866 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 5011 | 4867 | |
| 5012 | const array_ty = func.typeOf(bin_op.lhs); | |
| 5013 | const array = try func.resolveInst(bin_op.lhs); | |
| 5014 | const index = try func.resolveInst(bin_op.rhs); | |
| 4868 | const array_ty = cg.typeOf(bin_op.lhs); | |
| 4869 | const array = try cg.resolveInst(bin_op.lhs); | |
| 4870 | const index = try cg.resolveInst(bin_op.rhs); | |
| 5015 | 4871 | const elem_ty = array_ty.childType(zcu); |
| 5016 | 4872 | const elem_size = elem_ty.abiSize(zcu); |
| 5017 | 4873 | |
| 5018 | if (isByRef(array_ty, pt, func.target.*)) { | |
| 5019 | try func.lowerToStack(array); | |
| 5020 | try func.emitWValue(index); | |
| 5021 | try func.addImm32(@intCast(elem_size)); | |
| 5022 | try func.addTag(.i32_mul); | |
| 5023 | try func.addTag(.i32_add); | |
| 4874 | if (isByRef(array_ty, zcu, cg.target)) { | |
| 4875 | try cg.lowerToStack(array); | |
| 4876 | try cg.emitWValue(index); | |
| 4877 | try cg.addImm32(@intCast(elem_size)); | |
| 4878 | try cg.addTag(.i32_mul); | |
| 4879 | try cg.addTag(.i32_add); | |
| 5024 | 4880 | } else { |
| 5025 | std.debug.assert(array_ty.zigTypeTag(zcu) == .vector); | |
| 4881 | assert(array_ty.zigTypeTag(zcu) == .vector); | |
| 5026 | 4882 | |
| 5027 | 4883 | switch (index) { |
| 5028 | 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 | 4886 | 8 => if (elem_ty.isSignedInt(zcu)) .i8x16_extract_lane_s else .i8x16_extract_lane_u, |
| 5031 | 4887 | 16 => if (elem_ty.isSignedInt(zcu)) .i16x8_extract_lane_s else .i16x8_extract_lane_u, |
| 5032 | 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 | 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)); | |
| 5042 | try func.mir_extra.appendSlice(func.gpa, &operands); | |
| 5043 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 4897 | const extra_index = cg.extraLen(); | |
| 4898 | try cg.mir_extra.appendSlice(cg.gpa, &operands); | |
| 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 | 4903 | else => { |
| 5048 | const stack_vec = try func.allocStack(array_ty); | |
| 5049 | try func.store(stack_vec, array, array_ty, 0); | |
| 4904 | const stack_vec = try cg.allocStack(array_ty); | |
| 4905 | try cg.store(stack_vec, array, array_ty, 0); | |
| 5050 | 4906 | |
| 5051 | 4907 | // Is a non-unrolled vector (v128) |
| 5052 | try func.lowerToStack(stack_vec); | |
| 5053 | try func.emitWValue(index); | |
| 5054 | try func.addImm32(@intCast(elem_size)); | |
| 5055 | try func.addTag(.i32_mul); | |
| 5056 | try func.addTag(.i32_add); | |
| 4908 | try cg.lowerToStack(stack_vec); | |
| 4909 | try cg.emitWValue(index); | |
| 4910 | try cg.addImm32(@intCast(elem_size)); | |
| 4911 | try cg.addTag(.i32_mul); | |
| 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 | 4918 | .stack |
| 5063 | 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 { | |
| 5070 | const pt = func.pt; | |
| 5071 | const zcu = pt.zcu; | |
| 5072 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4925 | fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4926 | const zcu = cg.pt.zcu; | |
| 4927 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5073 | 4928 | |
| 5074 | const operand = try func.resolveInst(ty_op.operand); | |
| 5075 | const op_ty = func.typeOf(ty_op.operand); | |
| 5076 | const op_bits = op_ty.floatBits(func.target.*); | |
| 4929 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4930 | const op_ty = cg.typeOf(ty_op.operand); | |
| 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 | 4934 | const dest_info = dest_ty.intInfo(zcu); |
| 5080 | 4935 | |
| 5081 | 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 | 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); | |
| 5087 | ||
| 5088 | var fn_name_buf: [16]u8 = undefined; | |
| 5089 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__fix{s}{s}f{s}i", .{ | |
| 5090 | switch (dest_info.signedness) { | |
| 5091 | .signed => "", | |
| 5092 | .unsigned => "uns", | |
| 4941 | const dest_bitsize = if (dest_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, dest_info.bits); | |
| 4942 | ||
| 4943 | const intrinsic = switch (dest_info.signedness) { | |
| 4944 | inline .signed, .unsigned => |ct_s| switch (op_bits) { | |
| 4945 | inline 16, 32, 64, 80, 128 => |ct_op_bits| switch (dest_bitsize) { | |
| 4946 | inline 32, 64, 128 => |ct_dest_bits| @field( | |
| 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), | |
| 5095 | target_util.compilerRtIntAbbrev(dest_bitsize), | |
| 5096 | }) catch unreachable; | |
| 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}); | |
| 4959 | }; | |
| 4960 | const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{operand}); | |
| 4961 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 5100 | 4962 | } |
| 5101 | 4963 | |
| 5102 | try func.emitWValue(operand); | |
| 4964 | try cg.emitWValue(operand); | |
| 5103 | 4965 | const op = buildOpcode(.{ |
| 5104 | 4966 | .op = .trunc, |
| 5105 | .valtype1 = typeToValtype(dest_ty, pt, func.target.*), | |
| 5106 | .valtype2 = typeToValtype(op_ty, pt, func.target.*), | |
| 4967 | .valtype1 = typeToValtype(dest_ty, zcu, cg.target), | |
| 4968 | .valtype2 = typeToValtype(op_ty, zcu, cg.target), | |
| 5107 | 4969 | .signedness = dest_info.signedness, |
| 5108 | 4970 | }); |
| 5109 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); | |
| 5110 | const result = try func.wrapOperand(.stack, dest_ty); | |
| 5111 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 4971 | try cg.addTag(Mir.Inst.Tag.fromOpcode(op)); | |
| 4972 | const result = try cg.wrapOperand(.stack, dest_ty); | |
| 4973 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 5112 | 4974 | } |
| 5113 | 4975 | |
| 5114 | fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5115 | const pt = func.pt; | |
| 5116 | const zcu = pt.zcu; | |
| 5117 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 4976 | fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 4977 | const zcu = cg.pt.zcu; | |
| 4978 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5118 | 4979 | |
| 5119 | const operand = try func.resolveInst(ty_op.operand); | |
| 5120 | const op_ty = func.typeOf(ty_op.operand); | |
| 4980 | const operand = try cg.resolveInst(ty_op.operand); | |
| 4981 | const op_ty = cg.typeOf(ty_op.operand); | |
| 5121 | 4982 | const op_info = op_ty.intInfo(zcu); |
| 5122 | 4983 | |
| 5123 | const dest_ty = func.typeOfIndex(inst); | |
| 5124 | const dest_bits = dest_ty.floatBits(func.target.*); | |
| 4984 | const dest_ty = cg.typeOfIndex(inst); | |
| 4985 | const dest_bits = dest_ty.floatBits(cg.target.*); | |
| 5125 | 4986 | |
| 5126 | 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 | 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); | |
| 5132 | ||
| 5133 | var fn_name_buf: [16]u8 = undefined; | |
| 5134 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "__float{s}{s}i{s}f", .{ | |
| 5135 | switch (op_info.signedness) { | |
| 5136 | .signed => "", | |
| 5137 | .unsigned => "un", | |
| 4992 | const op_bitsize = if (op_info.bits <= 32) 32 else std.math.ceilPowerOfTwoAssert(u16, op_info.bits); | |
| 4993 | ||
| 4994 | const intrinsic = switch (op_info.signedness) { | |
| 4995 | inline .signed, .unsigned => |ct_s| switch (op_bitsize) { | |
| 4996 | inline 32, 64, 128 => |ct_int_bits| switch (dest_bits) { | |
| 4997 | inline 16, 32, 64, 80, 128 => |ct_float_bits| @field( | |
| 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), | |
| 5140 | target_util.compilerRtFloatAbbrev(dest_bits), | |
| 5141 | }) catch unreachable; | |
| 5010 | }; | |
| 5142 | 5011 | |
| 5143 | const result = try func.callIntrinsic(fn_name, &.{op_ty.ip_index}, dest_ty, &.{operand}); | |
| 5144 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5012 | const result = try cg.callIntrinsic(intrinsic, &.{op_ty.ip_index}, dest_ty, &.{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 | 5017 | const op = buildOpcode(.{ |
| 5149 | 5018 | .op = .convert, |
| 5150 | .valtype1 = typeToValtype(dest_ty, pt, func.target.*), | |
| 5151 | .valtype2 = typeToValtype(op_ty, pt, func.target.*), | |
| 5019 | .valtype1 = typeToValtype(dest_ty, zcu, cg.target), | |
| 5020 | .valtype2 = typeToValtype(op_ty, zcu, cg.target), | |
| 5152 | 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 { | |
| 5160 | const pt = func.pt; | |
| 5161 | const zcu = pt.zcu; | |
| 5162 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5163 | const operand = try func.resolveInst(ty_op.operand); | |
| 5164 | const ty = func.typeOfIndex(inst); | |
| 5028 | fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5029 | const zcu = cg.pt.zcu; | |
| 5030 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5031 | const operand = try cg.resolveInst(ty_op.operand); | |
| 5032 | const ty = cg.typeOfIndex(inst); | |
| 5165 | 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 | 5036 | switch (operand) { |
| 5169 | 5037 | // when the operand lives in the linear memory section, we can directly |
| 5170 | 5038 | // load and splat the value at once. Meaning we do not first have to load |
| 5171 | 5039 | // the scalar value onto the stack. |
| 5172 | .stack_offset, .memory, .memory_offset => { | |
| 5040 | .stack_offset, .nav_ref, .uav_ref => { | |
| 5173 | 5041 | const opcode = switch (elem_ty.bitSize(zcu)) { |
| 5174 | 8 => std.wasm.simdOpcode(.v128_load8_splat), | |
| 5175 | 16 => std.wasm.simdOpcode(.v128_load16_splat), | |
| 5176 | 32 => std.wasm.simdOpcode(.v128_load32_splat), | |
| 5177 | 64 => std.wasm.simdOpcode(.v128_load64_splat), | |
| 5042 | 8 => @intFromEnum(std.wasm.SimdOpcode.v128_load8_splat), | |
| 5043 | 16 => @intFromEnum(std.wasm.SimdOpcode.v128_load16_splat), | |
| 5044 | 32 => @intFromEnum(std.wasm.SimdOpcode.v128_load32_splat), | |
| 5045 | 64 => @intFromEnum(std.wasm.SimdOpcode.v128_load64_splat), | |
| 5178 | 5046 | else => break :blk, // Cannot make use of simd-instructions |
| 5179 | 5047 | }; |
| 5180 | try func.emitWValue(operand); | |
| 5181 | // TODO: Add helper functions for simd opcodes | |
| 5182 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | |
| 5048 | try cg.emitWValue(operand); | |
| 5049 | const extra_index: u32 = cg.extraLen(); | |
| 5183 | 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 | 5052 | opcode, |
| 5186 | 5053 | operand.offset(), |
| 5187 | 5054 | @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?), |
| 5188 | 5055 | }); |
| 5189 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 5190 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 5056 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 5057 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 5191 | 5058 | }, |
| 5192 | 5059 | .local => { |
| 5193 | 5060 | const opcode = switch (elem_ty.bitSize(zcu)) { |
| 5194 | 8 => std.wasm.simdOpcode(.i8x16_splat), | |
| 5195 | 16 => std.wasm.simdOpcode(.i16x8_splat), | |
| 5196 | 32 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i32x4_splat) else std.wasm.simdOpcode(.f32x4_splat), | |
| 5197 | 64 => if (elem_ty.isInt(zcu)) std.wasm.simdOpcode(.i64x2_splat) else std.wasm.simdOpcode(.f64x2_splat), | |
| 5061 | 8 => @intFromEnum(std.wasm.SimdOpcode.i8x16_splat), | |
| 5062 | 16 => @intFromEnum(std.wasm.SimdOpcode.i16x8_splat), | |
| 5063 | 32 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i32x4_splat) else @intFromEnum(std.wasm.SimdOpcode.f32x4_splat), | |
| 5064 | 64 => if (elem_ty.isInt(zcu)) @intFromEnum(std.wasm.SimdOpcode.i64x2_splat) else @intFromEnum(std.wasm.SimdOpcode.f64x2_splat), | |
| 5198 | 5065 | else => break :blk, // Cannot make use of simd-instructions |
| 5199 | 5066 | }; |
| 5200 | try func.emitWValue(operand); | |
| 5201 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | |
| 5202 | try func.mir_extra.append(func.gpa, opcode); | |
| 5203 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 5204 | return func.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 5067 | try cg.emitWValue(operand); | |
| 5068 | const extra_index = cg.extraLen(); | |
| 5069 | try cg.mir_extra.append(cg.gpa, opcode); | |
| 5070 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 5071 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); | |
| 5205 | 5072 | }, |
| 5206 | 5073 | else => unreachable, |
| 5207 | 5074 | } |
| ... | ... | @@ -5209,38 +5076,38 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5209 | 5076 | const elem_size = elem_ty.bitSize(zcu); |
| 5210 | 5077 | const vector_len = @as(usize, @intCast(ty.vectorLen(zcu))); |
| 5211 | 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 | 5083 | const elem_byte_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); |
| 5217 | 5084 | var index: usize = 0; |
| 5218 | 5085 | var offset: u32 = 0; |
| 5219 | 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 | 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 { | |
| 5228 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 5229 | const operand = try func.resolveInst(pl_op.operand); | |
| 5094 | fn airSelect(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5095 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 5096 | const operand = try cg.resolveInst(pl_op.operand); | |
| 5230 | 5097 | |
| 5231 | 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 { | |
| 5236 | const pt = func.pt; | |
| 5102 | fn airShuffle(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5103 | const pt = cg.pt; | |
| 5237 | 5104 | const zcu = pt.zcu; |
| 5238 | const inst_ty = func.typeOfIndex(inst); | |
| 5239 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5240 | const extra = func.air.extraData(Air.Shuffle, ty_pl.payload).data; | |
| 5105 | const inst_ty = cg.typeOfIndex(inst); | |
| 5106 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5107 | const extra = cg.air.extraData(Air.Shuffle, ty_pl.payload).data; | |
| 5241 | 5108 | |
| 5242 | const a = try func.resolveInst(extra.a); | |
| 5243 | const b = try func.resolveInst(extra.b); | |
| 5109 | const a = try cg.resolveInst(extra.a); | |
| 5110 | const b = try cg.resolveInst(extra.b); | |
| 5244 | 5111 | const mask = Value.fromInterned(extra.mask); |
| 5245 | 5112 | const mask_len = extra.mask_len; |
| 5246 | 5113 | |
| ... | ... | @@ -5248,26 +5115,26 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5248 | 5115 | const elem_size = child_ty.abiSize(zcu); |
| 5249 | 5116 | |
| 5250 | 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.*)) { | |
| 5252 | const result = try func.allocStack(inst_ty); | |
| 5118 | if (isByRef(cg.typeOf(extra.a), zcu, cg.target) or isByRef(inst_ty, zcu, cg.target)) { | |
| 5119 | const result = try cg.allocStack(inst_ty); | |
| 5253 | 5120 | |
| 5254 | 5121 | for (0..mask_len) |index| { |
| 5255 | 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 | 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 | 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 | 5135 | } else { |
| 5269 | 5136 | var operands = [_]u32{ |
| 5270 | std.wasm.simdOpcode(.i8x16_shuffle), | |
| 5137 | @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle), | |
| 5271 | 5138 | } ++ [1]u32{undefined} ** 4; |
| 5272 | 5139 | |
| 5273 | 5140 | var lanes = mem.asBytes(operands[1..]); |
| ... | ... | @@ -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); | |
| 5287 | try func.emitWValue(b); | |
| 5153 | try cg.emitWValue(a); | |
| 5154 | try cg.emitWValue(b); | |
| 5288 | 5155 | |
| 5289 | const extra_index = @as(u32, @intCast(func.mir_extra.items.len)); | |
| 5290 | try func.mir_extra.appendSlice(func.gpa, &operands); | |
| 5291 | try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | |
| 5156 | const extra_index = cg.extraLen(); | |
| 5157 | try cg.mir_extra.appendSlice(cg.gpa, &operands); | |
| 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 { | |
| 5298 | const reduce = func.air.instructions.items(.data)[@intFromEnum(inst)].reduce; | |
| 5299 | const operand = try func.resolveInst(reduce.operand); | |
| 5164 | fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5165 | const reduce = cg.air.instructions.items(.data)[@intFromEnum(inst)].reduce; | |
| 5166 | const operand = try cg.resolveInst(reduce.operand); | |
| 5300 | 5167 | |
| 5301 | 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 { | |
| 5306 | const pt = func.pt; | |
| 5172 | fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5173 | const pt = cg.pt; | |
| 5307 | 5174 | const zcu = pt.zcu; |
| 5308 | 5175 | const ip = &zcu.intern_pool; |
| 5309 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5310 | const result_ty = func.typeOfIndex(inst); | |
| 5176 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5177 | const result_ty = cg.typeOfIndex(inst); | |
| 5311 | 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 | 5181 | const result: WValue = result_value: { |
| 5315 | 5182 | switch (result_ty.zigTypeTag(zcu)) { |
| 5316 | 5183 | .array => { |
| 5317 | const result = try func.allocStack(result_ty); | |
| 5184 | const result = try cg.allocStack(result_ty); | |
| 5318 | 5185 | const elem_ty = result_ty.childType(zcu); |
| 5319 | 5186 | const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu))); |
| 5320 | 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 | 5189 | } else null; |
| 5323 | 5190 | |
| 5324 | 5191 | // When the element type is by reference, we must copy the entire |
| 5325 | 5192 | // value. It is therefore safer to move the offset pointer and store |
| 5326 | 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 | 5195 | // copy stack pointer into a temporary local, which is |
| 5329 | 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 | 5198 | for (elements, 0..) |elem, elem_index| { |
| 5332 | const elem_val = try func.resolveInst(elem); | |
| 5333 | try func.store(offset, elem_val, elem_ty, 0); | |
| 5199 | const elem_val = try cg.resolveInst(elem); | |
| 5200 | try cg.store(offset, elem_val, elem_ty, 0); | |
| 5334 | 5201 | |
| 5335 | 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 | 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 | 5209 | } else { |
| 5343 | 5210 | var offset: u32 = 0; |
| 5344 | 5211 | for (elements) |elem| { |
| 5345 | const elem_val = try func.resolveInst(elem); | |
| 5346 | try func.store(result, elem_val, elem_ty, offset); | |
| 5212 | const elem_val = try cg.resolveInst(elem); | |
| 5213 | try cg.store(result, elem_val, elem_ty, offset); | |
| 5347 | 5214 | offset += elem_size; |
| 5348 | 5215 | } |
| 5349 | 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 | 5220 | break :result_value result; |
| 5354 | 5221 | }, |
| 5355 | 5222 | .@"struct" => switch (result_ty.containerLayout(zcu)) { |
| 5356 | 5223 | .@"packed" => { |
| 5357 | if (isByRef(result_ty, pt, func.target.*)) { | |
| 5358 | return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{}); | |
| 5224 | if (isByRef(result_ty, zcu, cg.target)) { | |
| 5225 | return cg.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{}); | |
| 5359 | 5226 | } |
| 5360 | 5227 | const packed_struct = zcu.typeToPackedStruct(result_ty).?; |
| 5361 | 5228 | const field_types = packed_struct.field_types; |
| 5362 | 5229 | const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); |
| 5363 | 5230 | |
| 5364 | 5231 | // ensure the result is zero'd |
| 5365 | const result = try func.allocLocal(backing_type); | |
| 5232 | const result = try cg.allocLocal(backing_type); | |
| 5366 | 5233 | if (backing_type.bitSize(zcu) <= 32) |
| 5367 | try func.addImm32(0) | |
| 5234 | try cg.addImm32(0) | |
| 5368 | 5235 | else |
| 5369 | try func.addImm64(0); | |
| 5370 | try func.addLabel(.local_set, result.local.value); | |
| 5236 | try cg.addImm64(0); | |
| 5237 | try cg.addLocal(.local_set, result.local.value); | |
| 5371 | 5238 | |
| 5372 | 5239 | var current_bit: u16 = 0; |
| 5373 | 5240 | for (elements, 0..) |elem, elem_index| { |
| ... | ... | @@ -5379,46 +5246,46 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5379 | 5246 | else |
| 5380 | 5247 | .{ .imm64 = current_bit }; |
| 5381 | 5248 | |
| 5382 | const value = try func.resolveInst(elem); | |
| 5249 | const value = try cg.resolveInst(elem); | |
| 5383 | 5250 | const value_bit_size: u16 = @intCast(field_ty.bitSize(zcu)); |
| 5384 | 5251 | const int_ty = try pt.intType(.unsigned, value_bit_size); |
| 5385 | 5252 | |
| 5386 | 5253 | // load our current result on stack so we can perform all transformations |
| 5387 | 5254 | // using only stack values. Saving the cost of loads and stores. |
| 5388 | try func.emitWValue(result); | |
| 5389 | const bitcasted = try func.bitcast(int_ty, field_ty, value); | |
| 5390 | const extended_val = try func.intcast(bitcasted, int_ty, backing_type); | |
| 5255 | try cg.emitWValue(result); | |
| 5256 | const bitcasted = try cg.bitcast(int_ty, field_ty, value); | |
| 5257 | const extended_val = try cg.intcast(bitcasted, int_ty, backing_type); | |
| 5391 | 5258 | // no need to shift any values when the current offset is 0 |
| 5392 | 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 | 5261 | } else extended_val; |
| 5395 | 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"); | |
| 5397 | try func.addLabel(.local_set, result.local.value); | |
| 5263 | _ = try cg.binOp(.stack, shifted, backing_type, .@"or"); | |
| 5264 | try cg.addLocal(.local_set, result.local.value); | |
| 5398 | 5265 | current_bit += value_bit_size; |
| 5399 | 5266 | } |
| 5400 | 5267 | break :result_value result; |
| 5401 | 5268 | }, |
| 5402 | 5269 | else => { |
| 5403 | const result = try func.allocStack(result_ty); | |
| 5404 | const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset | |
| 5270 | const result = try cg.allocStack(result_ty); | |
| 5271 | const offset = try cg.buildPointerOffset(result, 0, .new); // pointer to offset | |
| 5405 | 5272 | var prev_field_offset: u64 = 0; |
| 5406 | 5273 | for (elements, 0..) |elem, elem_index| { |
| 5407 | 5274 | if (try result_ty.structFieldValueComptime(pt, elem_index) != null) continue; |
| 5408 | 5275 | |
| 5409 | 5276 | const elem_ty = result_ty.fieldType(elem_index, zcu); |
| 5410 | 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 | 5279 | prev_field_offset = field_offset; |
| 5413 | 5280 | |
| 5414 | const value = try func.resolveInst(elem); | |
| 5415 | try func.store(offset, value, elem_ty, 0); | |
| 5281 | const value = try cg.resolveInst(elem); | |
| 5282 | try cg.store(offset, value, elem_ty, 0); | |
| 5416 | 5283 | } |
| 5417 | 5284 | |
| 5418 | 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 | 5289 | else => unreachable, |
| 5423 | 5290 | } |
| 5424 | 5291 | }; |
| ... | ... | @@ -5426,22 +5293,22 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5426 | 5293 | if (elements.len <= Liveness.bpi - 1) { |
| 5427 | 5294 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 5428 | 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 | 5299 | for (elements) |arg| bt.feed(arg); |
| 5433 | 5300 | return bt.finishAir(result); |
| 5434 | 5301 | } |
| 5435 | 5302 | |
| 5436 | fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5437 | const pt = func.pt; | |
| 5303 | fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5304 | const pt = cg.pt; | |
| 5438 | 5305 | const zcu = pt.zcu; |
| 5439 | 5306 | const ip = &zcu.intern_pool; |
| 5440 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5441 | const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data; | |
| 5307 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5308 | const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data; | |
| 5442 | 5309 | |
| 5443 | 5310 | const result = result: { |
| 5444 | const union_ty = func.typeOfIndex(inst); | |
| 5311 | const union_ty = cg.typeOfIndex(inst); | |
| 5445 | 5312 | const layout = union_ty.unionGetLayout(zcu); |
| 5446 | 5313 | const union_obj = zcu.typeToUnion(union_ty).?; |
| 5447 | 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 | 5318 | const tag_ty = union_ty.unionTagTypeHypothetical(zcu); |
| 5452 | 5319 | const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?; |
| 5453 | 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 | 5323 | if (layout.payload_size == 0) { |
| 5457 | 5324 | if (layout.tag_size == 0) { |
| 5458 | 5325 | break :result .none; |
| 5459 | 5326 | } |
| 5460 | assert(!isByRef(union_ty, pt, func.target.*)); | |
| 5327 | assert(!isByRef(union_ty, zcu, cg.target)); | |
| 5461 | 5328 | break :result tag_int; |
| 5462 | 5329 | } |
| 5463 | 5330 | |
| 5464 | if (isByRef(union_ty, pt, func.target.*)) { | |
| 5465 | const result_ptr = try func.allocStack(union_ty); | |
| 5466 | const payload = try func.resolveInst(extra.init); | |
| 5331 | if (isByRef(union_ty, zcu, cg.target)) { | |
| 5332 | const result_ptr = try cg.allocStack(union_ty); | |
| 5333 | const payload = try cg.resolveInst(extra.init); | |
| 5467 | 5334 | if (layout.tag_align.compare(.gte, layout.payload_align)) { |
| 5468 | if (isByRef(field_ty, pt, func.target.*)) { | |
| 5469 | const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); | |
| 5470 | try func.store(payload_ptr, payload, field_ty, 0); | |
| 5335 | if (isByRef(field_ty, zcu, cg.target)) { | |
| 5336 | const payload_ptr = try cg.buildPointerOffset(result_ptr, layout.tag_size, .new); | |
| 5337 | try cg.store(payload_ptr, payload, field_ty, 0); | |
| 5471 | 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 | 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 | 5345 | } else { |
| 5479 | try func.store(result_ptr, payload, field_ty, 0); | |
| 5346 | try cg.store(result_ptr, payload, field_ty, 0); | |
| 5480 | 5347 | if (layout.tag_size > 0) { |
| 5481 | try func.store( | |
| 5348 | try cg.store( | |
| 5482 | 5349 | result_ptr, |
| 5483 | 5350 | tag_int, |
| 5484 | 5351 | Type.fromInterned(union_obj.enum_tag_ty), |
| ... | ... | @@ -5488,138 +5355,136 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5488 | 5355 | } |
| 5489 | 5356 | break :result result_ptr; |
| 5490 | 5357 | } else { |
| 5491 | const operand = try func.resolveInst(extra.init); | |
| 5358 | const operand = try cg.resolveInst(extra.init); | |
| 5492 | 5359 | const union_int_type = try pt.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(zcu)))); |
| 5493 | 5360 | if (field_ty.zigTypeTag(zcu) == .float) { |
| 5494 | 5361 | const int_type = try pt.intType(.unsigned, @intCast(field_ty.bitSize(zcu))); |
| 5495 | const bitcasted = try func.bitcast(field_ty, int_type, operand); | |
| 5496 | break :result try func.trunc(bitcasted, int_type, union_int_type); | |
| 5362 | const bitcasted = try cg.bitcast(field_ty, int_type, operand); | |
| 5363 | break :result try cg.trunc(bitcasted, int_type, union_int_type); | |
| 5497 | 5364 | } else if (field_ty.isPtrAtRuntime(zcu)) { |
| 5498 | 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 { | |
| 5509 | const prefetch = func.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; | |
| 5510 | return func.finishAir(inst, .none, &.{prefetch.ptr}); | |
| 5375 | fn airPrefetch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5376 | const prefetch = cg.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; | |
| 5377 | return cg.finishAir(inst, .none, &.{prefetch.ptr}); | |
| 5511 | 5378 | } |
| 5512 | 5379 | |
| 5513 | fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5514 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 5380 | fn airWasmMemorySize(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5381 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 5515 | 5382 | |
| 5516 | try func.addLabel(.memory_size, pl_op.payload); | |
| 5517 | return func.finishAir(inst, .stack, &.{pl_op.operand}); | |
| 5383 | try cg.addLabel(.memory_size, pl_op.payload); | |
| 5384 | return cg.finishAir(inst, .stack, &.{pl_op.operand}); | |
| 5518 | 5385 | } |
| 5519 | 5386 | |
| 5520 | fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void { | |
| 5521 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 5387 | fn airWasmMemoryGrow(cg: *CodeGen, inst: Air.Inst.Index) !void { | |
| 5388 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 5522 | 5389 | |
| 5523 | const operand = try func.resolveInst(pl_op.operand); | |
| 5524 | try func.emitWValue(operand); | |
| 5525 | try func.addLabel(.memory_grow, pl_op.payload); | |
| 5526 | return func.finishAir(inst, .stack, &.{pl_op.operand}); | |
| 5390 | const operand = try cg.resolveInst(pl_op.operand); | |
| 5391 | try cg.emitWValue(operand); | |
| 5392 | try cg.addLabel(.memory_grow, pl_op.payload); | |
| 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 { | |
| 5530 | const pt = func.pt; | |
| 5531 | const zcu = pt.zcu; | |
| 5396 | fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { | |
| 5397 | const zcu = cg.pt.zcu; | |
| 5532 | 5398 | assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu)); |
| 5533 | 5399 | assert(op == .eq or op == .neq); |
| 5534 | 5400 | const payload_ty = operand_ty.optionalChild(zcu); |
| 5535 | 5401 | |
| 5536 | 5402 | // We store the final result in here that will be validated |
| 5537 | 5403 | // if the optional is truly equal. |
| 5538 | var result = try func.ensureAllocLocal(Type.i32); | |
| 5539 | defer result.free(func); | |
| 5540 | ||
| 5541 | try func.startBlock(.block, wasm.block_empty); | |
| 5542 | _ = try func.isNull(lhs, operand_ty, .i32_eq); | |
| 5543 | _ = try func.isNull(rhs, operand_ty, .i32_eq); | |
| 5544 | try func.addTag(.i32_ne); // inverse so we can exit early | |
| 5545 | try func.addLabel(.br_if, 0); | |
| 5546 | ||
| 5547 | _ = try func.load(lhs, payload_ty, 0); | |
| 5548 | _ = try func.load(rhs, payload_ty, 0); | |
| 5549 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target.*) }); | |
| 5550 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 5551 | try func.addLabel(.br_if, 0); | |
| 5552 | ||
| 5553 | try func.addImm32(1); | |
| 5554 | try func.addLabel(.local_set, result.local.value); | |
| 5555 | try func.endBlock(); | |
| 5556 | ||
| 5557 | try func.emitWValue(result); | |
| 5558 | try func.addImm32(0); | |
| 5559 | try func.addTag(if (op == .eq) .i32_ne else .i32_eq); | |
| 5404 | var result = try cg.ensureAllocLocal(Type.i32); | |
| 5405 | defer result.free(cg); | |
| 5406 | ||
| 5407 | try cg.startBlock(.block, .empty); | |
| 5408 | _ = try cg.isNull(lhs, operand_ty, .i32_eq); | |
| 5409 | _ = try cg.isNull(rhs, operand_ty, .i32_eq); | |
| 5410 | try cg.addTag(.i32_ne); // inverse so we can exit early | |
| 5411 | try cg.addLabel(.br_if, 0); | |
| 5412 | ||
| 5413 | _ = try cg.load(lhs, payload_ty, 0); | |
| 5414 | _ = try cg.load(rhs, payload_ty, 0); | |
| 5415 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, zcu, cg.target) }); | |
| 5416 | try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | |
| 5417 | try cg.addLabel(.br_if, 0); | |
| 5418 | ||
| 5419 | try cg.addImm32(1); | |
| 5420 | try cg.addLocal(.local_set, result.local.value); | |
| 5421 | try cg.endBlock(); | |
| 5422 | ||
| 5423 | try cg.emitWValue(result); | |
| 5424 | try cg.addImm32(0); | |
| 5425 | try cg.addTag(if (op == .eq) .i32_ne else .i32_eq); | |
| 5560 | 5426 | return .stack; |
| 5561 | 5427 | } |
| 5562 | 5428 | |
| 5563 | 5429 | /// Compares big integers by checking both its high bits and low bits. |
| 5564 | 5430 | /// NOTE: Leaves the result of the comparison on top of the stack. |
| 5565 | 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 { | |
| 5567 | const pt = func.pt; | |
| 5568 | const zcu = pt.zcu; | |
| 5432 | fn cmpBigInt(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { | |
| 5433 | const zcu = cg.pt.zcu; | |
| 5569 | 5434 | assert(operand_ty.abiSize(zcu) >= 16); |
| 5570 | 5435 | assert(!(lhs != .stack and rhs == .stack)); |
| 5571 | 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); | |
| 5576 | defer lhs_msb.free(func); | |
| 5577 | var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64); | |
| 5578 | defer rhs_msb.free(func); | |
| 5440 | var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 5441 | defer lhs_msb.free(cg); | |
| 5442 | var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 5443 | defer rhs_msb.free(cg); | |
| 5579 | 5444 | |
| 5580 | 5445 | switch (op) { |
| 5581 | 5446 | .eq, .neq => { |
| 5582 | const xor_high = try func.binOp(lhs_msb, rhs_msb, Type.u64, .xor); | |
| 5583 | const lhs_lsb = try func.load(lhs, Type.u64, 0); | |
| 5584 | const rhs_lsb = try func.load(rhs, Type.u64, 0); | |
| 5585 | const xor_low = try func.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor); | |
| 5586 | const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or"); | |
| 5447 | const xor_high = try cg.binOp(lhs_msb, rhs_msb, Type.u64, .xor); | |
| 5448 | const lhs_lsb = try cg.load(lhs, Type.u64, 0); | |
| 5449 | const rhs_lsb = try cg.load(rhs, Type.u64, 0); | |
| 5450 | const xor_low = try cg.binOp(lhs_lsb, rhs_lsb, Type.u64, .xor); | |
| 5451 | const or_result = try cg.binOp(xor_high, xor_low, Type.u64, .@"or"); | |
| 5587 | 5452 | |
| 5588 | 5453 | switch (op) { |
| 5589 | .eq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq), | |
| 5590 | .neq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq), | |
| 5454 | .eq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq), | |
| 5455 | .neq => return cg.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq), | |
| 5591 | 5456 | else => unreachable, |
| 5592 | 5457 | } |
| 5593 | 5458 | }, |
| 5594 | 5459 | else => { |
| 5595 | 5460 | const ty = if (operand_ty.isSignedInt(zcu)) Type.i64 else Type.u64; |
| 5596 | 5461 | // leave those value on top of the stack for '.select' |
| 5597 | const lhs_lsb = try func.load(lhs, Type.u64, 0); | |
| 5598 | const rhs_lsb = try func.load(rhs, Type.u64, 0); | |
| 5599 | _ = try func.cmp(lhs_lsb, rhs_lsb, Type.u64, op); | |
| 5600 | _ = try func.cmp(lhs_msb, rhs_msb, ty, op); | |
| 5601 | _ = try func.cmp(lhs_msb, rhs_msb, ty, .eq); | |
| 5602 | try func.addTag(.select); | |
| 5462 | const lhs_lsb = try cg.load(lhs, Type.u64, 0); | |
| 5463 | const rhs_lsb = try cg.load(rhs, Type.u64, 0); | |
| 5464 | _ = try cg.cmp(lhs_lsb, rhs_lsb, Type.u64, op); | |
| 5465 | _ = try cg.cmp(lhs_msb, rhs_msb, ty, op); | |
| 5466 | _ = try cg.cmp(lhs_msb, rhs_msb, ty, .eq); | |
| 5467 | try cg.addTag(.select); | |
| 5603 | 5468 | }, |
| 5604 | 5469 | } |
| 5605 | 5470 | |
| 5606 | 5471 | return .stack; |
| 5607 | 5472 | } |
| 5608 | 5473 | |
| 5609 | fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5610 | const pt = func.pt; | |
| 5474 | fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5475 | const pt = cg.pt; | |
| 5611 | 5476 | const zcu = pt.zcu; |
| 5612 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 5613 | const un_ty = func.typeOf(bin_op.lhs).childType(zcu); | |
| 5614 | const tag_ty = func.typeOf(bin_op.rhs); | |
| 5477 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 5478 | const un_ty = cg.typeOf(bin_op.lhs).childType(zcu); | |
| 5479 | const tag_ty = cg.typeOf(bin_op.rhs); | |
| 5615 | 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); | |
| 5619 | const new_tag = try func.resolveInst(bin_op.rhs); | |
| 5483 | const union_ptr = try cg.resolveInst(bin_op.lhs); | |
| 5484 | const new_tag = try cg.resolveInst(bin_op.rhs); | |
| 5620 | 5485 | if (layout.payload_size == 0) { |
| 5621 | try func.store(union_ptr, new_tag, tag_ty, 0); | |
| 5622 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | |
| 5486 | try cg.store(union_ptr, new_tag, tag_ty, 0); | |
| 5487 | return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | |
| 5623 | 5488 | } |
| 5624 | 5489 | |
| 5625 | 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 | 5492 | const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: { |
| 5628 | 5493 | break :blk @intCast(layout.payload_size); |
| 5629 | 5494 | } else 0; |
| 5630 | try func.store(union_ptr, new_tag, tag_ty, offset); | |
| 5631 | return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | |
| 5495 | try cg.store(union_ptr, new_tag, tag_ty, offset); | |
| 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 { | |
| 5635 | const zcu = func.pt.zcu; | |
| 5636 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5499 | fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5500 | const zcu = cg.pt.zcu; | |
| 5501 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5637 | 5502 | |
| 5638 | const un_ty = func.typeOf(ty_op.operand); | |
| 5639 | const tag_ty = func.typeOfIndex(inst); | |
| 5503 | const un_ty = cg.typeOf(ty_op.operand); | |
| 5504 | const tag_ty = cg.typeOfIndex(inst); | |
| 5640 | 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 | 5509 | // when the tag alignment is smaller than the payload, the field will be stored |
| 5645 | 5510 | // after the payload. |
| 5646 | 5511 | const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) |
| 5647 | 5512 | @intCast(layout.payload_size) |
| 5648 | 5513 | else |
| 5649 | 5514 | 0; |
| 5650 | const result = try func.load(operand, tag_ty, offset); | |
| 5651 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5515 | const result = try cg.load(operand, tag_ty, offset); | |
| 5516 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 5652 | 5517 | } |
| 5653 | 5518 | |
| 5654 | fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5655 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5519 | fn airFpext(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5520 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5656 | 5521 | |
| 5657 | const dest_ty = func.typeOfIndex(inst); | |
| 5658 | const operand = try func.resolveInst(ty_op.operand); | |
| 5659 | const result = try func.fpext(operand, func.typeOf(ty_op.operand), dest_ty); | |
| 5660 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5522 | const dest_ty = cg.typeOfIndex(inst); | |
| 5523 | const operand = try cg.resolveInst(ty_op.operand); | |
| 5524 | const result = try cg.fpext(operand, cg.typeOf(ty_op.operand), dest_ty); | |
| 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` | |
| 5664 | /// NOTE: Leaves the result on the stack | |
| 5665 | fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | |
| 5666 | const given_bits = given.floatBits(func.target.*); | |
| 5667 | const wanted_bits = wanted.floatBits(func.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; | |
| 5528 | /// Extends a float from a given `Type` to a larger wanted `Type`, leaving the | |
| 5529 | /// result on the stack. | |
| 5530 | fn fpext(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | |
| 5531 | const given_bits = given.floatBits(cg.target.*); | |
| 5532 | const wanted_bits = wanted.floatBits(cg.target.*); | |
| 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 { | |
| 5699 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5573 | fn airFptrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5574 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5700 | 5575 | |
| 5701 | const dest_ty = func.typeOfIndex(inst); | |
| 5702 | const operand = try func.resolveInst(ty_op.operand); | |
| 5703 | const result = try func.fptrunc(operand, func.typeOf(ty_op.operand), dest_ty); | |
| 5704 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5576 | const dest_ty = cg.typeOfIndex(inst); | |
| 5577 | const operand = try cg.resolveInst(ty_op.operand); | |
| 5578 | const result = try cg.fptrunc(operand, cg.typeOf(ty_op.operand), dest_ty); | |
| 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` | |
| 5708 | /// NOTE: The result value remains on the stack | |
| 5709 | fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | |
| 5710 | const given_bits = given.floatBits(func.target.*); | |
| 5711 | const wanted_bits = wanted.floatBits(func.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; | |
| 5582 | /// Truncates a float from a given `Type` to its wanted `Type`, leaving the | |
| 5583 | /// result on the stack. | |
| 5584 | fn fptrunc(cg: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | |
| 5585 | const given_bits = given.floatBits(cg.target.*); | |
| 5586 | const wanted_bits = wanted.floatBits(cg.target.*); | |
| 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 { | |
| 5738 | const pt = func.pt; | |
| 5739 | const zcu = pt.zcu; | |
| 5740 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5626 | fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5627 | const zcu = cg.pt.zcu; | |
| 5628 | const ty_op = cg.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 | 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 | 5634 | // set error-tag to '0' to annotate error union is non-error |
| 5747 | try func.store( | |
| 5635 | try cg.store( | |
| 5748 | 5636 | operand, |
| 5749 | 5637 | .{ .imm32 = 0 }, |
| 5750 | 5638 | Type.anyerror, |
| ... | ... | @@ -5753,63 +5641,60 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi |
| 5753 | 5641 | |
| 5754 | 5642 | const result = result: { |
| 5755 | 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 { | |
| 5765 | const pt = func.pt; | |
| 5766 | const zcu = pt.zcu; | |
| 5767 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5768 | const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; | |
| 5652 | fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5653 | const zcu = cg.pt.zcu; | |
| 5654 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5655 | const extra = cg.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 | 5658 | const parent_ty = ty_pl.ty.toType().childType(zcu); |
| 5772 | 5659 | const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu); |
| 5773 | 5660 | |
| 5774 | 5661 | const result = if (field_offset != 0) result: { |
| 5775 | const base = try func.buildPointerOffset(field_ptr, 0, .new); | |
| 5776 | try func.addLabel(.local_get, base.local.value); | |
| 5777 | try func.addImm32(@intCast(field_offset)); | |
| 5778 | try func.addTag(.i32_sub); | |
| 5779 | try func.addLabel(.local_set, base.local.value); | |
| 5662 | const base = try cg.buildPointerOffset(field_ptr, 0, .new); | |
| 5663 | try cg.addLocal(.local_get, base.local.value); | |
| 5664 | try cg.addImm32(@intCast(field_offset)); | |
| 5665 | try cg.addTag(.i32_sub); | |
| 5666 | try cg.addLocal(.local_set, base.local.value); | |
| 5780 | 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 { | |
| 5787 | const pt = func.pt; | |
| 5788 | const zcu = pt.zcu; | |
| 5673 | fn sliceOrArrayPtr(cg: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue { | |
| 5674 | const zcu = cg.pt.zcu; | |
| 5789 | 5675 | if (ptr_ty.isSlice(zcu)) { |
| 5790 | return func.slicePtr(ptr); | |
| 5676 | return cg.slicePtr(ptr); | |
| 5791 | 5677 | } else { |
| 5792 | 5678 | return ptr; |
| 5793 | 5679 | } |
| 5794 | 5680 | } |
| 5795 | 5681 | |
| 5796 | fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5797 | const pt = func.pt; | |
| 5798 | const zcu = pt.zcu; | |
| 5799 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 5800 | const dst = try func.resolveInst(bin_op.lhs); | |
| 5801 | const dst_ty = func.typeOf(bin_op.lhs); | |
| 5682 | fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5683 | const zcu = cg.pt.zcu; | |
| 5684 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 5685 | const dst = try cg.resolveInst(bin_op.lhs); | |
| 5686 | const dst_ty = cg.typeOf(bin_op.lhs); | |
| 5802 | 5687 | const ptr_elem_ty = dst_ty.childType(zcu); |
| 5803 | const src = try func.resolveInst(bin_op.rhs); | |
| 5804 | const src_ty = func.typeOf(bin_op.rhs); | |
| 5688 | const src = try cg.resolveInst(bin_op.rhs); | |
| 5689 | const src_ty = cg.typeOf(bin_op.rhs); | |
| 5805 | 5690 | const len = switch (dst_ty.ptrSize(zcu)) { |
| 5806 | 5691 | .Slice => blk: { |
| 5807 | const slice_len = try func.sliceLen(dst); | |
| 5692 | const slice_len = try cg.sliceLen(dst); | |
| 5808 | 5693 | if (ptr_elem_ty.abiSize(zcu) != 1) { |
| 5809 | try func.emitWValue(slice_len); | |
| 5810 | try func.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); | |
| 5811 | try func.addTag(.i32_mul); | |
| 5812 | try func.addLabel(.local_set, slice_len.local.value); | |
| 5694 | try cg.emitWValue(slice_len); | |
| 5695 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); | |
| 5696 | try cg.addTag(.i32_mul); | |
| 5697 | try cg.addLocal(.local_set, slice_len.local.value); | |
| 5813 | 5698 | } |
| 5814 | 5699 | break :blk slice_len; |
| 5815 | 5700 | }, |
| ... | ... | @@ -5818,96 +5703,94 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5818 | 5703 | }), |
| 5819 | 5704 | .C, .Many => unreachable, |
| 5820 | 5705 | }; |
| 5821 | const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty); | |
| 5822 | const src_ptr = try func.sliceOrArrayPtr(src, src_ty); | |
| 5823 | try func.memcpy(dst_ptr, src_ptr, len); | |
| 5706 | const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty); | |
| 5707 | const src_ptr = try cg.sliceOrArrayPtr(src, src_ty); | |
| 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 | 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 | 5716 | .wasm32 => .{ .imm32 = 0 }, |
| 5832 | 5717 | .wasm64 => .{ .imm64 = 0 }, |
| 5833 | else => unreachable, | |
| 5834 | 5718 | }, &.{}); |
| 5835 | 5719 | } |
| 5836 | 5720 | |
| 5837 | fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5838 | const pt = func.pt; | |
| 5721 | fn airPopcount(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5722 | const pt = cg.pt; | |
| 5839 | 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); | |
| 5843 | const op_ty = func.typeOf(ty_op.operand); | |
| 5726 | const operand = try cg.resolveInst(ty_op.operand); | |
| 5727 | const op_ty = cg.typeOf(ty_op.operand); | |
| 5844 | 5728 | |
| 5845 | 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 | 5733 | const int_info = op_ty.intInfo(zcu); |
| 5850 | 5734 | const bits = int_info.bits; |
| 5851 | 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 | 5739 | switch (wasm_bits) { |
| 5856 | 5740 | 32 => { |
| 5857 | try func.emitWValue(operand); | |
| 5741 | try cg.emitWValue(operand); | |
| 5858 | 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 | 5747 | 64 => { |
| 5864 | try func.emitWValue(operand); | |
| 5748 | try cg.emitWValue(operand); | |
| 5865 | 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); | |
| 5869 | try func.addTag(.i32_wrap_i64); | |
| 5870 | try func.emitWValue(operand); | |
| 5752 | try cg.addTag(.i64_popcnt); | |
| 5753 | try cg.addTag(.i32_wrap_i64); | |
| 5754 | try cg.emitWValue(operand); | |
| 5871 | 5755 | }, |
| 5872 | 5756 | 128 => { |
| 5873 | _ = try func.load(operand, Type.u64, 0); | |
| 5874 | try func.addTag(.i64_popcnt); | |
| 5875 | _ = try func.load(operand, Type.u64, 8); | |
| 5757 | _ = try cg.load(operand, Type.u64, 0); | |
| 5758 | try cg.addTag(.i64_popcnt); | |
| 5759 | _ = try cg.load(operand, Type.u64, 8); | |
| 5876 | 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); | |
| 5880 | try func.addTag(.i64_add); | |
| 5881 | try func.addTag(.i32_wrap_i64); | |
| 5763 | try cg.addTag(.i64_popcnt); | |
| 5764 | try cg.addTag(.i64_add); | |
| 5765 | try cg.addTag(.i32_wrap_i64); | |
| 5882 | 5766 | }, |
| 5883 | 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 { | |
| 5890 | const pt = func.pt; | |
| 5891 | const zcu = pt.zcu; | |
| 5892 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5773 | fn airBitReverse(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5774 | const zcu = cg.pt.zcu; | |
| 5775 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5893 | 5776 | |
| 5894 | const operand = try func.resolveInst(ty_op.operand); | |
| 5895 | const ty = func.typeOf(ty_op.operand); | |
| 5777 | const operand = try cg.resolveInst(ty_op.operand); | |
| 5778 | const ty = cg.typeOf(ty_op.operand); | |
| 5896 | 5779 | |
| 5897 | 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 | 5784 | const int_info = ty.intInfo(zcu); |
| 5902 | 5785 | const bits = int_info.bits; |
| 5903 | 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 | 5790 | switch (wasm_bits) { |
| 5908 | 5791 | 32 => { |
| 5909 | const intrin_ret = try func.callIntrinsic( | |
| 5910 | "__bitreversesi2", | |
| 5792 | const intrin_ret = try cg.callIntrinsic( | |
| 5793 | .__bitreversesi2, | |
| 5911 | 5794 | &.{.u32_type}, |
| 5912 | 5795 | Type.u32, |
| 5913 | 5796 | &.{operand}, |
| ... | ... | @@ -5915,12 +5798,12 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5915 | 5798 | const result = if (bits == 32) |
| 5916 | 5799 | intrin_ret |
| 5917 | 5800 | else |
| 5918 | try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr); | |
| 5919 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5801 | try cg.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr); | |
| 5802 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 5920 | 5803 | }, |
| 5921 | 5804 | 64 => { |
| 5922 | const intrin_ret = try func.callIntrinsic( | |
| 5923 | "__bitreversedi2", | |
| 5805 | const intrin_ret = try cg.callIntrinsic( | |
| 5806 | .__bitreversedi2, | |
| 5924 | 5807 | &.{.u64_type}, |
| 5925 | 5808 | Type.u64, |
| 5926 | 5809 | &.{operand}, |
| ... | ... | @@ -5928,68 +5811,63 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5928 | 5811 | const result = if (bits == 64) |
| 5929 | 5812 | intrin_ret |
| 5930 | 5813 | else |
| 5931 | try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr); | |
| 5932 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5814 | try cg.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr); | |
| 5815 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 5933 | 5816 | }, |
| 5934 | 5817 | 128 => { |
| 5935 | const result = try func.allocStack(ty); | |
| 5818 | const result = try cg.allocStack(ty); | |
| 5936 | 5819 | |
| 5937 | try func.emitWValue(result); | |
| 5938 | const first_half = try func.load(operand, Type.u64, 8); | |
| 5939 | const intrin_ret_first = try func.callIntrinsic( | |
| 5940 | "__bitreversedi2", | |
| 5820 | try cg.emitWValue(result); | |
| 5821 | const first_half = try cg.load(operand, Type.u64, 8); | |
| 5822 | const intrin_ret_first = try cg.callIntrinsic( | |
| 5823 | .__bitreversedi2, | |
| 5941 | 5824 | &.{.u64_type}, |
| 5942 | 5825 | Type.u64, |
| 5943 | 5826 | &.{first_half}, |
| 5944 | 5827 | ); |
| 5945 | try func.emitWValue(intrin_ret_first); | |
| 5828 | try cg.emitWValue(intrin_ret_first); | |
| 5946 | 5829 | if (bits < 128) { |
| 5947 | try func.emitWValue(.{ .imm64 = 128 - bits }); | |
| 5948 | try func.addTag(.i64_shr_u); | |
| 5830 | try cg.emitWValue(.{ .imm64 = 128 - bits }); | |
| 5831 | try cg.addTag(.i64_shr_u); | |
| 5949 | 5832 | } |
| 5950 | try func.emitWValue(result); | |
| 5951 | const second_half = try func.load(operand, Type.u64, 0); | |
| 5952 | const intrin_ret_second = try func.callIntrinsic( | |
| 5953 | "__bitreversedi2", | |
| 5833 | try cg.emitWValue(result); | |
| 5834 | const second_half = try cg.load(operand, Type.u64, 0); | |
| 5835 | const intrin_ret_second = try cg.callIntrinsic( | |
| 5836 | .__bitreversedi2, | |
| 5954 | 5837 | &.{.u64_type}, |
| 5955 | 5838 | Type.u64, |
| 5956 | 5839 | &.{second_half}, |
| 5957 | 5840 | ); |
| 5958 | try func.emitWValue(intrin_ret_second); | |
| 5841 | try cg.emitWValue(intrin_ret_second); | |
| 5959 | 5842 | if (bits == 128) { |
| 5960 | try func.store(.stack, .stack, Type.u64, result.offset() + 8); | |
| 5961 | try func.store(.stack, .stack, Type.u64, result.offset()); | |
| 5843 | try cg.store(.stack, .stack, Type.u64, result.offset() + 8); | |
| 5844 | try cg.store(.stack, .stack, Type.u64, result.offset()); | |
| 5962 | 5845 | } else { |
| 5963 | var tmp = try func.allocLocal(Type.u64); | |
| 5964 | defer tmp.free(func); | |
| 5965 | try func.addLabel(.local_tee, tmp.local.value); | |
| 5966 | try func.emitWValue(.{ .imm64 = 128 - bits }); | |
| 5846 | var tmp = try cg.allocLocal(Type.u64); | |
| 5847 | defer tmp.free(cg); | |
| 5848 | try cg.addLocal(.local_tee, tmp.local.value); | |
| 5849 | try cg.emitWValue(.{ .imm64 = 128 - bits }); | |
| 5967 | 5850 | if (ty.isSignedInt(zcu)) { |
| 5968 | try func.addTag(.i64_shr_s); | |
| 5851 | try cg.addTag(.i64_shr_s); | |
| 5969 | 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); | |
| 5973 | try func.addLabel(.local_get, tmp.local.value); | |
| 5974 | try func.emitWValue(.{ .imm64 = bits - 64 }); | |
| 5975 | try func.addTag(.i64_shl); | |
| 5976 | try func.addTag(.i64_or); | |
| 5977 | try func.store(.stack, .stack, Type.u64, result.offset()); | |
| 5855 | try cg.store(.stack, .stack, Type.u64, result.offset() + 8); | |
| 5856 | try cg.addLocal(.local_get, tmp.local.value); | |
| 5857 | try cg.emitWValue(.{ .imm64 = bits - 64 }); | |
| 5858 | try cg.addTag(.i64_shl); | |
| 5859 | try cg.addTag(.i64_or); | |
| 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 | 5864 | else => unreachable, |
| 5982 | 5865 | } |
| 5983 | 5866 | } |
| 5984 | 5867 | |
| 5985 | fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5986 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 5987 | ||
| 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 | // | |
| 5868 | fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5869 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 5870 | const operand = try cg.resolveInst(un_op); | |
| 5993 | 5871 | // Each entry to this table is a slice (ptr+len). |
| 5994 | 5872 | // The operand in this instruction represents the index within this table. |
| 5995 | 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 | 5875 | // |
| 5998 | 5876 | // As the names are global and the slice elements are constant, we do not have |
| 5999 | 5877 | // to make a copy of the ptr+value but can point towards them directly. |
| 6000 | const pt = func.pt; | |
| 6001 | const error_table_symbol = try func.bin_file.getErrorTableSymbol(pt); | |
| 5878 | const pt = cg.pt; | |
| 6002 | 5879 | const name_ty = Type.slice_const_u8_sentinel_0; |
| 6003 | 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 | |
| 6006 | try func.emitWValue(error_name_value); | |
| 6007 | try func.emitWValue(operand); | |
| 6008 | switch (func.arch()) { | |
| 5882 | cg.wasm.error_name_table_ref_count += 1; | |
| 5883 | ||
| 5884 | // Lowers to a i32.const or i64.const with the error table memory address. | |
| 5885 | try cg.addTag(.error_name_table_ref); | |
| 5886 | try cg.emitWValue(operand); | |
| 5887 | switch (cg.ptr_size) { | |
| 6009 | 5888 | .wasm32 => { |
| 6010 | try func.addImm32(@intCast(abi_size)); | |
| 6011 | try func.addTag(.i32_mul); | |
| 6012 | try func.addTag(.i32_add); | |
| 5889 | try cg.addImm32(@intCast(abi_size)); | |
| 5890 | try cg.addTag(.i32_mul); | |
| 5891 | try cg.addTag(.i32_add); | |
| 6013 | 5892 | }, |
| 6014 | 5893 | .wasm64 => { |
| 6015 | try func.addImm64(abi_size); | |
| 6016 | try func.addTag(.i64_mul); | |
| 6017 | try func.addTag(.i64_add); | |
| 5894 | try cg.addImm64(abi_size); | |
| 5895 | try cg.addTag(.i64_mul); | |
| 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 { | |
| 6026 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 6027 | const slice_ptr = try func.resolveInst(ty_op.operand); | |
| 6028 | const result = try func.buildPointerOffset(slice_ptr, offset, .new); | |
| 6029 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 5903 | fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void { | |
| 5904 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 5905 | const slice_ptr = try cg.resolveInst(ty_op.operand); | |
| 5906 | const result = try cg.buildPointerOffset(slice_ptr, offset, .new); | |
| 5907 | return cg.finishAir(inst, result, &.{ty_op.operand}); | |
| 6030 | 5908 | } |
| 6031 | 5909 | |
| 6032 | 5910 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits |
| 6033 | fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue { | |
| 6034 | const zcu = func.bin_file.base.comp.zcu.?; | |
| 5911 | fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue { | |
| 5912 | const zcu = cg.wasm.base.comp.zcu.?; | |
| 6035 | 5913 | const int_info = ty.intInfo(zcu); |
| 6036 | 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 | 5917 | switch (wasm_bits) { |
| 6040 | 5918 | 32 => return .{ .imm32 = 0 }, |
| 6041 | 5919 | 64 => return .{ .imm64 = 0 }, |
| 6042 | 5920 | 128 => { |
| 6043 | const result = try func.allocStack(ty); | |
| 6044 | try func.store(result, .{ .imm64 = 0 }, Type.u64, 0); | |
| 6045 | try func.store(result, .{ .imm64 = 0 }, Type.u64, 8); | |
| 5921 | const result = try cg.allocStack(ty); | |
| 5922 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); | |
| 5923 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); | |
| 6046 | 5924 | return result; |
| 6047 | 5925 | }, |
| 6048 | 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 | 5931 | assert(op == .add or op == .sub); |
| 6054 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6055 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 5932 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5933 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 6056 | 5934 | |
| 6057 | const lhs = try func.resolveInst(extra.lhs); | |
| 6058 | const rhs = try func.resolveInst(extra.rhs); | |
| 6059 | const ty = func.typeOf(extra.lhs); | |
| 6060 | const pt = func.pt; | |
| 5935 | const lhs = try cg.resolveInst(extra.lhs); | |
| 5936 | const rhs = try cg.resolveInst(extra.rhs); | |
| 5937 | const ty = cg.typeOf(extra.lhs); | |
| 5938 | const pt = cg.pt; | |
| 6061 | 5939 | const zcu = pt.zcu; |
| 6062 | 5940 | |
| 6063 | 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 | 5945 | const int_info = ty.intInfo(zcu); |
| 6068 | 5946 | const is_signed = int_info.signedness == .signed; |
| 6069 | 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); | |
| 6074 | var op_tmp = try op_result.toLocal(func, ty); | |
| 6075 | defer op_tmp.free(func); | |
| 5951 | const op_result = try cg.wrapBinOp(lhs, rhs, ty, op); | |
| 5952 | var op_tmp = try op_result.toLocal(cg, ty); | |
| 5953 | defer op_tmp.free(cg); | |
| 6076 | 5954 | |
| 6077 | 5955 | const cmp_op: std.math.CompareOperator = switch (op) { |
| 6078 | 5956 | .add => .lt, |
| ... | ... | @@ -6080,40 +5958,40 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro |
| 6080 | 5958 | else => unreachable, |
| 6081 | 5959 | }; |
| 6082 | 5960 | const overflow_bit = if (is_signed) blk: { |
| 6083 | const zero = try intZeroValue(func, ty); | |
| 6084 | const rhs_is_neg = try func.cmp(rhs, zero, ty, .lt); | |
| 6085 | const overflow_cmp = try func.cmp(op_tmp, lhs, ty, cmp_op); | |
| 6086 | break :blk try func.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq); | |
| 6087 | } else try func.cmp(op_tmp, lhs, ty, cmp_op); | |
| 6088 | var bit_tmp = try overflow_bit.toLocal(func, Type.u1); | |
| 6089 | defer bit_tmp.free(func); | |
| 6090 | ||
| 6091 | const result = try func.allocStack(func.typeOfIndex(inst)); | |
| 5961 | const zero = try intZeroValue(cg, ty); | |
| 5962 | const rhs_is_neg = try cg.cmp(rhs, zero, ty, .lt); | |
| 5963 | const overflow_cmp = try cg.cmp(op_tmp, lhs, ty, cmp_op); | |
| 5964 | break :blk try cg.cmp(rhs_is_neg, overflow_cmp, Type.u1, .neq); | |
| 5965 | } else try cg.cmp(op_tmp, lhs, ty, cmp_op); | |
| 5966 | var bit_tmp = try overflow_bit.toLocal(cg, Type.u1); | |
| 5967 | defer bit_tmp.free(cg); | |
| 5968 | ||
| 5969 | const result = try cg.allocStack(cg.typeOfIndex(inst)); | |
| 6092 | 5970 | const offset: u32 = @intCast(ty.abiSize(zcu)); |
| 6093 | try func.store(result, op_tmp, ty, 0); | |
| 6094 | try func.store(result, bit_tmp, Type.u1, offset); | |
| 5971 | try cg.store(result, op_tmp, ty, 0); | |
| 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 { | |
| 6100 | const pt = func.pt; | |
| 5977 | fn airShlWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 5978 | const pt = cg.pt; | |
| 6101 | 5979 | const zcu = pt.zcu; |
| 6102 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6103 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 5980 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 5981 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 6104 | 5982 | |
| 6105 | const lhs = try func.resolveInst(extra.lhs); | |
| 6106 | const rhs = try func.resolveInst(extra.rhs); | |
| 6107 | const ty = func.typeOf(extra.lhs); | |
| 6108 | const rhs_ty = func.typeOf(extra.rhs); | |
| 5983 | const lhs = try cg.resolveInst(extra.lhs); | |
| 5984 | const rhs = try cg.resolveInst(extra.rhs); | |
| 5985 | const ty = cg.typeOf(extra.lhs); | |
| 5986 | const rhs_ty = cg.typeOf(extra.rhs); | |
| 6109 | 5987 | |
| 6110 | 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 | 5992 | const int_info = ty.intInfo(zcu); |
| 6115 | 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 | 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 | 5999 | const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(zcu).bits).?; |
| 6122 | 6000 | // If wasm_bits == 128, compiler-rt expects i32 for shift |
| 6123 | 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); | |
| 6125 | break :blk try rhs_casted.toLocal(func, ty); | |
| 6002 | const rhs_casted = try cg.intcast(rhs, rhs_ty, ty); | |
| 6003 | break :blk try rhs_casted.toLocal(cg, ty); | |
| 6126 | 6004 | } else rhs; |
| 6127 | 6005 | |
| 6128 | var shl = try (try func.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(func, ty); | |
| 6129 | defer shl.free(func); | |
| 6006 | var shl = try (try cg.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(cg, ty); | |
| 6007 | defer shl.free(cg); | |
| 6130 | 6008 | |
| 6131 | 6009 | const overflow_bit = blk: { |
| 6132 | const shr = try func.binOp(shl, rhs_final, ty, .shr); | |
| 6133 | break :blk try func.cmp(shr, lhs, ty, .neq); | |
| 6010 | const shr = try cg.binOp(shl, rhs_final, ty, .shr); | |
| 6011 | break :blk try cg.cmp(shr, lhs, ty, .neq); | |
| 6134 | 6012 | }; |
| 6135 | var overflow_local = try overflow_bit.toLocal(func, Type.u1); | |
| 6136 | defer overflow_local.free(func); | |
| 6013 | var overflow_local = try overflow_bit.toLocal(cg, Type.u1); | |
| 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 | 6017 | const offset: u32 = @intCast(ty.abiSize(zcu)); |
| 6140 | try func.store(result, shl, ty, 0); | |
| 6141 | try func.store(result, overflow_local, Type.u1, offset); | |
| 6018 | try cg.store(result, shl, ty, 0); | |
| 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 { | |
| 6147 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6148 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 6024 | fn airMulWithOverflow(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6025 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6026 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; | |
| 6149 | 6027 | |
| 6150 | const lhs = try func.resolveInst(extra.lhs); | |
| 6151 | const rhs = try func.resolveInst(extra.rhs); | |
| 6152 | const ty = func.typeOf(extra.lhs); | |
| 6153 | const pt = func.pt; | |
| 6028 | const lhs = try cg.resolveInst(extra.lhs); | |
| 6029 | const rhs = try cg.resolveInst(extra.rhs); | |
| 6030 | const ty = cg.typeOf(extra.lhs); | |
| 6031 | const pt = cg.pt; | |
| 6154 | 6032 | const zcu = pt.zcu; |
| 6155 | 6033 | |
| 6156 | 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 | 6038 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 6161 | 6039 | // we only need to update it if an overflow (or underflow) occurred. |
| 6162 | var overflow_bit = try func.ensureAllocLocal(Type.u1); | |
| 6163 | defer overflow_bit.free(func); | |
| 6040 | var overflow_bit = try cg.ensureAllocLocal(Type.u1); | |
| 6041 | defer overflow_bit.free(cg); | |
| 6164 | 6042 | |
| 6165 | 6043 | const int_info = ty.intInfo(zcu); |
| 6166 | 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 | 6048 | const zero: WValue = switch (wasm_bits) { |
| ... | ... | @@ -6176,248 +6054,250 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6176 | 6054 | // for 32 bit integers we upcast it to a 64bit integer |
| 6177 | 6055 | const mul = if (wasm_bits == 32) blk: { |
| 6178 | 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); | |
| 6180 | const rhs_upcast = try func.intcast(rhs, ty, new_ty); | |
| 6181 | const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty); | |
| 6182 | const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty); | |
| 6183 | const res_upcast = try func.intcast(res, ty, new_ty); | |
| 6184 | _ = try func.cmp(res_upcast, bin_op, new_ty, .neq); | |
| 6185 | try func.addLabel(.local_set, overflow_bit.local.value); | |
| 6057 | const lhs_upcast = try cg.intcast(lhs, ty, new_ty); | |
| 6058 | const rhs_upcast = try cg.intcast(rhs, ty, new_ty); | |
| 6059 | const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty); | |
| 6060 | const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty); | |
| 6061 | const res_upcast = try cg.intcast(res, ty, new_ty); | |
| 6062 | _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq); | |
| 6063 | try cg.addLocal(.local_set, overflow_bit.local.value); | |
| 6186 | 6064 | break :blk res; |
| 6187 | 6065 | } else if (wasm_bits == 64) blk: { |
| 6188 | 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); | |
| 6190 | const rhs_upcast = try func.intcast(rhs, ty, new_ty); | |
| 6191 | const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty); | |
| 6192 | const res = try (try func.trunc(bin_op, ty, new_ty)).toLocal(func, ty); | |
| 6193 | const res_upcast = try func.intcast(res, ty, new_ty); | |
| 6194 | _ = try func.cmp(res_upcast, bin_op, new_ty, .neq); | |
| 6195 | try func.addLabel(.local_set, overflow_bit.local.value); | |
| 6067 | const lhs_upcast = try cg.intcast(lhs, ty, new_ty); | |
| 6068 | const rhs_upcast = try cg.intcast(rhs, ty, new_ty); | |
| 6069 | const bin_op = try (try cg.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(cg, new_ty); | |
| 6070 | const res = try (try cg.trunc(bin_op, ty, new_ty)).toLocal(cg, ty); | |
| 6071 | const res_upcast = try cg.intcast(res, ty, new_ty); | |
| 6072 | _ = try cg.cmp(res_upcast, bin_op, new_ty, .neq); | |
| 6073 | try cg.addLocal(.local_set, overflow_bit.local.value); | |
| 6196 | 6074 | break :blk res; |
| 6197 | 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); | |
| 6199 | defer lhs_lsb.free(func); | |
| 6200 | var lhs_msb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64); | |
| 6201 | defer lhs_msb.free(func); | |
| 6202 | var rhs_lsb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); | |
| 6203 | defer rhs_lsb.free(func); | |
| 6204 | var rhs_msb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64); | |
| 6205 | defer rhs_msb.free(func); | |
| 6206 | ||
| 6207 | const cross_1 = try func.callIntrinsic( | |
| 6208 | "__multi3", | |
| 6076 | var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 6077 | defer lhs_lsb.free(cg); | |
| 6078 | var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 6079 | defer lhs_msb.free(cg); | |
| 6080 | var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 6081 | defer rhs_lsb.free(cg); | |
| 6082 | var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 6083 | defer rhs_msb.free(cg); | |
| 6084 | ||
| 6085 | const cross_1 = try cg.callIntrinsic( | |
| 6086 | .__multi3, | |
| 6209 | 6087 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6210 | 6088 | Type.i128, |
| 6211 | 6089 | &.{ lhs_msb, zero, rhs_lsb, zero }, |
| 6212 | 6090 | ); |
| 6213 | const cross_2 = try func.callIntrinsic( | |
| 6214 | "__multi3", | |
| 6091 | const cross_2 = try cg.callIntrinsic( | |
| 6092 | .__multi3, | |
| 6215 | 6093 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6216 | 6094 | Type.i128, |
| 6217 | 6095 | &.{ rhs_msb, zero, lhs_lsb, zero }, |
| 6218 | 6096 | ); |
| 6219 | const mul_lsb = try func.callIntrinsic( | |
| 6220 | "__multi3", | |
| 6097 | const mul_lsb = try cg.callIntrinsic( | |
| 6098 | .__multi3, | |
| 6221 | 6099 | &[_]InternPool.Index{.i64_type} ** 4, |
| 6222 | 6100 | Type.i128, |
| 6223 | 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); | |
| 6227 | const lhs_msb_not_zero = try func.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"); | |
| 6229 | const cross_1_msb = try func.load(cross_1, Type.u64, 8); | |
| 6230 | const cross_1_msb_not_zero = try func.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"); | |
| 6232 | const cross_2_msb = try func.load(cross_2, Type.u64, 8); | |
| 6233 | const cross_2_msb_not_zero = try func.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"); | |
| 6235 | ||
| 6236 | const cross_1_lsb = try func.load(cross_1, Type.u64, 0); | |
| 6237 | const cross_2_lsb = try func.load(cross_2, Type.u64, 0); | |
| 6238 | const cross_add = try func.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add); | |
| 6239 | ||
| 6240 | var mul_lsb_msb = try (try func.load(mul_lsb, Type.u64, 8)).toLocal(func, Type.u64); | |
| 6241 | defer mul_lsb_msb.free(func); | |
| 6242 | var all_add = try (try func.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(func, Type.u64); | |
| 6243 | defer all_add.free(func); | |
| 6244 | const add_overflow = try func.cmp(all_add, mul_lsb_msb, Type.u64, .lt); | |
| 6104 | const rhs_msb_not_zero = try cg.cmp(rhs_msb, zero, Type.u64, .neq); | |
| 6105 | const lhs_msb_not_zero = try cg.cmp(lhs_msb, zero, Type.u64, .neq); | |
| 6106 | const both_msb_not_zero = try cg.binOp(rhs_msb_not_zero, lhs_msb_not_zero, Type.bool, .@"and"); | |
| 6107 | const cross_1_msb = try cg.load(cross_1, Type.u64, 8); | |
| 6108 | const cross_1_msb_not_zero = try cg.cmp(cross_1_msb, zero, Type.u64, .neq); | |
| 6109 | const cond_1 = try cg.binOp(both_msb_not_zero, cross_1_msb_not_zero, Type.bool, .@"or"); | |
| 6110 | const cross_2_msb = try cg.load(cross_2, Type.u64, 8); | |
| 6111 | const cross_2_msb_not_zero = try cg.cmp(cross_2_msb, zero, Type.u64, .neq); | |
| 6112 | const cond_2 = try cg.binOp(cond_1, cross_2_msb_not_zero, Type.bool, .@"or"); | |
| 6113 | ||
| 6114 | const cross_1_lsb = try cg.load(cross_1, Type.u64, 0); | |
| 6115 | const cross_2_lsb = try cg.load(cross_2, Type.u64, 0); | |
| 6116 | const cross_add = try cg.binOp(cross_1_lsb, cross_2_lsb, Type.u64, .add); | |
| 6117 | ||
| 6118 | var mul_lsb_msb = try (try cg.load(mul_lsb, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 6119 | defer mul_lsb_msb.free(cg); | |
| 6120 | var all_add = try (try cg.binOp(cross_add, mul_lsb_msb, Type.u64, .add)).toLocal(cg, Type.u64); | |
| 6121 | defer all_add.free(cg); | |
| 6122 | const add_overflow = try cg.cmp(all_add, mul_lsb_msb, Type.u64, .lt); | |
| 6245 | 6123 | |
| 6246 | 6124 | // result for overflow bit |
| 6247 | _ = try func.binOp(cond_2, add_overflow, Type.bool, .@"or"); | |
| 6248 | try func.addLabel(.local_set, overflow_bit.local.value); | |
| 6249 | ||
| 6250 | const tmp_result = try func.allocStack(Type.u128); | |
| 6251 | try func.emitWValue(tmp_result); | |
| 6252 | const mul_lsb_lsb = try func.load(mul_lsb, Type.u64, 0); | |
| 6253 | try func.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset()); | |
| 6254 | try func.store(tmp_result, all_add, Type.u64, 8); | |
| 6125 | _ = try cg.binOp(cond_2, add_overflow, Type.bool, .@"or"); | |
| 6126 | try cg.addLocal(.local_set, overflow_bit.local.value); | |
| 6127 | ||
| 6128 | const tmp_result = try cg.allocStack(Type.u128); | |
| 6129 | try cg.emitWValue(tmp_result); | |
| 6130 | const mul_lsb_lsb = try cg.load(mul_lsb, Type.u64, 0); | |
| 6131 | try cg.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset()); | |
| 6132 | try cg.store(tmp_result, all_add, Type.u64, 8); | |
| 6255 | 6133 | break :blk tmp_result; |
| 6256 | 6134 | } else if (int_info.bits == 128 and int_info.signedness == .signed) blk: { |
| 6257 | const overflow_ret = try func.allocStack(Type.i32); | |
| 6258 | const res = try func.callIntrinsic( | |
| 6259 | "__muloti4", | |
| 6135 | const overflow_ret = try cg.allocStack(Type.i32); | |
| 6136 | const res = try cg.callIntrinsic( | |
| 6137 | .__muloti4, | |
| 6260 | 6138 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, |
| 6261 | 6139 | Type.i128, |
| 6262 | 6140 | &.{ lhs, rhs, overflow_ret }, |
| 6263 | 6141 | ); |
| 6264 | _ = try func.load(overflow_ret, Type.i32, 0); | |
| 6265 | try func.addLabel(.local_set, overflow_bit.local.value); | |
| 6142 | _ = try cg.load(overflow_ret, Type.i32, 0); | |
| 6143 | try cg.addLocal(.local_set, overflow_bit.local.value); | |
| 6266 | 6144 | break :blk res; |
| 6267 | } else return func.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)}); | |
| 6268 | var bin_op_local = try mul.toLocal(func, ty); | |
| 6269 | defer bin_op_local.free(func); | |
| 6145 | } else return cg.fail("TODO: @mulWithOverflow for {}", .{ty.fmt(pt)}); | |
| 6146 | var bin_op_local = try mul.toLocal(cg, ty); | |
| 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 | 6150 | const offset: u32 = @intCast(ty.abiSize(zcu)); |
| 6273 | try func.store(result, bin_op_local, ty, 0); | |
| 6274 | try func.store(result, overflow_bit, Type.u1, offset); | |
| 6151 | try cg.store(result, bin_op_local, ty, 0); | |
| 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 { | |
| 6280 | assert(op == .max or op == .min); | |
| 6281 | const pt = func.pt; | |
| 6282 | const zcu = pt.zcu; | |
| 6283 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6157 | fn airMaxMin( | |
| 6158 | cg: *CodeGen, | |
| 6159 | inst: Air.Inst.Index, | |
| 6160 | op: enum { fmax, fmin }, | |
| 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 | 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 | 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); | |
| 6295 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6175 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6176 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6296 | 6177 | |
| 6297 | 6178 | if (ty.zigTypeTag(zcu) == .float) { |
| 6298 | var fn_name_buf: [64]u8 = undefined; | |
| 6299 | const float_bits = ty.floatBits(func.target.*); | |
| 6300 | const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{ | |
| 6301 | target_util.libcFloatPrefix(float_bits), | |
| 6302 | @tagName(op), | |
| 6303 | target_util.libcFloatSuffix(float_bits), | |
| 6304 | }) catch unreachable; | |
| 6305 | const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); | |
| 6306 | try func.lowerToStack(result); | |
| 6179 | const intrinsic = switch (op) { | |
| 6180 | inline .fmin, .fmax => |ct_op| switch (ty.floatBits(cg.target.*)) { | |
| 6181 | inline 16, 32, 64, 80, 128 => |bits| @field( | |
| 6182 | Mir.Intrinsic, | |
| 6183 | libcFloatPrefix(bits) ++ @tagName(ct_op) ++ libcFloatSuffix(bits), | |
| 6184 | ), | |
| 6185 | else => unreachable, | |
| 6186 | }, | |
| 6187 | }; | |
| 6188 | const result = try cg.callIntrinsic(intrinsic, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs }); | |
| 6189 | try cg.lowerToStack(result); | |
| 6307 | 6190 | } else { |
| 6308 | 6191 | // operands to select from |
| 6309 | try func.lowerToStack(lhs); | |
| 6310 | try func.lowerToStack(rhs); | |
| 6311 | _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); | |
| 6192 | try cg.lowerToStack(lhs); | |
| 6193 | try cg.lowerToStack(rhs); | |
| 6194 | _ = try cg.cmp(lhs, rhs, ty, cmp_op); | |
| 6312 | 6195 | |
| 6313 | 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 { | |
| 6321 | const pt = func.pt; | |
| 6322 | const zcu = pt.zcu; | |
| 6323 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 6324 | const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data; | |
| 6203 | fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6204 | const zcu = cg.pt.zcu; | |
| 6205 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 6206 | const bin_op = cg.air.extraData(Air.Bin, pl_op.payload).data; | |
| 6325 | 6207 | |
| 6326 | const ty = func.typeOfIndex(inst); | |
| 6208 | const ty = cg.typeOfIndex(inst); | |
| 6327 | 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); | |
| 6332 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6333 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6213 | const addend = try cg.resolveInst(pl_op.operand); | |
| 6214 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6215 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6334 | 6216 | |
| 6335 | const result = if (ty.floatBits(func.target.*) == 16) fl_result: { | |
| 6336 | const rhs_ext = try func.fpext(rhs, ty, Type.f32); | |
| 6337 | const lhs_ext = try func.fpext(lhs, ty, Type.f32); | |
| 6338 | const addend_ext = try func.fpext(addend, ty, Type.f32); | |
| 6217 | const result = if (ty.floatBits(cg.target.*) == 16) fl_result: { | |
| 6218 | const rhs_ext = try cg.fpext(rhs, ty, Type.f32); | |
| 6219 | const lhs_ext = try cg.fpext(lhs, ty, Type.f32); | |
| 6220 | const addend_ext = try cg.fpext(addend, ty, Type.f32); | |
| 6339 | 6221 | // call to compiler-rt `fn fmaf(f32, f32, f32) f32` |
| 6340 | const result = try func.callIntrinsic( | |
| 6341 | "fmaf", | |
| 6222 | const result = try cg.callIntrinsic( | |
| 6223 | .fmaf, | |
| 6342 | 6224 | &.{ .f32_type, .f32_type, .f32_type }, |
| 6343 | 6225 | Type.f32, |
| 6344 | 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 | 6229 | } else result: { |
| 6348 | const mul_result = try func.binOp(lhs, rhs, ty, .mul); | |
| 6349 | break :result try func.binOp(mul_result, addend, ty, .add); | |
| 6230 | const mul_result = try cg.binOp(lhs, rhs, ty, .mul); | |
| 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 { | |
| 6356 | const pt = func.pt; | |
| 6357 | const zcu = pt.zcu; | |
| 6358 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 6237 | fn airClz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6238 | const zcu = cg.pt.zcu; | |
| 6239 | const ty_op = cg.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 | 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 | 6247 | const int_info = ty.intInfo(zcu); |
| 6367 | 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 | 6252 | switch (wasm_bits) { |
| 6372 | 6253 | 32 => { |
| 6373 | try func.emitWValue(operand); | |
| 6374 | try func.addTag(.i32_clz); | |
| 6254 | try cg.emitWValue(operand); | |
| 6255 | try cg.addTag(.i32_clz); | |
| 6375 | 6256 | }, |
| 6376 | 6257 | 64 => { |
| 6377 | try func.emitWValue(operand); | |
| 6378 | try func.addTag(.i64_clz); | |
| 6379 | try func.addTag(.i32_wrap_i64); | |
| 6258 | try cg.emitWValue(operand); | |
| 6259 | try cg.addTag(.i64_clz); | |
| 6260 | try cg.addTag(.i32_wrap_i64); | |
| 6380 | 6261 | }, |
| 6381 | 6262 | 128 => { |
| 6382 | var msb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64); | |
| 6383 | defer msb.free(func); | |
| 6384 | ||
| 6385 | try func.emitWValue(msb); | |
| 6386 | try func.addTag(.i64_clz); | |
| 6387 | _ = try func.load(operand, Type.u64, 0); | |
| 6388 | try func.addTag(.i64_clz); | |
| 6389 | try func.emitWValue(.{ .imm64 = 64 }); | |
| 6390 | try func.addTag(.i64_add); | |
| 6391 | _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); | |
| 6392 | try func.addTag(.select); | |
| 6393 | try func.addTag(.i32_wrap_i64); | |
| 6263 | var msb = try (try cg.load(operand, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 6264 | defer msb.free(cg); | |
| 6265 | ||
| 6266 | try cg.emitWValue(msb); | |
| 6267 | try cg.addTag(.i64_clz); | |
| 6268 | _ = try cg.load(operand, Type.u64, 0); | |
| 6269 | try cg.addTag(.i64_clz); | |
| 6270 | try cg.emitWValue(.{ .imm64 = 64 }); | |
| 6271 | try cg.addTag(.i64_add); | |
| 6272 | _ = try cg.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); | |
| 6273 | try cg.addTag(.select); | |
| 6274 | try cg.addTag(.i32_wrap_i64); | |
| 6394 | 6275 | }, |
| 6395 | 6276 | else => unreachable, |
| 6396 | 6277 | } |
| 6397 | 6278 | |
| 6398 | 6279 | if (wasm_bits != int_info.bits) { |
| 6399 | try func.emitWValue(.{ .imm32 = wasm_bits - int_info.bits }); | |
| 6400 | try func.addTag(.i32_sub); | |
| 6280 | try cg.emitWValue(.{ .imm32 = wasm_bits - int_info.bits }); | |
| 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 { | |
| 6407 | const pt = func.pt; | |
| 6408 | const zcu = pt.zcu; | |
| 6409 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 6287 | fn airCtz(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6288 | const zcu = cg.pt.zcu; | |
| 6289 | const ty_op = cg.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 | 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 | 6298 | const int_info = ty.intInfo(zcu); |
| 6419 | 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 | 6303 | switch (wasm_bits) { |
| ... | ... | @@ -6425,131 +6305,108 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6425 | 6305 | if (wasm_bits != int_info.bits) { |
| 6426 | 6306 | const val: u32 = @as(u32, 1) << @as(u5, @intCast(int_info.bits)); |
| 6427 | 6307 | // leave value on the stack |
| 6428 | _ = try func.binOp(operand, .{ .imm32 = val }, ty, .@"or"); | |
| 6429 | } else try func.emitWValue(operand); | |
| 6430 | try func.addTag(.i32_ctz); | |
| 6308 | _ = try cg.binOp(operand, .{ .imm32 = val }, ty, .@"or"); | |
| 6309 | } else try cg.emitWValue(operand); | |
| 6310 | try cg.addTag(.i32_ctz); | |
| 6431 | 6311 | }, |
| 6432 | 6312 | 64 => { |
| 6433 | 6313 | if (wasm_bits != int_info.bits) { |
| 6434 | 6314 | const val: u64 = @as(u64, 1) << @as(u6, @intCast(int_info.bits)); |
| 6435 | 6315 | // leave value on the stack |
| 6436 | _ = try func.binOp(operand, .{ .imm64 = val }, ty, .@"or"); | |
| 6437 | } else try func.emitWValue(operand); | |
| 6438 | try func.addTag(.i64_ctz); | |
| 6439 | try func.addTag(.i32_wrap_i64); | |
| 6316 | _ = try cg.binOp(operand, .{ .imm64 = val }, ty, .@"or"); | |
| 6317 | } else try cg.emitWValue(operand); | |
| 6318 | try cg.addTag(.i64_ctz); | |
| 6319 | try cg.addTag(.i32_wrap_i64); | |
| 6440 | 6320 | }, |
| 6441 | 6321 | 128 => { |
| 6442 | var lsb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64); | |
| 6443 | defer lsb.free(func); | |
| 6322 | var lsb = try (try cg.load(operand, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 6323 | defer lsb.free(cg); | |
| 6444 | 6324 | |
| 6445 | try func.emitWValue(lsb); | |
| 6446 | try func.addTag(.i64_ctz); | |
| 6447 | _ = try func.load(operand, Type.u64, 8); | |
| 6325 | try cg.emitWValue(lsb); | |
| 6326 | try cg.addTag(.i64_ctz); | |
| 6327 | _ = try cg.load(operand, Type.u64, 8); | |
| 6448 | 6328 | if (wasm_bits != int_info.bits) { |
| 6449 | try func.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64))); | |
| 6450 | try func.addTag(.i64_or); | |
| 6329 | try cg.addImm64(@as(u64, 1) << @as(u6, @intCast(int_info.bits - 64))); | |
| 6330 | try cg.addTag(.i64_or); | |
| 6451 | 6331 | } |
| 6452 | try func.addTag(.i64_ctz); | |
| 6453 | try func.addImm64(64); | |
| 6332 | try cg.addTag(.i64_ctz); | |
| 6333 | try cg.addImm64(64); | |
| 6454 | 6334 | if (wasm_bits != int_info.bits) { |
| 6455 | try func.addTag(.i64_or); | |
| 6335 | try cg.addTag(.i64_or); | |
| 6456 | 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); | |
| 6460 | try func.addTag(.select); | |
| 6461 | try func.addTag(.i32_wrap_i64); | |
| 6339 | _ = try cg.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq); | |
| 6340 | try cg.addTag(.select); | |
| 6341 | try cg.addTag(.i32_wrap_i64); | |
| 6462 | 6342 | }, |
| 6463 | 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 { | |
| 6470 | if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); | |
| 6471 | ||
| 6472 | const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; | |
| 6473 | try func.addInst(.{ .tag = .dbg_line, .data = .{ | |
| 6474 | .payload = try func.addExtra(Mir.DbgLineColumn{ | |
| 6349 | fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6350 | const dbg_stmt = cg.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; | |
| 6351 | try cg.addInst(.{ .tag = .dbg_line, .data = .{ | |
| 6352 | .payload = try cg.addExtra(Mir.DbgLineColumn{ | |
| 6475 | 6353 | .line = dbg_stmt.line, |
| 6476 | 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 { | |
| 6483 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6484 | const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload); | |
| 6360 | fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6361 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6362 | const extra = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload); | |
| 6485 | 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 | 6367 | fn airDbgVar( |
| 6490 | func: *CodeGen, | |
| 6368 | cg: *CodeGen, | |
| 6491 | 6369 | inst: Air.Inst.Index, |
| 6492 | 6370 | local_tag: link.File.Dwarf.WipNav.LocalTag, |
| 6493 | 6371 | is_ptr: bool, |
| 6494 | 6372 | ) InnerError!void { |
| 6495 | 6373 | _ = is_ptr; |
| 6496 | if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); | |
| 6497 | ||
| 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, &.{}); | |
| 6374 | _ = local_tag; | |
| 6375 | return cg.finishAir(inst, .none, &.{}); | |
| 6517 | 6376 | } |
| 6518 | 6377 | |
| 6519 | fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6520 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 6521 | const err_union = try func.resolveInst(pl_op.operand); | |
| 6522 | const extra = func.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]); | |
| 6524 | const err_union_ty = func.typeOf(pl_op.operand); | |
| 6525 | const result = try lowerTry(func, inst, err_union, body, err_union_ty, false); | |
| 6526 | return func.finishAir(inst, result, &.{pl_op.operand}); | |
| 6378 | fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6379 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 6380 | const err_union = try cg.resolveInst(pl_op.operand); | |
| 6381 | const extra = cg.air.extraData(Air.Try, pl_op.payload); | |
| 6382 | const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]); | |
| 6383 | const err_union_ty = cg.typeOf(pl_op.operand); | |
| 6384 | const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false); | |
| 6385 | return cg.finishAir(inst, result, &.{pl_op.operand}); | |
| 6527 | 6386 | } |
| 6528 | 6387 | |
| 6529 | fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6530 | const pt = func.pt; | |
| 6531 | const zcu = pt.zcu; | |
| 6532 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6533 | const extra = func.air.extraData(Air.TryPtr, ty_pl.payload); | |
| 6534 | const err_union_ptr = try func.resolveInst(extra.data.ptr); | |
| 6535 | const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]); | |
| 6536 | const err_union_ty = func.typeOf(extra.data.ptr).childType(zcu); | |
| 6537 | const result = try lowerTry(func, inst, err_union_ptr, body, err_union_ty, true); | |
| 6538 | return func.finishAir(inst, result, &.{extra.data.ptr}); | |
| 6388 | fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6389 | const zcu = cg.pt.zcu; | |
| 6390 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 6391 | const extra = cg.air.extraData(Air.TryPtr, ty_pl.payload); | |
| 6392 | const err_union_ptr = try cg.resolveInst(extra.data.ptr); | |
| 6393 | const body: []const Air.Inst.Index = @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len]); | |
| 6394 | const err_union_ty = cg.typeOf(extra.data.ptr).childType(zcu); | |
| 6395 | const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true); | |
| 6396 | return cg.finishAir(inst, result, &.{extra.data.ptr}); | |
| 6539 | 6397 | } |
| 6540 | 6398 | |
| 6541 | 6399 | fn lowerTry( |
| 6542 | func: *CodeGen, | |
| 6400 | cg: *CodeGen, | |
| 6543 | 6401 | inst: Air.Inst.Index, |
| 6544 | 6402 | err_union: WValue, |
| 6545 | 6403 | body: []const Air.Inst.Index, |
| 6546 | 6404 | err_union_ty: Type, |
| 6547 | 6405 | operand_is_ptr: bool, |
| 6548 | 6406 | ) InnerError!WValue { |
| 6549 | const pt = func.pt; | |
| 6550 | const zcu = pt.zcu; | |
| 6407 | const zcu = cg.pt.zcu; | |
| 6551 | 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 | 6412 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| ... | ... | @@ -6557,29 +6414,29 @@ fn lowerTry( |
| 6557 | 6414 | |
| 6558 | 6415 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { |
| 6559 | 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 | 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 | 6421 | if (pl_has_bits) { |
| 6565 | 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 | 6424 | .offset = err_union.offset() + err_offset, |
| 6568 | 6425 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), |
| 6569 | 6426 | }); |
| 6570 | 6427 | } |
| 6571 | try func.addTag(.i32_eqz); | |
| 6572 | try func.addLabel(.br_if, 0); // jump out of block when error is '0' | |
| 6428 | try cg.addTag(.i32_eqz); | |
| 6429 | try cg.addLabel(.br_if, 0); // jump out of block when error is '0' | |
| 6573 | 6430 | |
| 6574 | const liveness = func.liveness.getCondBr(inst); | |
| 6575 | try func.branches.append(func.gpa, .{}); | |
| 6576 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len); | |
| 6431 | const liveness = cg.liveness.getCondBr(inst); | |
| 6432 | try cg.branches.append(cg.gpa, .{}); | |
| 6433 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, liveness.else_deaths.len + liveness.then_deaths.len); | |
| 6577 | 6434 | defer { |
| 6578 | var branch = func.branches.pop(); | |
| 6579 | branch.deinit(func.gpa); | |
| 6435 | var branch = cg.branches.pop(); | |
| 6436 | branch.deinit(cg.gpa); | |
| 6580 | 6437 | } |
| 6581 | try func.genBody(body); | |
| 6582 | try func.endBlock(); | |
| 6438 | try cg.genBody(body); | |
| 6439 | try cg.endBlock(); | |
| 6583 | 6440 | } |
| 6584 | 6441 | |
| 6585 | 6442 | // if we reach here it means error was not set, and we want the payload |
| ... | ... | @@ -6588,39 +6445,38 @@ fn lowerTry( |
| 6588 | 6445 | } |
| 6589 | 6446 | |
| 6590 | 6447 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); |
| 6591 | if (isByRef(pl_ty, pt, func.target.*)) { | |
| 6592 | return buildPointerOffset(func, err_union, pl_offset, .new); | |
| 6448 | if (isByRef(pl_ty, zcu, cg.target)) { | |
| 6449 | return buildPointerOffset(cg, err_union, pl_offset, .new); | |
| 6593 | 6450 | } |
| 6594 | const payload = try func.load(err_union, pl_ty, pl_offset); | |
| 6595 | return payload.toLocal(func, pl_ty); | |
| 6451 | const payload = try cg.load(err_union, pl_ty, pl_offset); | |
| 6452 | return payload.toLocal(cg, pl_ty); | |
| 6596 | 6453 | } |
| 6597 | 6454 | |
| 6598 | fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6599 | const pt = func.pt; | |
| 6600 | const zcu = pt.zcu; | |
| 6601 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 6455 | fn airByteSwap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6456 | const zcu = cg.pt.zcu; | |
| 6457 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | |
| 6602 | 6458 | |
| 6603 | const ty = func.typeOfIndex(inst); | |
| 6604 | const operand = try func.resolveInst(ty_op.operand); | |
| 6459 | const ty = cg.typeOfIndex(inst); | |
| 6460 | const operand = try cg.resolveInst(ty_op.operand); | |
| 6605 | 6461 | |
| 6606 | 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 | 6465 | const int_info = ty.intInfo(zcu); |
| 6610 | 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 | 6470 | // bytes are no-op |
| 6615 | 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 | 6475 | const result = result: { |
| 6620 | 6476 | switch (wasm_bits) { |
| 6621 | 6477 | 32 => { |
| 6622 | const intrin_ret = try func.callIntrinsic( | |
| 6623 | "__bswapsi2", | |
| 6478 | const intrin_ret = try cg.callIntrinsic( | |
| 6479 | .__bswapsi2, | |
| 6624 | 6480 | &.{.u32_type}, |
| 6625 | 6481 | Type.u32, |
| 6626 | 6482 | &.{operand}, |
| ... | ... | @@ -6628,11 +6484,11 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6628 | 6484 | break :result if (int_info.bits == 32) |
| 6629 | 6485 | intrin_ret |
| 6630 | 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 | 6489 | 64 => { |
| 6634 | const intrin_ret = try func.callIntrinsic( | |
| 6635 | "__bswapdi2", | |
| 6490 | const intrin_ret = try cg.callIntrinsic( | |
| 6491 | .__bswapdi2, | |
| 6636 | 6492 | &.{.u64_type}, |
| 6637 | 6493 | Type.u64, |
| 6638 | 6494 | &.{operand}, |
| ... | ... | @@ -6640,61 +6496,60 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6640 | 6496 | break :result if (int_info.bits == 64) |
| 6641 | 6497 | intrin_ret |
| 6642 | 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 { | |
| 6652 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6507 | fn airDiv(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6508 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6653 | 6509 | |
| 6654 | const ty = func.typeOfIndex(inst); | |
| 6655 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6656 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6510 | const ty = cg.typeOfIndex(inst); | |
| 6511 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6512 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6657 | 6513 | |
| 6658 | const result = try func.binOp(lhs, rhs, ty, .div); | |
| 6659 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6514 | const result = try cg.binOp(lhs, rhs, ty, .div); | |
| 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 { | |
| 6663 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6518 | fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6519 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6664 | 6520 | |
| 6665 | const ty = func.typeOfIndex(inst); | |
| 6666 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6667 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6521 | const ty = cg.typeOfIndex(inst); | |
| 6522 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 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 | 6527 | if (ty.isAnyFloat()) { |
| 6672 | const trunc_result = try func.floatOp(.trunc, ty, &.{div_result}); | |
| 6673 | return func.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6528 | const trunc_result = try cg.floatOp(.trunc, ty, &.{div_result}); | |
| 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 { | |
| 6680 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6535 | fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6536 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6681 | 6537 | |
| 6682 | const pt = func.pt; | |
| 6683 | const zcu = pt.zcu; | |
| 6684 | const ty = func.typeOfIndex(inst); | |
| 6685 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6686 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6538 | const zcu = cg.pt.zcu; | |
| 6539 | const ty = cg.typeOfIndex(inst); | |
| 6540 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6541 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6687 | 6542 | |
| 6688 | 6543 | if (ty.isUnsignedInt(zcu)) { |
| 6689 | _ = try func.binOp(lhs, rhs, ty, .div); | |
| 6544 | _ = try cg.binOp(lhs, rhs, ty, .div); | |
| 6690 | 6545 | } else if (ty.isSignedInt(zcu)) { |
| 6691 | 6546 | const int_bits = ty.intInfo(zcu).bits; |
| 6692 | 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 | 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 | 6555 | const zero: WValue = switch (wasm_bits) { |
| ... | ... | @@ -6704,108 +6559,108 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6704 | 6559 | }; |
| 6705 | 6560 | |
| 6706 | 6561 | // tee leaves the value on the stack and stores it in a local. |
| 6707 | const quotient = try func.allocLocal(ty); | |
| 6708 | _ = try func.binOp(lhs, rhs, ty, .div); | |
| 6709 | try func.addLabel(.local_tee, quotient.local.value); | |
| 6562 | const quotient = try cg.allocLocal(ty); | |
| 6563 | _ = try cg.binOp(lhs, rhs, ty, .div); | |
| 6564 | try cg.addLocal(.local_tee, quotient.local.value); | |
| 6710 | 6565 | |
| 6711 | 6566 | // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow |
| 6712 | 6567 | // the 64 bit value we want to use as the condition to 32 bits. |
| 6713 | 6568 | // This also inverts the condition (non 0 => 0, 0 => 1), so we put the adjusted and |
| 6714 | 6569 | // non-adjusted quotients on the stack in the opposite order for 32 vs 64 bits. |
| 6715 | 6570 | if (wasm_bits == 64) { |
| 6716 | try func.emitWValue(quotient); | |
| 6571 | try cg.emitWValue(quotient); | |
| 6717 | 6572 | } |
| 6718 | 6573 | |
| 6719 | 6574 | // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise. |
| 6720 | _ = try func.binOp(lhs, rhs, ty, .xor); | |
| 6721 | _ = try func.cmp(.stack, zero, ty, .lt); | |
| 6575 | _ = try cg.binOp(lhs, rhs, ty, .xor); | |
| 6576 | _ = try cg.cmp(.stack, zero, ty, .lt); | |
| 6722 | 6577 | |
| 6723 | 6578 | switch (wasm_bits) { |
| 6724 | 6579 | 32 => { |
| 6725 | try func.addTag(.i32_sub); | |
| 6726 | try func.emitWValue(quotient); | |
| 6580 | try cg.addTag(.i32_sub); | |
| 6581 | try cg.emitWValue(quotient); | |
| 6727 | 6582 | }, |
| 6728 | 6583 | 64 => { |
| 6729 | try func.addTag(.i64_extend_i32_u); | |
| 6730 | try func.addTag(.i64_sub); | |
| 6584 | try cg.addTag(.i64_extend_i32_u); | |
| 6585 | try cg.addTag(.i64_sub); | |
| 6731 | 6586 | }, |
| 6732 | 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 | 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 | 6598 | // We need to zero the high bits because N bit comparisons consider all 32 or 64 bits, and |
| 6744 | 6599 | // expect all but the lowest N bits to be 0. |
| 6745 | 6600 | // TODO: Should we be zeroing the high bits here or should we be ignoring the high bits |
| 6746 | 6601 | // when performing comparisons? |
| 6747 | 6602 | if (int_bits != wasm_bits) { |
| 6748 | _ = try func.wrapOperand(.stack, ty); | |
| 6603 | _ = try cg.wrapOperand(.stack, ty); | |
| 6749 | 6604 | } |
| 6750 | 6605 | } else { |
| 6751 | const float_bits = ty.floatBits(func.target.*); | |
| 6606 | const float_bits = ty.floatBits(cg.target.*); | |
| 6752 | 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 | 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; | |
| 6758 | const rhs_wasm = if (is_f16) try func.fpext(rhs, Type.f16, Type.f32) else rhs; | |
| 6612 | const lhs_wasm = if (is_f16) try cg.fpext(lhs, Type.f16, Type.f32) else lhs; | |
| 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); | |
| 6761 | try func.emitWValue(rhs_wasm); | |
| 6615 | try cg.emitWValue(lhs_wasm); | |
| 6616 | try cg.emitWValue(rhs_wasm); | |
| 6762 | 6617 | |
| 6763 | 6618 | switch (float_bits) { |
| 6764 | 6619 | 16, 32 => { |
| 6765 | try func.addTag(.f32_div); | |
| 6766 | try func.addTag(.f32_floor); | |
| 6620 | try cg.addTag(.f32_div); | |
| 6621 | try cg.addTag(.f32_floor); | |
| 6767 | 6622 | }, |
| 6768 | 6623 | 64 => { |
| 6769 | try func.addTag(.f64_div); | |
| 6770 | try func.addTag(.f64_floor); | |
| 6624 | try cg.addTag(.f64_div); | |
| 6625 | try cg.addTag(.f64_floor); | |
| 6771 | 6626 | }, |
| 6772 | 6627 | else => unreachable, |
| 6773 | 6628 | } |
| 6774 | 6629 | |
| 6775 | 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 { | |
| 6784 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6638 | fn airRem(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6639 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6785 | 6640 | |
| 6786 | const ty = func.typeOfIndex(inst); | |
| 6787 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6788 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6641 | const ty = cg.typeOfIndex(inst); | |
| 6642 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 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 | 6650 | /// Remainder after floor division, defined by: |
| 6796 | 6651 | /// @divFloor(a, b) * b + @mod(a, b) = a |
| 6797 | fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6798 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6652 | fn airMod(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 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 | 6656 | const zcu = pt.zcu; |
| 6802 | const ty = func.typeOfIndex(inst); | |
| 6803 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6804 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6657 | const ty = cg.typeOfIndex(inst); | |
| 6658 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6659 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6805 | 6660 | |
| 6806 | 6661 | const result = result: { |
| 6807 | 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 | 6665 | if (ty.isSignedInt(zcu)) { |
| 6811 | 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 | 6669 | // @mod(a, b) = @rem(@rem(a, b) + b, b) |
| 6815 | 6670 | const int_bits = ty.intInfo(zcu).bits; |
| 6816 | 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 | 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); | |
| 6825 | _ = try func.binOp(.stack, rhs, ty, .add); | |
| 6826 | break :result try func.binOp(.stack, rhs, ty, .rem); | |
| 6679 | _ = try cg.binOp(lhs, rhs, ty, .rem); | |
| 6680 | _ = try cg.binOp(.stack, rhs, ty, .add); | |
| 6681 | break :result try cg.binOp(.stack, rhs, ty, .rem); | |
| 6827 | 6682 | } |
| 6828 | 6683 | if (ty.isAnyFloat()) { |
| 6829 | const rem = try func.binOp(lhs, rhs, ty, .rem); | |
| 6830 | const add = try func.binOp(rem, rhs, ty, .add); | |
| 6831 | break :result try func.binOp(add, rhs, ty, .rem); | |
| 6684 | const rem = try cg.binOp(lhs, rhs, ty, .rem); | |
| 6685 | const add = try cg.binOp(rem, rhs, ty, .add); | |
| 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 { | |
| 6840 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6694 | fn airSatMul(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 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 | 6698 | const zcu = pt.zcu; |
| 6844 | const ty = func.typeOfIndex(inst); | |
| 6699 | const ty = cg.typeOfIndex(inst); | |
| 6845 | 6700 | const int_info = ty.intInfo(zcu); |
| 6846 | 6701 | const is_signed = int_info.signedness == .signed; |
| 6847 | 6702 | |
| 6848 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6849 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6703 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6704 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6850 | 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 | 6709 | switch (wasm_bits) { |
| 6855 | 6710 | 32 => { |
| 6856 | 6711 | const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64; |
| 6857 | const lhs_up = try func.intcast(lhs, ty, upcast_ty); | |
| 6858 | const rhs_up = try func.intcast(rhs, ty, upcast_ty); | |
| 6859 | var mul_res = try (try func.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(func, upcast_ty); | |
| 6860 | defer mul_res.free(func); | |
| 6712 | const lhs_up = try cg.intcast(lhs, ty, upcast_ty); | |
| 6713 | const rhs_up = try cg.intcast(rhs, ty, upcast_ty); | |
| 6714 | var mul_res = try (try cg.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(cg, upcast_ty); | |
| 6715 | defer mul_res.free(cg); | |
| 6861 | 6716 | if (is_signed) { |
| 6862 | 6717 | const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) }; |
| 6863 | try func.emitWValue(mul_res); | |
| 6864 | try func.emitWValue(imm_max); | |
| 6865 | _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt); | |
| 6866 | try func.addTag(.select); | |
| 6718 | try cg.emitWValue(mul_res); | |
| 6719 | try cg.emitWValue(imm_max); | |
| 6720 | _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt); | |
| 6721 | try cg.addTag(.select); | |
| 6867 | 6722 | |
| 6868 | var tmp = try func.allocLocal(upcast_ty); | |
| 6869 | defer tmp.free(func); | |
| 6870 | try func.addLabel(.local_set, tmp.local.value); | |
| 6723 | var tmp = try cg.allocLocal(upcast_ty); | |
| 6724 | defer tmp.free(cg); | |
| 6725 | try cg.addLocal(.local_set, tmp.local.value); | |
| 6871 | 6726 | |
| 6872 | 6727 | const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) }; |
| 6873 | try func.emitWValue(tmp); | |
| 6874 | try func.emitWValue(imm_min); | |
| 6875 | _ = try func.cmp(tmp, imm_min, upcast_ty, .gt); | |
| 6876 | try func.addTag(.select); | |
| 6728 | try cg.emitWValue(tmp); | |
| 6729 | try cg.emitWValue(imm_min); | |
| 6730 | _ = try cg.cmp(tmp, imm_min, upcast_ty, .gt); | |
| 6731 | try cg.addTag(.select); | |
| 6877 | 6732 | } else { |
| 6878 | 6733 | const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) }; |
| 6879 | try func.emitWValue(mul_res); | |
| 6880 | try func.emitWValue(imm_max); | |
| 6881 | _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt); | |
| 6882 | try func.addTag(.select); | |
| 6734 | try cg.emitWValue(mul_res); | |
| 6735 | try cg.emitWValue(imm_max); | |
| 6736 | _ = try cg.cmp(mul_res, imm_max, upcast_ty, .lt); | |
| 6737 | try cg.addTag(.select); | |
| 6883 | 6738 | } |
| 6884 | try func.addTag(.i32_wrap_i64); | |
| 6739 | try cg.addTag(.i32_wrap_i64); | |
| 6885 | 6740 | }, |
| 6886 | 6741 | 64 => { |
| 6887 | 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); | |
| 6891 | _ = try func.callIntrinsic( | |
| 6892 | "__mulodi4", | |
| 6745 | const overflow_ret = try cg.allocStack(Type.i32); | |
| 6746 | _ = try cg.callIntrinsic( | |
| 6747 | .__mulodi4, | |
| 6893 | 6748 | &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type }, |
| 6894 | 6749 | Type.i64, |
| 6895 | 6750 | &.{ lhs, rhs, overflow_ret }, |
| 6896 | 6751 | ); |
| 6897 | const xor = try func.binOp(lhs, rhs, Type.i64, .xor); | |
| 6898 | const sign_v = try func.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr); | |
| 6899 | _ = try func.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor); | |
| 6900 | _ = try func.load(overflow_ret, Type.i32, 0); | |
| 6901 | try func.addTag(.i32_eqz); | |
| 6902 | try func.addTag(.select); | |
| 6752 | const xor = try cg.binOp(lhs, rhs, Type.i64, .xor); | |
| 6753 | const sign_v = try cg.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr); | |
| 6754 | _ = try cg.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor); | |
| 6755 | _ = try cg.load(overflow_ret, Type.i32, 0); | |
| 6756 | try cg.addTag(.i32_eqz); | |
| 6757 | try cg.addTag(.select); | |
| 6903 | 6758 | }, |
| 6904 | 6759 | 128 => { |
| 6905 | 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); | |
| 6909 | const ret = try func.callIntrinsic( | |
| 6910 | "__muloti4", | |
| 6763 | const overflow_ret = try cg.allocStack(Type.i32); | |
| 6764 | const ret = try cg.callIntrinsic( | |
| 6765 | .__muloti4, | |
| 6911 | 6766 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, |
| 6912 | 6767 | Type.i128, |
| 6913 | 6768 | &.{ lhs, rhs, overflow_ret }, |
| 6914 | 6769 | ); |
| 6915 | try func.lowerToStack(ret); | |
| 6916 | const xor = try func.binOp(lhs, rhs, Type.i128, .xor); | |
| 6917 | const sign_v = try func.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr); | |
| 6770 | try cg.lowerToStack(ret); | |
| 6771 | const xor = try cg.binOp(lhs, rhs, Type.i128, .xor); | |
| 6772 | const sign_v = try cg.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr); | |
| 6918 | 6773 | |
| 6919 | 6774 | // xor ~@as(u127, 0) |
| 6920 | try func.emitWValue(sign_v); | |
| 6921 | const lsb = try func.load(sign_v, Type.u64, 0); | |
| 6922 | _ = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); | |
| 6923 | try func.store(.stack, .stack, Type.u64, sign_v.offset()); | |
| 6924 | try func.emitWValue(sign_v); | |
| 6925 | const msb = try func.load(sign_v, Type.u64, 8); | |
| 6926 | _ = try func.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor); | |
| 6927 | try func.store(.stack, .stack, Type.u64, sign_v.offset() + 8); | |
| 6928 | ||
| 6929 | try func.lowerToStack(sign_v); | |
| 6930 | _ = try func.load(overflow_ret, Type.i32, 0); | |
| 6931 | try func.addTag(.i32_eqz); | |
| 6932 | try func.addTag(.select); | |
| 6775 | try cg.emitWValue(sign_v); | |
| 6776 | const lsb = try cg.load(sign_v, Type.u64, 0); | |
| 6777 | _ = try cg.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); | |
| 6778 | try cg.store(.stack, .stack, Type.u64, sign_v.offset()); | |
| 6779 | try cg.emitWValue(sign_v); | |
| 6780 | const msb = try cg.load(sign_v, Type.u64, 8); | |
| 6781 | _ = try cg.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor); | |
| 6782 | try cg.store(.stack, .stack, Type.u64, sign_v.offset() + 8); | |
| 6783 | ||
| 6784 | try cg.lowerToStack(sign_v); | |
| 6785 | _ = try cg.load(overflow_ret, Type.i32, 0); | |
| 6786 | try cg.addTag(.i32_eqz); | |
| 6787 | try cg.addTag(.select); | |
| 6933 | 6788 | }, |
| 6934 | 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 | 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; | |
| 6944 | const zcu = pt.zcu; | |
| 6945 | const ty = func.typeOfIndex(inst); | |
| 6946 | const lhs = try func.resolveInst(bin_op.lhs); | |
| 6947 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6798 | const zcu = cg.pt.zcu; | |
| 6799 | const ty = cg.typeOfIndex(inst); | |
| 6800 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6801 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 6948 | 6802 | |
| 6949 | 6803 | const int_info = ty.intInfo(zcu); |
| 6950 | 6804 | const is_signed = int_info.signedness == .signed; |
| 6951 | 6805 | |
| 6952 | 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 | 6810 | if (is_signed) { |
| 6957 | const result = try signedSat(func, lhs, rhs, ty, op); | |
| 6958 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6811 | const result = try signedSat(cg, lhs, rhs, ty, op); | |
| 6812 | return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6959 | 6813 | } |
| 6960 | 6814 | |
| 6961 | 6815 | const wasm_bits = toWasmBits(int_info.bits).?; |
| 6962 | var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty); | |
| 6963 | defer bin_result.free(func); | |
| 6816 | var bin_result = try (try cg.binOp(lhs, rhs, ty, op)).toLocal(cg, ty); | |
| 6817 | defer bin_result.free(cg); | |
| 6964 | 6818 | if (wasm_bits != int_info.bits and op == .add) { |
| 6965 | 6819 | const val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits))) - 1)); |
| 6966 | 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 | 6823 | else => unreachable, |
| 6970 | 6824 | }; |
| 6971 | 6825 | |
| 6972 | try func.emitWValue(bin_result); | |
| 6973 | try func.emitWValue(imm_val); | |
| 6974 | _ = try func.cmp(bin_result, imm_val, ty, .lt); | |
| 6826 | try cg.emitWValue(bin_result); | |
| 6827 | try cg.emitWValue(imm_val); | |
| 6828 | _ = try cg.cmp(bin_result, imm_val, ty, .lt); | |
| 6975 | 6829 | } else { |
| 6976 | 6830 | switch (wasm_bits) { |
| 6977 | 32 => try func.addImm32(if (op == .add) std.math.maxInt(u32) else 0), | |
| 6978 | 64 => try func.addImm64(if (op == .add) std.math.maxInt(u64) else 0), | |
| 6831 | 32 => try cg.addImm32(if (op == .add) std.math.maxInt(u32) else 0), | |
| 6832 | 64 => try cg.addImm64(if (op == .add) std.math.maxInt(u64) else 0), | |
| 6979 | 6833 | else => unreachable, |
| 6980 | 6834 | } |
| 6981 | try func.emitWValue(bin_result); | |
| 6982 | _ = try func.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); | |
| 6835 | try cg.emitWValue(bin_result); | |
| 6836 | _ = try cg.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); | |
| 6983 | 6837 | } |
| 6984 | 6838 | |
| 6985 | try func.addTag(.select); | |
| 6986 | return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6839 | try cg.addTag(.select); | |
| 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 { | |
| 6990 | const pt = func.pt; | |
| 6843 | fn signedSat(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | |
| 6844 | const pt = cg.pt; | |
| 6991 | 6845 | const zcu = pt.zcu; |
| 6992 | 6846 | const int_info = ty.intInfo(zcu); |
| 6993 | 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 | 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 | 6865 | if (!is_wasm_bits) { |
| 7012 | defer bin_result.free(func); // not returned in this branch | |
| 7013 | try func.emitWValue(bin_result); | |
| 7014 | try func.emitWValue(max_wvalue); | |
| 7015 | _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt); | |
| 7016 | try func.addTag(.select); | |
| 7017 | try func.addLabel(.local_set, bin_result.local.value); // re-use local | |
| 7018 | ||
| 7019 | try func.emitWValue(bin_result); | |
| 7020 | try func.emitWValue(min_wvalue); | |
| 7021 | _ = try func.cmp(bin_result, min_wvalue, ext_ty, .gt); | |
| 7022 | try func.addTag(.select); | |
| 7023 | try func.addLabel(.local_set, bin_result.local.value); // re-use local | |
| 7024 | return (try func.wrapOperand(bin_result, ty)).toLocal(func, ty); | |
| 6866 | defer bin_result.free(cg); // not returned in this branch | |
| 6867 | try cg.emitWValue(bin_result); | |
| 6868 | try cg.emitWValue(max_wvalue); | |
| 6869 | _ = try cg.cmp(bin_result, max_wvalue, ext_ty, .lt); | |
| 6870 | try cg.addTag(.select); | |
| 6871 | try cg.addLocal(.local_set, bin_result.local.value); // re-use local | |
| 6872 | ||
| 6873 | try cg.emitWValue(bin_result); | |
| 6874 | try cg.emitWValue(min_wvalue); | |
| 6875 | _ = try cg.cmp(bin_result, min_wvalue, ext_ty, .gt); | |
| 6876 | try cg.addTag(.select); | |
| 6877 | try cg.addLocal(.local_set, bin_result.local.value); // re-use local | |
| 6878 | return (try cg.wrapOperand(bin_result, ty)).toLocal(cg, ty); | |
| 7025 | 6879 | } else { |
| 7026 | 6880 | const zero: WValue = switch (wasm_bits) { |
| 7027 | 6881 | 32 => .{ .imm32 = 0 }, |
| 7028 | 6882 | 64 => .{ .imm64 = 0 }, |
| 7029 | 6883 | else => unreachable, |
| 7030 | 6884 | }; |
| 7031 | try func.emitWValue(max_wvalue); | |
| 7032 | try func.emitWValue(min_wvalue); | |
| 7033 | _ = try func.cmp(bin_result, zero, ty, .lt); | |
| 7034 | try func.addTag(.select); | |
| 7035 | try func.emitWValue(bin_result); | |
| 6885 | try cg.emitWValue(max_wvalue); | |
| 6886 | try cg.emitWValue(min_wvalue); | |
| 6887 | _ = try cg.cmp(bin_result, zero, ty, .lt); | |
| 6888 | try cg.addTag(.select); | |
| 6889 | try cg.emitWValue(bin_result); | |
| 7036 | 6890 | // leave on stack |
| 7037 | const cmp_zero_result = try func.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); | |
| 7038 | const cmp_bin_result = try func.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. | |
| 7040 | try func.addTag(.select); | |
| 7041 | try func.addLabel(.local_set, bin_result.local.value); // re-use local | |
| 6891 | const cmp_zero_result = try cg.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); | |
| 6892 | const cmp_bin_result = try cg.cmp(bin_result, lhs, ty, .lt); | |
| 6893 | _ = try cg.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. | |
| 6894 | try cg.addTag(.select); | |
| 6895 | try cg.addLocal(.local_set, bin_result.local.value); // re-use local | |
| 7042 | 6896 | return bin_result; |
| 7043 | 6897 | } |
| 7044 | 6898 | } |
| 7045 | 6899 | |
| 7046 | fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7047 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 6900 | fn airShlSat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 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 | 6904 | const zcu = pt.zcu; |
| 7051 | const ty = func.typeOfIndex(inst); | |
| 6905 | const ty = cg.typeOfIndex(inst); | |
| 7052 | 6906 | const int_info = ty.intInfo(zcu); |
| 7053 | 6907 | const is_signed = int_info.signedness == .signed; |
| 7054 | 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); | |
| 7059 | const rhs = try func.resolveInst(bin_op.rhs); | |
| 6912 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 6913 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 7060 | 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 | 6917 | if (wasm_bits == int_info.bits) { |
| 7064 | var shl = try (try func.binOp(lhs, rhs, ty, .shl)).toLocal(func, ty); | |
| 7065 | defer shl.free(func); | |
| 7066 | var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty); | |
| 7067 | defer shr.free(func); | |
| 6918 | var shl = try (try cg.binOp(lhs, rhs, ty, .shl)).toLocal(cg, ty); | |
| 6919 | defer shl.free(cg); | |
| 6920 | var shr = try (try cg.binOp(shl, rhs, ty, .shr)).toLocal(cg, ty); | |
| 6921 | defer shr.free(cg); | |
| 7068 | 6922 | |
| 7069 | 6923 | switch (wasm_bits) { |
| 7070 | 6924 | 32 => blk: { |
| 7071 | 6925 | if (!is_signed) { |
| 7072 | try func.addImm32(std.math.maxInt(u32)); | |
| 6926 | try cg.addImm32(std.math.maxInt(u32)); | |
| 7073 | 6927 | break :blk; |
| 7074 | 6928 | } |
| 7075 | try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); | |
| 7076 | try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); | |
| 7077 | _ = try func.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); | |
| 7078 | try func.addTag(.select); | |
| 6929 | try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); | |
| 6930 | try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); | |
| 6931 | _ = try cg.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); | |
| 6932 | try cg.addTag(.select); | |
| 7079 | 6933 | }, |
| 7080 | 6934 | 64 => blk: { |
| 7081 | 6935 | if (!is_signed) { |
| 7082 | try func.addImm64(std.math.maxInt(u64)); | |
| 6936 | try cg.addImm64(std.math.maxInt(u64)); | |
| 7083 | 6937 | break :blk; |
| 7084 | 6938 | } |
| 7085 | try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); | |
| 7086 | try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); | |
| 7087 | _ = try func.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); | |
| 7088 | try func.addTag(.select); | |
| 6939 | try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); | |
| 6940 | try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); | |
| 6941 | _ = try cg.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); | |
| 6942 | try cg.addTag(.select); | |
| 7089 | 6943 | }, |
| 7090 | 6944 | else => unreachable, |
| 7091 | 6945 | } |
| 7092 | try func.emitWValue(shl); | |
| 7093 | _ = try func.cmp(lhs, shr, ty, .neq); | |
| 7094 | try func.addTag(.select); | |
| 7095 | try func.addLabel(.local_set, result.local.value); | |
| 6946 | try cg.emitWValue(shl); | |
| 6947 | _ = try cg.cmp(lhs, shr, ty, .neq); | |
| 6948 | try cg.addTag(.select); | |
| 6949 | try cg.addLocal(.local_set, result.local.value); | |
| 7096 | 6950 | } else { |
| 7097 | 6951 | const shift_size = wasm_bits - int_info.bits; |
| 7098 | 6952 | const shift_value: WValue = switch (wasm_bits) { |
| ... | ... | @@ -7102,50 +6956,50 @@ fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7102 | 6956 | }; |
| 7103 | 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); | |
| 7106 | defer shl_res.free(func); | |
| 7107 | var shl = try (try func.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(func, ext_ty); | |
| 7108 | defer shl.free(func); | |
| 7109 | var shr = try (try func.binOp(shl, rhs, ext_ty, .shr)).toLocal(func, ext_ty); | |
| 7110 | defer shr.free(func); | |
| 6959 | var shl_res = try (try cg.binOp(lhs, shift_value, ext_ty, .shl)).toLocal(cg, ext_ty); | |
| 6960 | defer shl_res.free(cg); | |
| 6961 | var shl = try (try cg.binOp(shl_res, rhs, ext_ty, .shl)).toLocal(cg, ext_ty); | |
| 6962 | defer shl.free(cg); | |
| 6963 | var shr = try (try cg.binOp(shl, rhs, ext_ty, .shr)).toLocal(cg, ext_ty); | |
| 6964 | defer shr.free(cg); | |
| 7111 | 6965 | |
| 7112 | 6966 | switch (wasm_bits) { |
| 7113 | 6967 | 32 => blk: { |
| 7114 | 6968 | if (!is_signed) { |
| 7115 | try func.addImm32(std.math.maxInt(u32)); | |
| 6969 | try cg.addImm32(std.math.maxInt(u32)); | |
| 7116 | 6970 | break :blk; |
| 7117 | 6971 | } |
| 7118 | 6972 | |
| 7119 | try func.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); | |
| 7120 | try func.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); | |
| 7121 | _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt); | |
| 7122 | try func.addTag(.select); | |
| 6973 | try cg.addImm32(@bitCast(@as(i32, std.math.minInt(i32)))); | |
| 6974 | try cg.addImm32(@bitCast(@as(i32, std.math.maxInt(i32)))); | |
| 6975 | _ = try cg.cmp(shl_res, .{ .imm32 = 0 }, ext_ty, .lt); | |
| 6976 | try cg.addTag(.select); | |
| 7123 | 6977 | }, |
| 7124 | 6978 | 64 => blk: { |
| 7125 | 6979 | if (!is_signed) { |
| 7126 | try func.addImm64(std.math.maxInt(u64)); | |
| 6980 | try cg.addImm64(std.math.maxInt(u64)); | |
| 7127 | 6981 | break :blk; |
| 7128 | 6982 | } |
| 7129 | 6983 | |
| 7130 | try func.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); | |
| 7131 | try func.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); | |
| 7132 | _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt); | |
| 7133 | try func.addTag(.select); | |
| 6984 | try cg.addImm64(@bitCast(@as(i64, std.math.minInt(i64)))); | |
| 6985 | try cg.addImm64(@bitCast(@as(i64, std.math.maxInt(i64)))); | |
| 6986 | _ = try cg.cmp(shl_res, .{ .imm64 = 0 }, ext_ty, .lt); | |
| 6987 | try cg.addTag(.select); | |
| 7134 | 6988 | }, |
| 7135 | 6989 | else => unreachable, |
| 7136 | 6990 | } |
| 7137 | try func.emitWValue(shl); | |
| 7138 | _ = try func.cmp(shl_res, shr, ext_ty, .neq); | |
| 7139 | try func.addTag(.select); | |
| 7140 | try func.addLabel(.local_set, result.local.value); | |
| 7141 | var shift_result = try func.binOp(result, shift_value, ext_ty, .shr); | |
| 6991 | try cg.emitWValue(shl); | |
| 6992 | _ = try cg.cmp(shl_res, shr, ext_ty, .neq); | |
| 6993 | try cg.addTag(.select); | |
| 6994 | try cg.addLocal(.local_set, result.local.value); | |
| 6995 | var shift_result = try cg.binOp(result, shift_value, ext_ty, .shr); | |
| 7142 | 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 | 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 | 7009 | /// passed as the first parameter. |
| 7156 | 7010 | /// May leave the return value on the stack. |
| 7157 | 7011 | fn callIntrinsic( |
| 7158 | func: *CodeGen, | |
| 7159 | name: []const u8, | |
| 7012 | cg: *CodeGen, | |
| 7013 | intrinsic: Mir.Intrinsic, | |
| 7160 | 7014 | param_types: []const InternPool.Index, |
| 7161 | 7015 | return_type: Type, |
| 7162 | 7016 | args: []const WValue, |
| 7163 | 7017 | ) InnerError!WValue { |
| 7164 | 7018 | assert(param_types.len == args.len); |
| 7165 | const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| { | |
| 7166 | return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); | |
| 7167 | }; | |
| 7019 | const zcu = cg.pt.zcu; | |
| 7168 | 7020 | |
| 7169 | 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 | 7024 | // if we want return as first param, we allocate a pointer to stack, |
| 7179 | 7025 | // and emit it as our first argument |
| 7180 | 7026 | const sret = if (want_sret_param) blk: { |
| 7181 | const sret_local = try func.allocStack(return_type); | |
| 7182 | try func.lowerToStack(sret_local); | |
| 7027 | const sret_local = try cg.allocStack(return_type); | |
| 7028 | try cg.lowerToStack(sret_local); | |
| 7183 | 7029 | break :blk sret_local; |
| 7184 | 7030 | } else .none; |
| 7185 | 7031 | |
| ... | ... | @@ -7187,16 +7033,15 @@ fn callIntrinsic( |
| 7187 | 7033 | for (args, 0..) |arg, arg_i| { |
| 7188 | 7034 | assert(!(want_sret_param and arg == .stack)); |
| 7189 | 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 | |
| 7194 | try func.addLabel(.call, @intFromEnum(symbol_index)); | |
| 7039 | try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } }); | |
| 7195 | 7040 | |
| 7196 | 7041 | if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 7197 | 7042 | return .none; |
| 7198 | 7043 | } else if (return_type.isNoReturn(zcu)) { |
| 7199 | try func.addTag(.@"unreachable"); | |
| 7044 | try cg.addTag(.@"unreachable"); | |
| 7200 | 7045 | return .none; |
| 7201 | 7046 | } else if (want_sret_param) { |
| 7202 | 7047 | return sret; |
| ... | ... | @@ -7205,194 +7050,30 @@ fn callIntrinsic( |
| 7205 | 7050 | } |
| 7206 | 7051 | } |
| 7207 | 7052 | |
| 7208 | fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7209 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 7210 | const operand = try func.resolveInst(un_op); | |
| 7211 | const enum_ty = func.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 | } | |
| 7053 | fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7054 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | |
| 7055 | const operand = try cg.resolveInst(un_op); | |
| 7056 | const enum_ty = cg.typeOf(un_op); | |
| 7371 | 7057 | |
| 7372 | try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name | |
| 7373 | // finish outer block | |
| 7374 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 7375 | // finish function body | |
| 7376 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 7058 | const result_ptr = try cg.allocStack(cg.typeOfIndex(inst)); | |
| 7059 | try cg.lowerToStack(result_ptr); | |
| 7060 | try cg.emitWValue(operand); | |
| 7061 | try cg.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = enum_ty.toIntern() } }); | |
| 7377 | 7062 | |
| 7378 | const slice_ty = Type.slice_const_u8_sentinel_0; | |
| 7379 | const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, pt, func.target.*); | |
| 7380 | const sym_index = try func.bin_file.createFunction(func_name, func_type, &body_list, &relocs); | |
| 7381 | return @intFromEnum(sym_index); | |
| 7063 | return cg.finishAir(inst, result_ptr, &.{un_op}); | |
| 7382 | 7064 | } |
| 7383 | 7065 | |
| 7384 | fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7385 | const pt = func.pt; | |
| 7386 | const zcu = pt.zcu; | |
| 7066 | fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7067 | const zcu = cg.pt.zcu; | |
| 7387 | 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 | 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 | 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 | 7077 | defer values.deinit(); |
| 7397 | 7078 | |
| 7398 | 7079 | var lowest: ?u32 = null; |
| ... | ... | @@ -7418,23 +7099,23 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7418 | 7099 | } |
| 7419 | 7100 | |
| 7420 | 7101 | // start block for 'true' branch |
| 7421 | try func.startBlock(.block, wasm.block_empty); | |
| 7102 | try cg.startBlock(.block, .empty); | |
| 7422 | 7103 | // start block for 'false' branch |
| 7423 | try func.startBlock(.block, wasm.block_empty); | |
| 7104 | try cg.startBlock(.block, .empty); | |
| 7424 | 7105 | // block for the jump table itself |
| 7425 | try func.startBlock(.block, wasm.block_empty); | |
| 7106 | try cg.startBlock(.block, .empty); | |
| 7426 | 7107 | |
| 7427 | 7108 | // lower operand to determine jump table target |
| 7428 | try func.emitWValue(operand); | |
| 7429 | try func.addImm32(lowest.?); | |
| 7430 | try func.addTag(.i32_sub); | |
| 7109 | try cg.emitWValue(operand); | |
| 7110 | try cg.addImm32(lowest.?); | |
| 7111 | try cg.addTag(.i32_sub); | |
| 7431 | 7112 | |
| 7432 | 7113 | // Account for default branch so always add '1' |
| 7433 | 7114 | const depth = @as(u32, @intCast(highest.? - lowest.? + 1)); |
| 7434 | 7115 | const jump_table: Mir.JumpTable = .{ .length = depth }; |
| 7435 | const table_extra_index = try func.addExtra(jump_table); | |
| 7436 | try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | |
| 7437 | try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); | |
| 7116 | const table_extra_index = try cg.addExtra(jump_table); | |
| 7117 | try cg.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | |
| 7118 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, depth); | |
| 7438 | 7119 | |
| 7439 | 7120 | var value: u32 = lowest.?; |
| 7440 | 7121 | while (value <= highest.?) : (value += 1) { |
| ... | ... | @@ -7444,202 +7125,200 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7444 | 7125 | } |
| 7445 | 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 | 7132 | // 'false' branch (i.e. error set does not have value |
| 7452 | 7133 | // ensure we set local to 0 in case the local was re-used. |
| 7453 | try func.addImm32(0); | |
| 7454 | try func.addLabel(.local_set, result.local.value); | |
| 7455 | try func.addLabel(.br, 1); | |
| 7456 | try func.endBlock(); | |
| 7134 | try cg.addImm32(0); | |
| 7135 | try cg.addLocal(.local_set, result.local.value); | |
| 7136 | try cg.addLabel(.br, 1); | |
| 7137 | try cg.endBlock(); | |
| 7457 | 7138 | |
| 7458 | 7139 | // 'true' branch |
| 7459 | try func.addImm32(1); | |
| 7460 | try func.addLabel(.local_set, result.local.value); | |
| 7461 | try func.addLabel(.br, 0); | |
| 7462 | try func.endBlock(); | |
| 7140 | try cg.addImm32(1); | |
| 7141 | try cg.addLocal(.local_set, result.local.value); | |
| 7142 | try cg.addLabel(.br, 0); | |
| 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 { | |
| 7468 | return std.Target.wasm.featureSetHas(func.target.cpu.features, .atomics); | |
| 7148 | inline fn useAtomicFeature(cg: *const CodeGen) bool { | |
| 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 { | |
| 7472 | const pt = func.pt; | |
| 7473 | const zcu = pt.zcu; | |
| 7474 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 7475 | const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data; | |
| 7152 | fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7153 | const zcu = cg.pt.zcu; | |
| 7154 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | |
| 7155 | const extra = cg.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 | 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); | |
| 7482 | const expected_val = try func.resolveInst(extra.expected_value); | |
| 7483 | const new_val = try func.resolveInst(extra.new_value); | |
| 7161 | const ptr_operand = try cg.resolveInst(extra.ptr); | |
| 7162 | const expected_val = try cg.resolveInst(extra.expected_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: { | |
| 7488 | const val_local = try func.allocLocal(ty); | |
| 7489 | try func.emitWValue(ptr_operand); | |
| 7490 | try func.lowerToStack(expected_val); | |
| 7491 | try func.lowerToStack(new_val); | |
| 7492 | try func.addAtomicMemArg(switch (ty.abiSize(zcu)) { | |
| 7167 | const ptr_val = if (cg.useAtomicFeature()) val: { | |
| 7168 | const val_local = try cg.allocLocal(ty); | |
| 7169 | try cg.emitWValue(ptr_operand); | |
| 7170 | try cg.lowerToStack(expected_val); | |
| 7171 | try cg.lowerToStack(new_val); | |
| 7172 | try cg.addAtomicMemArg(switch (ty.abiSize(zcu)) { | |
| 7493 | 7173 | 1 => .i32_atomic_rmw8_cmpxchg_u, |
| 7494 | 7174 | 2 => .i32_atomic_rmw16_cmpxchg_u, |
| 7495 | 7175 | 4 => .i32_atomic_rmw_cmpxchg, |
| 7496 | 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 | 7179 | .offset = ptr_operand.offset(), |
| 7500 | 7180 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7501 | 7181 | }); |
| 7502 | try func.addLabel(.local_tee, val_local.local.value); | |
| 7503 | _ = try func.cmp(.stack, expected_val, ty, .eq); | |
| 7504 | try func.addLabel(.local_set, cmp_result.local.value); | |
| 7182 | try cg.addLocal(.local_tee, val_local.local.value); | |
| 7183 | _ = try cg.cmp(.stack, expected_val, ty, .eq); | |
| 7184 | try cg.addLocal(.local_set, cmp_result.local.value); | |
| 7505 | 7185 | break :val val_local; |
| 7506 | 7186 | } else val: { |
| 7507 | 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); | |
| 7513 | try func.lowerToStack(new_val); | |
| 7514 | try func.emitWValue(ptr_val); | |
| 7515 | _ = try func.cmp(ptr_val, expected_val, ty, .eq); | |
| 7516 | try func.addLabel(.local_tee, cmp_result.local.value); | |
| 7517 | try func.addTag(.select); | |
| 7518 | try func.store(.stack, .stack, ty, 0); | |
| 7192 | try cg.lowerToStack(ptr_operand); | |
| 7193 | try cg.lowerToStack(new_val); | |
| 7194 | try cg.emitWValue(ptr_val); | |
| 7195 | _ = try cg.cmp(ptr_val, expected_val, ty, .eq); | |
| 7196 | try cg.addLocal(.local_tee, cmp_result.local.value); | |
| 7197 | try cg.addTag(.select); | |
| 7198 | try cg.store(.stack, .stack, ty, 0); | |
| 7519 | 7199 | |
| 7520 | 7200 | break :val ptr_val; |
| 7521 | 7201 | }; |
| 7522 | 7202 | |
| 7523 | const result = if (isByRef(result_ty, pt, func.target.*)) val: { | |
| 7524 | try func.emitWValue(cmp_result); | |
| 7525 | try func.addImm32(~@as(u32, 0)); | |
| 7526 | try func.addTag(.i32_xor); | |
| 7527 | try func.addImm32(1); | |
| 7528 | try func.addTag(.i32_and); | |
| 7529 | const and_result = try WValue.toLocal(.stack, func, Type.bool); | |
| 7530 | const result_ptr = try func.allocStack(result_ty); | |
| 7531 | try func.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu)))); | |
| 7532 | try func.store(result_ptr, ptr_val, ty, 0); | |
| 7203 | const result = if (isByRef(result_ty, zcu, cg.target)) val: { | |
| 7204 | try cg.emitWValue(cmp_result); | |
| 7205 | try cg.addImm32(~@as(u32, 0)); | |
| 7206 | try cg.addTag(.i32_xor); | |
| 7207 | try cg.addImm32(1); | |
| 7208 | try cg.addTag(.i32_and); | |
| 7209 | const and_result = try WValue.toLocal(.stack, cg, Type.bool); | |
| 7210 | const result_ptr = try cg.allocStack(result_ty); | |
| 7211 | try cg.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu)))); | |
| 7212 | try cg.store(result_ptr, ptr_val, ty, 0); | |
| 7533 | 7213 | break :val result_ptr; |
| 7534 | 7214 | } else val: { |
| 7535 | try func.addImm32(0); | |
| 7536 | try func.emitWValue(ptr_val); | |
| 7537 | try func.emitWValue(cmp_result); | |
| 7538 | try func.addTag(.select); | |
| 7215 | try cg.addImm32(0); | |
| 7216 | try cg.emitWValue(ptr_val); | |
| 7217 | try cg.emitWValue(cmp_result); | |
| 7218 | try cg.addTag(.select); | |
| 7539 | 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 { | |
| 7546 | const pt = func.pt; | |
| 7547 | const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; | |
| 7548 | const ptr = try func.resolveInst(atomic_load.ptr); | |
| 7549 | const ty = func.typeOfIndex(inst); | |
| 7225 | fn airAtomicLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7226 | const zcu = cg.pt.zcu; | |
| 7227 | const atomic_load = cg.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; | |
| 7228 | const ptr = try cg.resolveInst(atomic_load.ptr); | |
| 7229 | const ty = cg.typeOfIndex(inst); | |
| 7550 | 7230 | |
| 7551 | if (func.useAtomicFeature()) { | |
| 7552 | const tag: wasm.AtomicsOpcode = switch (ty.abiSize(pt.zcu)) { | |
| 7231 | if (cg.useAtomicFeature()) { | |
| 7232 | const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | |
| 7553 | 7233 | 1 => .i32_atomic_load8_u, |
| 7554 | 7234 | 2 => .i32_atomic_load16_u, |
| 7555 | 7235 | 4 => .i32_atomic_load, |
| 7556 | 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); | |
| 7560 | try func.addAtomicMemArg(tag, .{ | |
| 7239 | try cg.emitWValue(ptr); | |
| 7240 | try cg.addAtomicMemArg(tag, .{ | |
| 7561 | 7241 | .offset = ptr.offset(), |
| 7562 | .alignment = @intCast(ty.abiAlignment(pt.zcu).toByteUnits().?), | |
| 7242 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), | |
| 7563 | 7243 | }); |
| 7564 | 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 { | |
| 7572 | const pt = func.pt; | |
| 7573 | const zcu = pt.zcu; | |
| 7574 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 7575 | const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 7251 | fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7252 | const zcu = cg.pt.zcu; | |
| 7253 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | |
| 7254 | const extra = cg.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 7576 | 7255 | |
| 7577 | const ptr = try func.resolveInst(pl_op.operand); | |
| 7578 | const operand = try func.resolveInst(extra.operand); | |
| 7579 | const ty = func.typeOfIndex(inst); | |
| 7256 | const ptr = try cg.resolveInst(pl_op.operand); | |
| 7257 | const operand = try cg.resolveInst(extra.operand); | |
| 7258 | const ty = cg.typeOfIndex(inst); | |
| 7580 | 7259 | const op: std.builtin.AtomicRmwOp = extra.op(); |
| 7581 | 7260 | |
| 7582 | if (func.useAtomicFeature()) { | |
| 7261 | if (cg.useAtomicFeature()) { | |
| 7583 | 7262 | switch (op) { |
| 7584 | 7263 | .Max, |
| 7585 | 7264 | .Min, |
| 7586 | 7265 | .Nand, |
| 7587 | 7266 | => { |
| 7588 | const tmp = try func.load(ptr, ty, 0); | |
| 7589 | const value = try tmp.toLocal(func, ty); | |
| 7267 | const tmp = try cg.load(ptr, ty, 0); | |
| 7268 | const value = try tmp.toLocal(cg, ty); | |
| 7590 | 7269 | |
| 7591 | 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); | |
| 7595 | try func.emitWValue(value); | |
| 7273 | try cg.emitWValue(ptr); | |
| 7274 | try cg.emitWValue(value); | |
| 7596 | 7275 | if (op == .Nand) { |
| 7597 | 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 | 7279 | if (wasm_bits == 32) |
| 7601 | try func.addImm32(~@as(u32, 0)) | |
| 7280 | try cg.addImm32(~@as(u32, 0)) | |
| 7602 | 7281 | else if (wasm_bits == 64) |
| 7603 | try func.addImm64(~@as(u64, 0)) | |
| 7282 | try cg.addImm64(~@as(u64, 0)) | |
| 7604 | 7283 | else |
| 7605 | return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); | |
| 7606 | _ = try func.binOp(and_res, .stack, ty, .xor); | |
| 7284 | return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); | |
| 7285 | _ = try cg.binOp(and_res, .stack, ty, .xor); | |
| 7607 | 7286 | } else { |
| 7608 | try func.emitWValue(value); | |
| 7609 | try func.emitWValue(operand); | |
| 7610 | _ = try func.cmp(value, operand, ty, if (op == .Max) .gt else .lt); | |
| 7611 | try func.addTag(.select); | |
| 7287 | try cg.emitWValue(value); | |
| 7288 | try cg.emitWValue(operand); | |
| 7289 | _ = try cg.cmp(value, operand, ty, if (op == .Max) .gt else .lt); | |
| 7290 | try cg.addTag(.select); | |
| 7612 | 7291 | } |
| 7613 | try func.addAtomicMemArg( | |
| 7292 | try cg.addAtomicMemArg( | |
| 7614 | 7293 | switch (ty.abiSize(zcu)) { |
| 7615 | 7294 | 1 => .i32_atomic_rmw8_cmpxchg_u, |
| 7616 | 7295 | 2 => .i32_atomic_rmw16_cmpxchg_u, |
| 7617 | 7296 | 4 => .i32_atomic_rmw_cmpxchg, |
| 7618 | 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 | 7301 | .offset = ptr.offset(), |
| 7623 | 7302 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7624 | 7303 | }, |
| 7625 | 7304 | ); |
| 7626 | const select_res = try func.allocLocal(ty); | |
| 7627 | try func.addLabel(.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 | |
| 7305 | const select_res = try cg.allocLocal(ty); | |
| 7306 | try cg.addLocal(.local_tee, select_res.local.value); | |
| 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); | |
| 7631 | try func.addLabel(.local_set, value.local.value); | |
| 7309 | try cg.emitWValue(select_res); | |
| 7310 | try cg.addLocal(.local_set, value.local.value); | |
| 7632 | 7311 | |
| 7633 | try func.addLabel(.br_if, 0); | |
| 7634 | try func.endBlock(); | |
| 7635 | return func.finishAir(inst, value, &.{ pl_op.operand, extra.operand }); | |
| 7312 | try cg.addLabel(.br_if, 0); | |
| 7313 | try cg.endBlock(); | |
| 7314 | return cg.finishAir(inst, value, &.{ pl_op.operand, extra.operand }); | |
| 7636 | 7315 | }, |
| 7637 | 7316 | |
| 7638 | 7317 | // the other operations have their own instructions for Wasm. |
| 7639 | 7318 | else => { |
| 7640 | try func.emitWValue(ptr); | |
| 7641 | try func.emitWValue(operand); | |
| 7642 | const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | |
| 7319 | try cg.emitWValue(ptr); | |
| 7320 | try cg.emitWValue(operand); | |
| 7321 | const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | |
| 7643 | 7322 | 1 => switch (op) { |
| 7644 | 7323 | .Xchg => .i32_atomic_rmw8_xchg_u, |
| 7645 | 7324 | .Add => .i32_atomic_rmw8_add_u, |
| ... | ... | @@ -7676,22 +7355,22 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7676 | 7355 | .Xor => .i64_atomic_rmw_xor, |
| 7677 | 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 | 7361 | .offset = ptr.offset(), |
| 7683 | 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 | 7367 | } else { |
| 7689 | const loaded = try func.load(ptr, ty, 0); | |
| 7690 | const result = try loaded.toLocal(func, ty); | |
| 7368 | const loaded = try cg.load(ptr, ty, 0); | |
| 7369 | const result = try loaded.toLocal(cg, ty); | |
| 7691 | 7370 | |
| 7692 | 7371 | switch (op) { |
| 7693 | 7372 | .Xchg => { |
| 7694 | try func.store(ptr, operand, ty, 0); | |
| 7373 | try cg.store(ptr, operand, ty, 0); | |
| 7695 | 7374 | }, |
| 7696 | 7375 | .Add, |
| 7697 | 7376 | .Sub, |
| ... | ... | @@ -7699,8 +7378,8 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7699 | 7378 | .Or, |
| 7700 | 7379 | .Xor, |
| 7701 | 7380 | => { |
| 7702 | try func.emitWValue(ptr); | |
| 7703 | _ = try func.binOp(result, operand, ty, switch (op) { | |
| 7381 | try cg.emitWValue(ptr); | |
| 7382 | _ = try cg.binOp(result, operand, ty, switch (op) { | |
| 7704 | 7383 | .Add => .add, |
| 7705 | 7384 | .Sub => .sub, |
| 7706 | 7385 | .And => .@"and", |
| ... | ... | @@ -7709,87 +7388,123 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7709 | 7388 | else => unreachable, |
| 7710 | 7389 | }); |
| 7711 | 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 | 7395 | .Max, |
| 7717 | 7396 | .Min, |
| 7718 | 7397 | => { |
| 7719 | try func.emitWValue(ptr); | |
| 7720 | try func.emitWValue(result); | |
| 7721 | try func.emitWValue(operand); | |
| 7722 | _ = try func.cmp(result, operand, ty, if (op == .Max) .gt else .lt); | |
| 7723 | try func.addTag(.select); | |
| 7724 | try func.store(.stack, .stack, ty, ptr.offset()); | |
| 7398 | try cg.emitWValue(ptr); | |
| 7399 | try cg.emitWValue(result); | |
| 7400 | try cg.emitWValue(operand); | |
| 7401 | _ = try cg.cmp(result, operand, ty, if (op == .Max) .gt else .lt); | |
| 7402 | try cg.addTag(.select); | |
| 7403 | try cg.store(.stack, .stack, ty, ptr.offset()); | |
| 7725 | 7404 | }, |
| 7726 | 7405 | .Nand => { |
| 7727 | 7406 | const wasm_bits = toWasmBits(@intCast(ty.bitSize(zcu))).?; |
| 7728 | 7407 | |
| 7729 | try func.emitWValue(ptr); | |
| 7730 | const and_res = try func.binOp(result, operand, ty, .@"and"); | |
| 7408 | try cg.emitWValue(ptr); | |
| 7409 | const and_res = try cg.binOp(result, operand, ty, .@"and"); | |
| 7731 | 7410 | if (wasm_bits == 32) |
| 7732 | try func.addImm32(~@as(u32, 0)) | |
| 7411 | try cg.addImm32(~@as(u32, 0)) | |
| 7733 | 7412 | else if (wasm_bits == 64) |
| 7734 | try func.addImm64(~@as(u64, 0)) | |
| 7413 | try cg.addImm64(~@as(u64, 0)) | |
| 7735 | 7414 | else |
| 7736 | return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); | |
| 7737 | _ = try func.binOp(and_res, .stack, ty, .xor); | |
| 7738 | try func.store(.stack, .stack, ty, ptr.offset()); | |
| 7415 | return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{}); | |
| 7416 | _ = try cg.binOp(and_res, .stack, ty, .xor); | |
| 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 { | |
| 7747 | const pt = func.pt; | |
| 7748 | const zcu = pt.zcu; | |
| 7749 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 7425 | fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7426 | const zcu = cg.pt.zcu; | |
| 7427 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 7750 | 7428 | |
| 7751 | const ptr = try func.resolveInst(bin_op.lhs); | |
| 7752 | const operand = try func.resolveInst(bin_op.rhs); | |
| 7753 | const ptr_ty = func.typeOf(bin_op.lhs); | |
| 7429 | const ptr = try cg.resolveInst(bin_op.lhs); | |
| 7430 | const operand = try cg.resolveInst(bin_op.rhs); | |
| 7431 | const ptr_ty = cg.typeOf(bin_op.lhs); | |
| 7754 | 7432 | const ty = ptr_ty.childType(zcu); |
| 7755 | 7433 | |
| 7756 | if (func.useAtomicFeature()) { | |
| 7757 | const tag: wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | |
| 7434 | if (cg.useAtomicFeature()) { | |
| 7435 | const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) { | |
| 7758 | 7436 | 1 => .i32_atomic_store8, |
| 7759 | 7437 | 2 => .i32_atomic_store16, |
| 7760 | 7438 | 4 => .i32_atomic_store, |
| 7761 | 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); | |
| 7765 | try func.lowerToStack(operand); | |
| 7766 | try func.addAtomicMemArg(tag, .{ | |
| 7442 | try cg.emitWValue(ptr); | |
| 7443 | try cg.lowerToStack(operand); | |
| 7444 | try cg.addAtomicMemArg(tag, .{ | |
| 7767 | 7445 | .offset = ptr.offset(), |
| 7768 | 7446 | .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?), |
| 7769 | 7447 | }); |
| 7770 | 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 { | |
| 7778 | if (func.initial_stack_value == .none) { | |
| 7779 | try func.initializeStack(); | |
| 7455 | fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 7456 | if (cg.initial_stack_value == .none) { | |
| 7457 | try cg.initializeStack(); | |
| 7780 | 7458 | } |
| 7781 | try func.emitWValue(func.bottom_stack_value); | |
| 7782 | return func.finishAir(inst, .stack, &.{}); | |
| 7459 | try cg.emitWValue(cg.bottom_stack_value); | |
| 7460 | return cg.finishAir(inst, .stack, &.{}); | |
| 7783 | 7461 | } |
| 7784 | 7462 | |
| 7785 | fn typeOf(func: *CodeGen, inst: Air.Inst.Ref) Type { | |
| 7786 | const pt = func.pt; | |
| 7787 | const zcu = pt.zcu; | |
| 7788 | return func.air.typeOf(inst, &zcu.intern_pool); | |
| 7463 | fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type { | |
| 7464 | const zcu = cg.pt.zcu; | |
| 7465 | return cg.air.typeOf(inst, &zcu.intern_pool); | |
| 7789 | 7466 | } |
| 7790 | 7467 | |
| 7791 | fn typeOfIndex(func: *CodeGen, inst: Air.Inst.Index) Type { | |
| 7792 | const pt = func.pt; | |
| 7793 | const zcu = pt.zcu; | |
| 7794 | return func.air.typeOfIndex(inst, &zcu.intern_pool); | |
| 7468 | fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type { | |
| 7469 | const zcu = cg.pt.zcu; | |
| 7470 | return cg.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 | //! Contains all logic to lower wasm MIR into its binary | |
| 2 | //! or textual representation. | |
| 3 | ||
| 4 | 1 | const Emit = @This(); |
| 2 | ||
| 5 | 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 | 9 | const Mir = @import("Mir.zig"); |
| 7 | 10 | const link = @import("../../link.zig"); |
| 8 | 11 | const Zcu = @import("../../Zcu.zig"); |
| 9 | 12 | const InternPool = @import("../../InternPool.zig"); |
| 10 | 13 | const codegen = @import("../../codegen.zig"); |
| 11 | const leb128 = std.leb; | |
| 12 | 14 | |
| 13 | /// Contains our list of instructions | |
| 14 | 15 | mir: Mir, |
| 15 | /// Reference to the Wasm module linker | |
| 16 | bin_file: *link.File.Wasm, | |
| 17 | /// Possible error message. When set, the value is allocated and | |
| 18 | /// must be freed manually. | |
| 19 | error_msg: ?*Zcu.ErrorMsg = null, | |
| 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{ | |
| 16 | wasm: *Wasm, | |
| 17 | /// The binary representation that will be emitted by this module. | |
| 18 | code: *std.ArrayListUnmanaged(u8), | |
| 19 | ||
| 20 | pub const Error = error{ | |
| 38 | 21 | OutOfMemory, |
| 39 | EmitFail, | |
| 40 | 22 | }; |
| 41 | 23 | |
| 42 | pub fn emitMir(emit: *Emit) InnerError!void { | |
| 43 | const mir_tags = emit.mir.instructions.items(.tag); | |
| 44 | // write the locals in the prologue of the function body | |
| 45 | // before we emit the function body when lowering MIR | |
| 46 | try emit.emitLocals(); | |
| 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.?; | |
| 24 | pub fn lowerToCode(emit: *Emit) Error!void { | |
| 25 | const mir = &emit.mir; | |
| 26 | const code = emit.code; | |
| 27 | const wasm = emit.wasm; | |
| 28 | const comp = wasm.base.comp; | |
| 259 | 29 | const gpa = comp.gpa; |
| 260 | emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args); | |
| 261 | return error.EmitFail; | |
| 262 | } | |
| 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(); | |
| 30 | const is_obj = comp.config.output_mode == .Obj; | |
| 31 | const target = &comp.root_mod.resolved_target.result; | |
| 32 | const is_wasm32 = target.cpu.arch == .wasm32; | |
| 289 | 33 | |
| 290 | try emit.code.append(std.wasm.opcode(.br_table)); | |
| 291 | try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth | |
| 292 | for (labels) |label| { | |
| 293 | try leb128.writeUleb128(writer, label); | |
| 294 | } | |
| 295 | } | |
| 34 | const tags = mir.instruction_tags; | |
| 35 | const datas = mir.instruction_datas; | |
| 36 | var inst: u32 = 0; | |
| 296 | 37 | |
| 297 | fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { | |
| 298 | const label = emit.mir.instructions.items(.data)[inst].label; | |
| 299 | try emit.code.append(@intFromEnum(tag)); | |
| 300 | try leb128.writeUleb128(emit.code.writer(), label); | |
| 301 | } | |
| 38 | loop: switch (tags[inst]) { | |
| 39 | .dbg_epilogue_begin => { | |
| 40 | return; | |
| 41 | }, | |
| 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 { | |
| 304 | const comp = emit.bin_file.base.comp; | |
| 305 | const gpa = comp.gpa; | |
| 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 | } | |
| 131 | inst += 1; | |
| 132 | continue :loop tags[inst]; | |
| 133 | }, | |
| 321 | 134 | |
| 322 | fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 323 | const value: i32 = emit.mir.instructions.items(.data)[inst].imm32; | |
| 324 | try emit.code.append(std.wasm.opcode(.i32_const)); | |
| 325 | try leb128.writeIleb128(emit.code.writer(), value); | |
| 326 | } | |
| 135 | .local_get, .local_set, .local_tee => { | |
| 136 | try code.ensureUnusedCapacity(gpa, 11); | |
| 137 | code.appendAssumeCapacity(@intFromEnum(tags[inst])); | |
| 138 | leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable; | |
| 327 | 139 | |
| 328 | fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 329 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 330 | const value = emit.mir.extraData(Mir.Imm64, extra_index); | |
| 331 | try emit.code.append(std.wasm.opcode(.i64_const)); | |
| 332 | try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64()))); | |
| 333 | } | |
| 140 | inst += 1; | |
| 141 | continue :loop tags[inst]; | |
| 142 | }, | |
| 334 | 143 | |
| 335 | fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 336 | const value: f32 = emit.mir.instructions.items(.data)[inst].float32; | |
| 337 | try emit.code.append(std.wasm.opcode(.f32_const)); | |
| 338 | try emit.code.writer().writeInt(u32, @bitCast(value), .little); | |
| 339 | } | |
| 144 | .br_table => { | |
| 145 | const extra_index = datas[inst].payload; | |
| 146 | const extra = mir.extraData(Mir.JumpTable, extra_index); | |
| 147 | const labels = mir.extra[extra.end..][0..extra.data.length]; | |
| 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 { | |
| 342 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 343 | const value = emit.mir.extraData(Mir.Float64, extra_index); | |
| 344 | try emit.code.append(std.wasm.opcode(.f64_const)); | |
| 345 | try emit.code.writer().writeInt(u64, value.data.toU64(), .little); | |
| 346 | } | |
| 158 | .call_nav => { | |
| 159 | try code.ensureUnusedCapacity(gpa, 6); | |
| 160 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); | |
| 161 | if (is_obj) { | |
| 162 | try wasm.out_relocs.append(gpa, .{ | |
| 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 { | |
| 349 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 350 | const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data; | |
| 351 | try emit.code.append(@intFromEnum(tag)); | |
| 352 | try encodeMemArg(mem_arg, emit.code.writer()); | |
| 353 | } | |
| 177 | .call_indirect => { | |
| 178 | try code.ensureUnusedCapacity(gpa, 11); | |
| 179 | const func_ty_index = datas[inst].func_ty; | |
| 180 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect)); | |
| 181 | if (is_obj) { | |
| 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 { | |
| 356 | // wasm encodes alignment as power of 2, rather than natural alignment | |
| 357 | const encoded_alignment = @ctz(mem_arg.alignment); | |
| 358 | try leb128.writeUleb128(writer, encoded_alignment); | |
| 359 | try leb128.writeUleb128(writer, mem_arg.offset); | |
| 360 | } | |
| 199 | .call_tag_name => { | |
| 200 | try code.ensureUnusedCapacity(gpa, 6); | |
| 201 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); | |
| 202 | if (is_obj) { | |
| 203 | try wasm.out_relocs.append(gpa, .{ | |
| 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 { | |
| 363 | const comp = emit.bin_file.base.comp; | |
| 364 | const gpa = comp.gpa; | |
| 365 | const label = emit.mir.instructions.items(.data)[inst].label; | |
| 366 | try emit.code.append(std.wasm.opcode(.call)); | |
| 367 | const call_offset = emit.offset(); | |
| 368 | var buf: [5]u8 = undefined; | |
| 369 | leb128.writeUnsignedFixed(5, &buf, label); | |
| 370 | try emit.code.appendSlice(&buf); | |
| 371 | ||
| 372 | if (label != 0) { | |
| 373 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | |
| 374 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 375 | try atom.relocs.append(gpa, .{ | |
| 376 | .offset = call_offset, | |
| 377 | .index = label, | |
| 378 | .relocation_type = .R_WASM_FUNCTION_INDEX_LEB, | |
| 379 | }); | |
| 380 | } | |
| 381 | } | |
| 218 | .call_intrinsic => { | |
| 219 | // Although this currently uses `wasm.internString`, note that it | |
| 220 | // *could* be changed to directly index into a preloaded strings | |
| 221 | // table initialized based on the `Mir.Intrinsic` enum. | |
| 222 | const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic)); | |
| 223 | ||
| 224 | try code.ensureUnusedCapacity(gpa, 6); | |
| 225 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); | |
| 226 | if (is_obj) { | |
| 227 | try wasm.out_relocs.append(gpa, .{ | |
| 228 | .offset = @intCast(code.items.len), | |
| 229 | .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) }, | |
| 230 | .tag = .function_index_leb, | |
| 231 | .addend = 0, | |
| 232 | }); | |
| 233 | code.appendNTimesAssumeCapacity(0, 5); | |
| 234 | } else { | |
| 235 | appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name)); | |
| 236 | } | |
| 237 | ||
| 238 | inst += 1; | |
| 239 | continue :loop tags[inst]; | |
| 240 | }, | |
| 382 | 241 | |
| 383 | fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 384 | const type_index = emit.mir.instructions.items(.data)[inst].label; | |
| 385 | try emit.code.append(std.wasm.opcode(.call_indirect)); | |
| 386 | // NOTE: If we remove unused function types in the future for incremental | |
| 387 | // linking, we must also emit a relocation for this `type_index` | |
| 388 | const call_offset = emit.offset(); | |
| 389 | var buf: [5]u8 = undefined; | |
| 390 | leb128.writeUnsignedFixed(5, &buf, type_index); | |
| 391 | try emit.code.appendSlice(&buf); | |
| 392 | if (type_index != 0) { | |
| 393 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | |
| 394 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 395 | try atom.relocs.append(emit.bin_file.base.comp.gpa, .{ | |
| 396 | .offset = call_offset, | |
| 397 | .index = type_index, | |
| 398 | .relocation_type = .R_WASM_TYPE_INDEX_LEB, | |
| 399 | }); | |
| 400 | } | |
| 401 | try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index | |
| 402 | } | |
| 242 | .global_set_sp => { | |
| 243 | try code.ensureUnusedCapacity(gpa, 6); | |
| 244 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); | |
| 245 | if (is_obj) { | |
| 246 | try wasm.out_relocs.append(gpa, .{ | |
| 247 | .offset = @intCast(code.items.len), | |
| 248 | .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() }, | |
| 249 | .tag = .global_index_leb, | |
| 250 | .addend = 0, | |
| 251 | }); | |
| 252 | code.appendNTimesAssumeCapacity(0, 5); | |
| 253 | } else { | |
| 254 | const sp_global: Wasm.GlobalIndex = .stack_pointer; | |
| 255 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | |
| 256 | } | |
| 257 | ||
| 258 | inst += 1; | |
| 259 | continue :loop tags[inst]; | |
| 260 | }, | |
| 403 | 261 | |
| 404 | fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 405 | const comp = emit.bin_file.base.comp; | |
| 406 | const gpa = comp.gpa; | |
| 407 | const symbol_index = emit.mir.instructions.items(.data)[inst].label; | |
| 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 | } | |
| 262 | .f32_const => { | |
| 263 | try code.ensureUnusedCapacity(gpa, 5); | |
| 264 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const)); | |
| 265 | std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little); | |
| 424 | 266 | |
| 425 | fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 426 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 427 | const mem = emit.mir.extraData(Mir.Memory, extra_index).data; | |
| 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 | } | |
| 267 | inst += 1; | |
| 268 | continue :loop tags[inst]; | |
| 269 | }, | |
| 444 | 270 | |
| 445 | if (mem.pointer != 0) { | |
| 446 | const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; | |
| 447 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 448 | try atom.relocs.append(gpa, .{ | |
| 449 | .offset = mem_offset, | |
| 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 | } | |
| 271 | .f64_const => { | |
| 272 | try code.ensureUnusedCapacity(gpa, 9); | |
| 273 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const)); | |
| 274 | const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data; | |
| 275 | std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little); | |
| 456 | 276 | |
| 457 | fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 458 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 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 | |
| 277 | inst += 1; | |
| 278 | continue :loop tags[inst]; | |
| 473 | 279 | }, |
| 474 | .memory_fill => { | |
| 475 | try leb128.writeUleb128(writer, @as(u32, 0)); // memory index | |
| 280 | .i32_const => { | |
| 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 => { | |
| 478 | try leb128.writeUleb128(writer, @as(u32, 0)); // dst memory index | |
| 479 | try leb128.writeUleb128(writer, @as(u32, 0)); // src memory index | |
| 288 | .i64_const => { | |
| 289 | try code.ensureUnusedCapacity(gpa, 11); | |
| 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 | |
| 483 | .i32_trunc_sat_f32_s, | |
| 484 | .i32_trunc_sat_f32_u, | |
| 485 | .i32_trunc_sat_f64_s, | |
| 486 | .i32_trunc_sat_f64_u, | |
| 487 | .i64_trunc_sat_f32_s, | |
| 488 | .i64_trunc_sat_f32_u, | |
| 489 | .i64_trunc_sat_f64_s, | |
| 490 | .i64_trunc_sat_f64_u, | |
| 491 | => {}, // opcode already written | |
| 492 | else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}), | |
| 493 | } | |
| 494 | } | |
| 495 | ||
| 496 | fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 497 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 498 | const opcode = emit.mir.extra[extra_index]; | |
| 499 | const writer = emit.code.writer(); | |
| 500 | try emit.code.append(std.wasm.opcode(.simd_prefix)); | |
| 501 | try leb128.writeUleb128(writer, opcode); | |
| 502 | switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) { | |
| 503 | .v128_store, | |
| 504 | .v128_load, | |
| 505 | .v128_load8_splat, | |
| 506 | .v128_load16_splat, | |
| 507 | .v128_load32_splat, | |
| 508 | .v128_load64_splat, | |
| 298 | .i32_load, | |
| 299 | .i64_load, | |
| 300 | .f32_load, | |
| 301 | .f64_load, | |
| 302 | .i32_load8_s, | |
| 303 | .i32_load8_u, | |
| 304 | .i32_load16_s, | |
| 305 | .i32_load16_u, | |
| 306 | .i64_load8_s, | |
| 307 | .i64_load8_u, | |
| 308 | .i64_load16_s, | |
| 309 | .i64_load16_u, | |
| 310 | .i64_load32_s, | |
| 311 | .i64_load32_u, | |
| 312 | .i32_store, | |
| 313 | .i64_store, | |
| 314 | .f32_store, | |
| 315 | .f64_store, | |
| 316 | .i32_store8, | |
| 317 | .i32_store16, | |
| 318 | .i64_store8, | |
| 319 | .i64_store16, | |
| 320 | .i64_store32, | |
| 509 | 321 | => { |
| 510 | const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data; | |
| 511 | try encodeMemArg(mem_arg, writer); | |
| 322 | try code.ensureUnusedCapacity(gpa, 1 + 20); | |
| 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, | |
| 514 | .i8x16_shuffle, | |
| 515 | => { | |
| 516 | const simd_value = emit.mir.extra[extra_index + 1 ..][0..4]; | |
| 517 | try writer.writeAll(std.mem.asBytes(simd_value)); | |
| 518 | }, | |
| 519 | .i8x16_extract_lane_s, | |
| 520 | .i8x16_extract_lane_u, | |
| 521 | .i8x16_replace_lane, | |
| 522 | .i16x8_extract_lane_s, | |
| 523 | .i16x8_extract_lane_u, | |
| 524 | .i16x8_replace_lane, | |
| 525 | .i32x4_extract_lane, | |
| 526 | .i32x4_replace_lane, | |
| 527 | .i64x2_extract_lane, | |
| 528 | .i64x2_replace_lane, | |
| 529 | .f32x4_extract_lane, | |
| 530 | .f32x4_replace_lane, | |
| 531 | .f64x2_extract_lane, | |
| 532 | .f64x2_replace_lane, | |
| 328 | ||
| 329 | .end, | |
| 330 | .@"return", | |
| 331 | .@"unreachable", | |
| 332 | .select, | |
| 333 | .i32_eqz, | |
| 334 | .i32_eq, | |
| 335 | .i32_ne, | |
| 336 | .i32_lt_s, | |
| 337 | .i32_lt_u, | |
| 338 | .i32_gt_s, | |
| 339 | .i32_gt_u, | |
| 340 | .i32_le_s, | |
| 341 | .i32_le_u, | |
| 342 | .i32_ge_s, | |
| 343 | .i32_ge_u, | |
| 344 | .i64_eqz, | |
| 345 | .i64_eq, | |
| 346 | .i64_ne, | |
| 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 { | |
| 548 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | |
| 549 | const opcode = emit.mir.extra[extra_index]; | |
| 550 | const writer = emit.code.writer(); | |
| 551 | try emit.code.append(std.wasm.opcode(.atomics_prefix)); | |
| 552 | try leb128.writeUleb128(writer, opcode); | |
| 553 | switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) { | |
| 554 | .i32_atomic_load, | |
| 555 | .i64_atomic_load, | |
| 556 | .i32_atomic_load8_u, | |
| 557 | .i32_atomic_load16_u, | |
| 558 | .i64_atomic_load8_u, | |
| 559 | .i64_atomic_load16_u, | |
| 560 | .i64_atomic_load32_u, | |
| 561 | .i32_atomic_store, | |
| 562 | .i64_atomic_store, | |
| 563 | .i32_atomic_store8, | |
| 564 | .i32_atomic_store16, | |
| 565 | .i64_atomic_store8, | |
| 566 | .i64_atomic_store16, | |
| 567 | .i64_atomic_store32, | |
| 568 | .i32_atomic_rmw_add, | |
| 569 | .i64_atomic_rmw_add, | |
| 570 | .i32_atomic_rmw8_add_u, | |
| 571 | .i32_atomic_rmw16_add_u, | |
| 572 | .i64_atomic_rmw8_add_u, | |
| 573 | .i64_atomic_rmw16_add_u, | |
| 574 | .i64_atomic_rmw32_add_u, | |
| 575 | .i32_atomic_rmw_sub, | |
| 576 | .i64_atomic_rmw_sub, | |
| 577 | .i32_atomic_rmw8_sub_u, | |
| 578 | .i32_atomic_rmw16_sub_u, | |
| 579 | .i64_atomic_rmw8_sub_u, | |
| 580 | .i64_atomic_rmw16_sub_u, | |
| 581 | .i64_atomic_rmw32_sub_u, | |
| 582 | .i32_atomic_rmw_and, | |
| 583 | .i64_atomic_rmw_and, | |
| 584 | .i32_atomic_rmw8_and_u, | |
| 585 | .i32_atomic_rmw16_and_u, | |
| 586 | .i64_atomic_rmw8_and_u, | |
| 587 | .i64_atomic_rmw16_and_u, | |
| 588 | .i64_atomic_rmw32_and_u, | |
| 589 | .i32_atomic_rmw_or, | |
| 590 | .i64_atomic_rmw_or, | |
| 591 | .i32_atomic_rmw8_or_u, | |
| 592 | .i32_atomic_rmw16_or_u, | |
| 593 | .i64_atomic_rmw8_or_u, | |
| 594 | .i64_atomic_rmw16_or_u, | |
| 595 | .i64_atomic_rmw32_or_u, | |
| 596 | .i32_atomic_rmw_xor, | |
| 597 | .i64_atomic_rmw_xor, | |
| 598 | .i32_atomic_rmw8_xor_u, | |
| 599 | .i32_atomic_rmw16_xor_u, | |
| 600 | .i64_atomic_rmw8_xor_u, | |
| 601 | .i64_atomic_rmw16_xor_u, | |
| 602 | .i64_atomic_rmw32_xor_u, | |
| 603 | .i32_atomic_rmw_xchg, | |
| 604 | .i64_atomic_rmw_xchg, | |
| 605 | .i32_atomic_rmw8_xchg_u, | |
| 606 | .i32_atomic_rmw16_xchg_u, | |
| 607 | .i64_atomic_rmw8_xchg_u, | |
| 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); | |
| 463 | .misc_prefix => { | |
| 464 | try code.ensureUnusedCapacity(gpa, 6 + 6); | |
| 465 | const extra_index = datas[inst].payload; | |
| 466 | const opcode = mir.extra[extra_index]; | |
| 467 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); | |
| 468 | leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable; | |
| 469 | switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) { | |
| 470 | // bulk-memory opcodes | |
| 471 | .data_drop => { | |
| 472 | const segment = mir.extra[extra_index + 1]; | |
| 473 | leb.writeUleb128(code.fixedWriter(), segment) catch unreachable; | |
| 474 | ||
| 475 | inst += 1; | |
| 476 | continue :loop tags[inst]; | |
| 477 | }, | |
| 478 | .memory_init => { | |
| 479 | const segment = mir.extra[extra_index + 1]; | |
| 480 | leb.writeUleb128(code.fixedWriter(), segment) catch unreachable; | |
| 481 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index | |
| 482 | ||
| 483 | inst += 1; | |
| 484 | continue :loop tags[inst]; | |
| 485 | }, | |
| 486 | .memory_fill => { | |
| 487 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index | |
| 488 | ||
| 489 | inst += 1; | |
| 490 | continue :loop tags[inst]; | |
| 491 | }, | |
| 492 | .memory_copy => { | |
| 493 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index | |
| 494 | leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index | |
| 495 | ||
| 496 | inst += 1; | |
| 497 | continue :loop tags[inst]; | |
| 498 | }, | |
| 499 | ||
| 500 | // nontrapping-float-to-int-conversion opcodes | |
| 501 | .i32_trunc_sat_f32_s, | |
| 502 | .i32_trunc_sat_f32_u, | |
| 503 | .i32_trunc_sat_f64_s, | |
| 504 | .i32_trunc_sat_f64_u, | |
| 505 | .i64_trunc_sat_f32_s, | |
| 506 | .i64_trunc_sat_f32_u, | |
| 507 | .i64_trunc_sat_f64_s, | |
| 508 | .i64_trunc_sat_f64_u, | |
| 509 | => { | |
| 510 | inst += 1; | |
| 511 | continue :loop tags[inst]; | |
| 512 | }, | |
| 513 | ||
| 514 | .table_init => @panic("TODO"), | |
| 515 | .elem_drop => @panic("TODO"), | |
| 516 | .table_copy => @panic("TODO"), | |
| 517 | .table_grow => @panic("TODO"), | |
| 518 | .table_size => @panic("TODO"), | |
| 519 | .table_fill => @panic("TODO"), | |
| 520 | ||
| 521 | _ => unreachable, | |
| 522 | } | |
| 523 | comptime unreachable; | |
| 621 | 524 | }, |
| 622 | .atomic_fence => { | |
| 623 | // TODO: When multi-memory proposal is accepted and implemented in the compiler, | |
| 624 | // change this to (user-)specified index, rather than hardcode it to memory index 0. | |
| 625 | const memory_index: u32 = 0; | |
| 626 | try leb128.writeUleb128(writer, memory_index); | |
| 525 | .simd_prefix => { | |
| 526 | try code.ensureUnusedCapacity(gpa, 6 + 20); | |
| 527 | const extra_index = datas[inst].payload; | |
| 528 | const opcode = mir.extra[extra_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 { | |
| 633 | try emit.code.append(0xFC); | |
| 634 | try emit.code.append(0x0B); | |
| 635 | // When multi-memory proposal reaches phase 4, we | |
| 636 | // can emit a different memory index here. | |
| 637 | // For now we will always emit index 0. | |
| 638 | try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); | |
| 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); | |
| 906 | /// Asserts 20 unused capacity. | |
| 907 | fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void { | |
| 908 | assert(code.unusedCapacitySlice().len >= 20); | |
| 909 | // Wasm encodes alignment as power of 2, rather than natural alignment. | |
| 910 | const encoded_alignment = @ctz(mem_arg.alignment); | |
| 911 | leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable; | |
| 912 | leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable; | |
| 645 | 913 | } |
| 646 | 914 | |
| 647 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { | |
| 648 | if (emit.dbg_output != .dwarf) return; | |
| 915 | fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void { | |
| 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)); | |
| 651 | const delta_pc = emit.offset() - emit.prev_di_offset; | |
| 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); | |
| 920 | try code.ensureUnusedCapacity(gpa, 11); | |
| 921 | code.appendAssumeCapacity(@intFromEnum(opcode)); | |
| 655 | 922 | |
| 656 | emit.prev_di_line = line; | |
| 657 | emit.prev_di_column = column; | |
| 658 | emit.prev_di_offset = emit.offset(); | |
| 923 | try wasm.out_relocs.append(gpa, .{ | |
| 924 | .offset = @intCast(code.items.len), | |
| 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 { | |
| 662 | if (emit.dbg_output != .dwarf) return; | |
| 932 | fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void { | |
| 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(); | |
| 665 | try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); | |
| 940 | const addr = wasm.uavAddr(data.uav_exe); | |
| 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 { | |
| 669 | if (emit.dbg_output != .dwarf) return; | |
| 944 | fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void { | |
| 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(); | |
| 672 | try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); | |
| 971 | fn appendOutputFunctionIndex(code: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) void { | |
| 972 | leb.writeUleb128(code.fixedWriter(), @intFromEnum(i)) catch unreachable; | |
| 673 | 973 | } |
src/arch/wasm/Mir.zig+338-102| ... | ... | @@ -7,11 +7,15 @@ |
| 7 | 7 | //! and known jump labels for blocks. |
| 8 | 8 | |
| 9 | 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 | 14 | const std = @import("std"); |
| 15 | const assert = std.debug.assert; | |
| 12 | 16 | |
| 13 | /// A struct of array that represents each individual wasm | |
| 14 | instructions: std.MultiArrayList(Inst).Slice, | |
| 17 | instruction_tags: []const Inst.Tag, | |
| 18 | instruction_datas: []const Inst.Data, | |
| 15 | 19 | /// A slice of indexes where the meaning of the data is determined by the |
| 16 | 20 | /// `Inst.Tag` value. |
| 17 | 21 | extra: []const u32, |
| ... | ... | @@ -26,16 +30,14 @@ pub const Inst = struct { |
| 26 | 30 | /// The position of a given MIR isntruction with the instruction list. |
| 27 | 31 | pub const Index = u32; |
| 28 | 32 | |
| 29 | /// Contains all possible wasm opcodes the Zig compiler may emit | |
| 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. | |
| 33 | /// Some tags match wasm opcode values to facilitate trivial lowering. | |
| 36 | 34 | pub const Tag = enum(u8) { |
| 37 | /// Uses `nop` | |
| 35 | /// Uses `tag`. | |
| 38 | 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 | 41 | /// Creates a new block that can be jump from. |
| 40 | 42 | /// |
| 41 | 43 | /// Type of the block is given in data `block_type` |
| ... | ... | @@ -44,56 +46,92 @@ pub const Inst = struct { |
| 44 | 46 | /// |
| 45 | 47 | /// Type of the loop is given in data `block_type` |
| 46 | 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 | 85 | /// Inserts debug information about the current line and column |
| 48 | 86 | /// of the source code |
| 49 | 87 | /// |
| 50 | 88 | /// Uses `payload` of which the payload type is `DbgLineColumn` |
| 51 | dbg_line = 0x06, | |
| 52 | /// Emits epilogue begin debug information | |
| 53 | /// | |
| 54 | /// Uses `nop` | |
| 55 | dbg_epilogue_begin = 0x07, | |
| 56 | /// Emits prologue end debug information | |
| 57 | /// | |
| 58 | /// Uses `nop` | |
| 59 | dbg_prologue_end = 0x08, | |
| 89 | dbg_line, | |
| 90 | /// Lowers to an i32_const containing the number of unique Zig error | |
| 91 | /// names. | |
| 92 | /// Uses `tag`. | |
| 93 | errors_len, | |
| 60 | 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 | 97 | end = 0x0B, |
| 64 | 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 | 101 | br = 0x0C, |
| 68 | 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 | 105 | br_if = 0x0D, |
| 72 | 106 | /// Jump table that takes the stack value as an index where each value |
| 73 | 107 | /// represents the label to jump to. |
| 74 | 108 | /// |
| 75 | 109 | /// Data is extra of which the Payload's type is `JumpTable` |
| 76 | br_table = 0x0E, | |
| 110 | br_table, | |
| 77 | 111 | /// Returns from the function |
| 78 | 112 | /// |
| 79 | /// Uses `nop` | |
| 113 | /// Uses `tag`. | |
| 80 | 114 | @"return" = 0x0F, |
| 81 | /// Calls a function by its index | |
| 82 | /// | |
| 83 | /// Uses `label` | |
| 84 | call = 0x10, | |
| 115 | /// Lowers to an i32_const (wasm32) or i64_const (wasm64) containing | |
| 116 | /// the base address of the table of error code names, with each | |
| 117 | /// element being a null-terminated slice. | |
| 118 | /// | |
| 119 | /// Uses `tag`. | |
| 120 | error_name_table_ref, | |
| 121 | /// Calls a function using `nav_index`. | |
| 122 | call_nav, | |
| 85 | 123 | /// Calls a function pointer by its function signature |
| 86 | 124 | /// and index into the function table. |
| 87 | 125 | /// |
| 88 | /// Uses `label` | |
| 89 | call_indirect = 0x11, | |
| 90 | /// Contains a symbol to a function pointer | |
| 91 | /// uses `label` | |
| 92 | /// | |
| 93 | /// Note: This uses `0x16` as value which is reserved by the WebAssembly | |
| 94 | /// specification but unused, meaning we must update this if the specification were to | |
| 95 | /// use this value. | |
| 96 | function_index = 0x16, | |
| 126 | /// Uses `func_ty` | |
| 127 | call_indirect, | |
| 128 | /// Calls a function by its index. | |
| 129 | /// | |
| 130 | /// The function is the auto-generated tag name function for the type | |
| 131 | /// provided in `ip_index`. | |
| 132 | call_tag_name, | |
| 133 | /// Lowers to a `call` instruction, using `intrinsic`. | |
| 134 | call_intrinsic, | |
| 97 | 135 | /// Pops three values from the stack and pushes |
| 98 | 136 | /// the first or second value dependent on the third value. |
| 99 | 137 | /// Uses `tag` |
| ... | ... | @@ -112,15 +150,11 @@ pub const Inst = struct { |
| 112 | 150 | /// |
| 113 | 151 | /// Uses `label` |
| 114 | 152 | local_tee = 0x22, |
| 115 | /// Loads a (mutable) global at given index onto the stack | |
| 116 | /// | |
| 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. | |
| 153 | /// Pops a value from the stack and sets the stack pointer global. | |
| 154 | /// The value must be the same type as the stack pointer global. | |
| 121 | 155 | /// |
| 122 | /// Uses `label`. | |
| 123 | global_set = 0x24, | |
| 156 | /// Uses `tag` (no additional data). | |
| 157 | global_set_sp, | |
| 124 | 158 | /// Loads a 32-bit integer from memory (data section) onto the stack |
| 125 | 159 | /// Pops the value from the stack which represents the offset into memory. |
| 126 | 160 | /// |
| ... | ... | @@ -256,19 +290,19 @@ pub const Inst = struct { |
| 256 | 290 | /// Loads a 32-bit signed immediate value onto the stack |
| 257 | 291 | /// |
| 258 | 292 | /// Uses `imm32` |
| 259 | i32_const = 0x41, | |
| 293 | i32_const, | |
| 260 | 294 | /// Loads a i64-bit signed immediate value onto the stack |
| 261 | 295 | /// |
| 262 | 296 | /// uses `payload` of type `Imm64` |
| 263 | i64_const = 0x42, | |
| 297 | i64_const, | |
| 264 | 298 | /// Loads a 32-bit float value onto the stack. |
| 265 | 299 | /// |
| 266 | 300 | /// Uses `float32` |
| 267 | f32_const = 0x43, | |
| 301 | f32_const, | |
| 268 | 302 | /// Loads a 64-bit float value onto the stack. |
| 269 | 303 | /// |
| 270 | 304 | /// Uses `payload` of type `Float64` |
| 271 | f64_const = 0x44, | |
| 305 | f64_const, | |
| 272 | 306 | /// Uses `tag` |
| 273 | 307 | i32_eqz = 0x45, |
| 274 | 308 | /// Uses `tag` |
| ... | ... | @@ -522,25 +556,19 @@ pub const Inst = struct { |
| 522 | 556 | /// |
| 523 | 557 | /// The `data` field depends on the extension instruction and |
| 524 | 558 | /// may contain additional data. |
| 525 | misc_prefix = 0xFC, | |
| 559 | misc_prefix, | |
| 526 | 560 | /// The instruction consists of a simd opcode. |
| 527 | 561 | /// The actual simd-opcode is found at payload's index. |
| 528 | 562 | /// |
| 529 | 563 | /// The `data` field depends on the simd instruction and |
| 530 | 564 | /// may contain additional data. |
| 531 | simd_prefix = 0xFD, | |
| 565 | simd_prefix, | |
| 532 | 566 | /// The instruction consists of an atomics opcode. |
| 533 | 567 | /// The actual atomics-opcode is found at payload's index. |
| 534 | 568 | /// |
| 535 | 569 | /// The `data` field depends on the atomics instruction and |
| 536 | 570 | /// may contain additional data. |
| 537 | 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 | 573 | /// From a given wasm opcode, returns a MIR tag. |
| 546 | 574 | pub fn fromOpcode(opcode: std.wasm.Opcode) Tag { |
| ... | ... | @@ -560,26 +588,41 @@ pub const Inst = struct { |
| 560 | 588 | /// Uses no additional data |
| 561 | 589 | tag: void, |
| 562 | 590 | /// Contains the result type of a block |
| 563 | /// | |
| 564 | /// Used by `block` and `loop` | |
| 565 | block_type: u8, | |
| 566 | /// Contains an u32 index into a wasm section entry, such as a local. | |
| 567 | /// Note: This is not an index to another instruction. | |
| 568 | /// | |
| 569 | /// Used by e.g. `local_get`, `local_set`, etc. | |
| 591 | block_type: std.wasm.BlockType, | |
| 592 | /// Label: Each structured control instruction introduces an implicit label. | |
| 593 | /// Labels are targets for branch instructions that reference them with | |
| 594 | /// label indices. Unlike with other index spaces, indexing of labels | |
| 595 | /// is relative by nesting depth, that is, label 0 refers to the | |
| 596 | /// innermost structured control instruction enclosing the referring | |
| 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 | 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 | 605 | /// A 32-bit immediate value. |
| 572 | /// | |
| 573 | /// Used by `i32_const` | |
| 574 | 606 | imm32: i32, |
| 575 | 607 | /// A 32-bit float value |
| 576 | /// | |
| 577 | /// Used by `f32_float` | |
| 578 | 608 | float32: f32, |
| 579 | 609 | /// Index into `extra`. Meaning of what can be found there is context-dependent. |
| 580 | /// | |
| 581 | /// Used by e.g. `br_table` | |
| 582 | 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 | 639 | inline for (fields) |field| { |
| 597 | 640 | @field(result, field.name) = switch (field.type) { |
| 598 | 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 | 647 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 600 | 648 | }; |
| 601 | 649 | i += 1; |
| ... | ... | @@ -609,28 +657,19 @@ pub const JumpTable = struct { |
| 609 | 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 | 660 | pub const Imm64 = struct { |
| 619 | 661 | msb: u32, |
| 620 | 662 | lsb: u32, |
| 621 | 663 | |
| 622 | pub fn fromU64(imm: u64) Imm64 { | |
| 664 | pub fn init(full: u64) Imm64 { | |
| 623 | 665 | return .{ |
| 624 | .msb = @as(u32, @truncate(imm >> 32)), | |
| 625 | .lsb = @as(u32, @truncate(imm)), | |
| 666 | .msb = @truncate(full >> 32), | |
| 667 | .lsb = @truncate(full), | |
| 626 | 668 | }; |
| 627 | 669 | } |
| 628 | 670 | |
| 629 | pub fn toU64(self: Imm64) u64 { | |
| 630 | var result: u64 = 0; | |
| 631 | result |= @as(u64, self.msb) << 32; | |
| 632 | result |= @as(u64, self.lsb); | |
| 633 | return result; | |
| 671 | pub fn toInt(i: Imm64) u64 { | |
| 672 | return (@as(u64, i.msb) << 32) | @as(u64, i.lsb); | |
| 634 | 673 | } |
| 635 | 674 | }; |
| 636 | 675 | |
| ... | ... | @@ -638,23 +677,16 @@ pub const Float64 = struct { |
| 638 | 677 | msb: u32, |
| 639 | 678 | lsb: u32, |
| 640 | 679 | |
| 641 | pub fn fromFloat64(float: f64) Float64 { | |
| 642 | const tmp = @as(u64, @bitCast(float)); | |
| 680 | pub fn init(f: f64) Float64 { | |
| 681 | const int: u64 = @bitCast(f); | |
| 643 | 682 | return .{ |
| 644 | .msb = @as(u32, @truncate(tmp >> 32)), | |
| 645 | .lsb = @as(u32, @truncate(tmp)), | |
| 683 | .msb = @truncate(int >> 32), | |
| 684 | .lsb = @truncate(int), | |
| 646 | 685 | }; |
| 647 | 686 | } |
| 648 | 687 | |
| 649 | pub fn toF64(self: Float64) f64 { | |
| 650 | @as(f64, @bitCast(self.toU64())); | |
| 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; | |
| 688 | pub fn toInt(f: Float64) u64 { | |
| 689 | return (@as(u64, f.msb) << 32) | @as(u64, f.lsb); | |
| 658 | 690 | } |
| 659 | 691 | }; |
| 660 | 692 | |
| ... | ... | @@ -663,11 +695,19 @@ pub const MemArg = struct { |
| 663 | 695 | alignment: u32, |
| 664 | 696 | }; |
| 665 | 697 | |
| 666 | /// Represents a memory address, which holds both the pointer | |
| 667 | /// or the parent pointer and the offset to it. | |
| 668 | pub const Memory = struct { | |
| 669 | pointer: u32, | |
| 670 | offset: u32, | |
| 698 | pub const UavRefOffObj = struct { | |
| 699 | uav_obj: Wasm.UavsObjIndex, | |
| 700 | offset: i32, | |
| 701 | }; | |
| 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 | 713 | /// Maps a source line with wasm bytecode |
| ... | ... | @@ -675,3 +715,199 @@ pub const DbgLineColumn = struct { |
| 675 | 715 | line: u32, |
| 676 | 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 | 22 | /// Classifies a given Zig type to determine how they must be passed |
| 23 | 23 | /// or returned as value within a wasm function. |
| 24 | 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 | 26 | const ip = &zcu.intern_pool; |
| 27 | 27 | const target = zcu.getTarget(); |
| 28 | 28 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none; |
src/arch/x86_64/CodeGen.zig+36-85| ... | ... | @@ -19,7 +19,6 @@ const Allocator = mem.Allocator; |
| 19 | 19 | const CodeGenError = codegen.CodeGenError; |
| 20 | 20 | const Compilation = @import("../../Compilation.zig"); |
| 21 | 21 | const ErrorMsg = Zcu.ErrorMsg; |
| 22 | const Result = codegen.Result; | |
| 23 | 22 | const Emit = @import("Emit.zig"); |
| 24 | 23 | const Liveness = @import("../../Liveness.zig"); |
| 25 | 24 | const Lower = @import("Lower.zig"); |
| ... | ... | @@ -59,7 +58,6 @@ target: *const std.Target, |
| 59 | 58 | owner: Owner, |
| 60 | 59 | inline_func: InternPool.Index, |
| 61 | 60 | mod: *Package.Module, |
| 62 | err_msg: ?*ErrorMsg, | |
| 63 | 61 | arg_index: u32, |
| 64 | 62 | args: []MCValue, |
| 65 | 63 | va_info: union { |
| ... | ... | @@ -819,9 +817,9 @@ pub fn generate( |
| 819 | 817 | func_index: InternPool.Index, |
| 820 | 818 | air: Air, |
| 821 | 819 | liveness: Liveness, |
| 822 | code: *std.ArrayList(u8), | |
| 820 | code: *std.ArrayListUnmanaged(u8), | |
| 823 | 821 | debug_output: link.File.DebugInfoOutput, |
| 824 | ) CodeGenError!Result { | |
| 822 | ) CodeGenError!void { | |
| 825 | 823 | const zcu = pt.zcu; |
| 826 | 824 | const comp = zcu.comp; |
| 827 | 825 | const gpa = zcu.gpa; |
| ... | ... | @@ -841,7 +839,6 @@ pub fn generate( |
| 841 | 839 | .debug_output = debug_output, |
| 842 | 840 | .owner = .{ .nav_index = func.owner_nav }, |
| 843 | 841 | .inline_func = func_index, |
| 844 | .err_msg = null, | |
| 845 | 842 | .arg_index = undefined, |
| 846 | 843 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 847 | 844 | .va_info = undefined, // populated after `resolveCallingConventionValues` |
| ... | ... | @@ -881,15 +878,7 @@ pub fn generate( |
| 881 | 878 | const fn_info = zcu.typeToFunc(fn_type).?; |
| 882 | 879 | const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*); |
| 883 | 880 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { |
| 884 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 881 | error.CodegenFail => return error.CodegenFail, | |
| 893 | 882 | else => |e| return e, |
| 894 | 883 | }; |
| 895 | 884 | defer call_info.deinit(&function); |
| ... | ... | @@ -926,10 +915,8 @@ pub fn generate( |
| 926 | 915 | }; |
| 927 | 916 | |
| 928 | 917 | function.gen() catch |err| switch (err) { |
| 929 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 930 | error.OutOfRegisters => return Result{ | |
| 931 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 932 | }, | |
| 918 | error.CodegenFail => return error.CodegenFail, | |
| 919 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 933 | 920 | else => |e| return e, |
| 934 | 921 | }; |
| 935 | 922 | |
| ... | ... | @@ -953,10 +940,7 @@ pub fn generate( |
| 953 | 940 | .pic = mod.pic, |
| 954 | 941 | }, |
| 955 | 942 | .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { |
| 956 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 943 | error.CodegenFail => return error.CodegenFail, | |
| 960 | 944 | else => |e| return e, |
| 961 | 945 | }, |
| 962 | 946 | .debug_output = debug_output, |
| ... | ... | @@ -974,29 +958,11 @@ pub fn generate( |
| 974 | 958 | }; |
| 975 | 959 | defer emit.deinit(); |
| 976 | 960 | emit.emitMir() catch |err| switch (err) { |
| 977 | error.LowerFail, error.EmitFail => return Result{ .fail = 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 | }; | |
| 961 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), | |
| 994 | 962 | |
| 995 | if (function.err_msg) |em| { | |
| 996 | return Result{ .fail = em }; | |
| 997 | } else { | |
| 998 | return Result.ok; | |
| 999 | } | |
| 963 | error.InvalidInstruction, error.CannotEncode => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), | |
| 964 | else => |e| return function.fail("emit MIR failed: {s}", .{@errorName(e)}), | |
| 965 | }; | |
| 1000 | 966 | } |
| 1001 | 967 | |
| 1002 | 968 | pub fn generateLazy( |
| ... | ... | @@ -1004,9 +970,9 @@ pub fn generateLazy( |
| 1004 | 970 | pt: Zcu.PerThread, |
| 1005 | 971 | src_loc: Zcu.LazySrcLoc, |
| 1006 | 972 | lazy_sym: link.File.LazySymbol, |
| 1007 | code: *std.ArrayList(u8), | |
| 973 | code: *std.ArrayListUnmanaged(u8), | |
| 1008 | 974 | debug_output: link.File.DebugInfoOutput, |
| 1009 | ) CodeGenError!Result { | |
| 975 | ) CodeGenError!void { | |
| 1010 | 976 | const comp = bin_file.comp; |
| 1011 | 977 | const gpa = comp.gpa; |
| 1012 | 978 | // This function is for generating global code, so we use the root module. |
| ... | ... | @@ -1022,7 +988,6 @@ pub fn generateLazy( |
| 1022 | 988 | .debug_output = debug_output, |
| 1023 | 989 | .owner = .{ .lazy_sym = lazy_sym }, |
| 1024 | 990 | .inline_func = undefined, |
| 1025 | .err_msg = null, | |
| 1026 | 991 | .arg_index = undefined, |
| 1027 | 992 | .args = undefined, |
| 1028 | 993 | .va_info = undefined, |
| ... | ... | @@ -1038,10 +1003,8 @@ pub fn generateLazy( |
| 1038 | 1003 | } |
| 1039 | 1004 | |
| 1040 | 1005 | function.genLazy(lazy_sym) catch |err| switch (err) { |
| 1041 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 1042 | error.OutOfRegisters => return Result{ | |
| 1043 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | |
| 1044 | }, | |
| 1006 | error.CodegenFail => return error.CodegenFail, | |
| 1007 | error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), | |
| 1045 | 1008 | else => |e| return e, |
| 1046 | 1009 | }; |
| 1047 | 1010 | |
| ... | ... | @@ -1065,10 +1028,7 @@ pub fn generateLazy( |
| 1065 | 1028 | .pic = mod.pic, |
| 1066 | 1029 | }, |
| 1067 | 1030 | .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { |
| 1068 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 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 | }, | |
| 1031 | error.CodegenFail => return error.CodegenFail, | |
| 1072 | 1032 | else => |e| return e, |
| 1073 | 1033 | }, |
| 1074 | 1034 | .debug_output = debug_output, |
| ... | ... | @@ -1078,29 +1038,11 @@ pub fn generateLazy( |
| 1078 | 1038 | }; |
| 1079 | 1039 | defer emit.deinit(); |
| 1080 | 1040 | emit.emitMir() catch |err| switch (err) { |
| 1081 | error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, | |
| 1082 | error.InvalidInstruction, error.CannotEncode => |e| { | |
| 1083 | const msg = switch (e) { | |
| 1084 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | |
| 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, | |
| 1041 | error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), | |
| 1042 | error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}), | |
| 1043 | error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}), | |
| 1044 | else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(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 | 1048 | const FormatNavData = struct { |
| ... | ... | @@ -19276,10 +19218,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { |
| 19276 | 19218 | .load_got => |sym_index| .{ .lea_got = sym_index }, |
| 19277 | 19219 | .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, |
| 19278 | 19220 | }, |
| 19279 | .fail => |msg| { | |
| 19280 | self.err_msg = msg; | |
| 19281 | return error.CodegenFail; | |
| 19282 | }, | |
| 19221 | .fail => |msg| return self.failMsg(msg), | |
| 19283 | 19222 | }; |
| 19284 | 19223 | } |
| 19285 | 19224 | |
| ... | ... | @@ -19592,11 +19531,23 @@ fn resolveCallingConventionValues( |
| 19592 | 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 | 19535 | @branchHint(.cold); |
| 19597 | assert(self.err_msg == null); | |
| 19598 | const gpa = self.gpa; | |
| 19599 | self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); | |
| 19536 | const zcu = self.pt.zcu; | |
| 19537 | switch (self.owner) { | |
| 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 | 19551 | return error.CodegenFail; |
| 19601 | 19552 | } |
| 19602 | 19553 |
src/arch/x86_64/Emit.zig+8-7| ... | ... | @@ -4,7 +4,7 @@ air: Air, |
| 4 | 4 | lower: Lower, |
| 5 | 5 | atom_index: u32, |
| 6 | 6 | debug_output: link.File.DebugInfoOutput, |
| 7 | code: *std.ArrayList(u8), | |
| 7 | code: *std.ArrayListUnmanaged(u8), | |
| 8 | 8 | |
| 9 | 9 | prev_di_loc: Loc, |
| 10 | 10 | /// Relative to the beginning of `code`. |
| ... | ... | @@ -18,6 +18,7 @@ pub const Error = Lower.Error || error{ |
| 18 | 18 | } || link.File.UpdateDebugInfoError; |
| 19 | 19 | |
| 20 | 20 | pub fn emitMir(emit: *Emit) Error!void { |
| 21 | const gpa = emit.lower.bin_file.comp.gpa; | |
| 21 | 22 | for (0..emit.lower.mir.instructions.len) |mir_i| { |
| 22 | 23 | const mir_index: Mir.Inst.Index = @intCast(mir_i); |
| 23 | 24 | try emit.code_offset_mapping.putNoClobber( |
| ... | ... | @@ -82,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 82 | 83 | } |
| 83 | 84 | continue; |
| 84 | 85 | } |
| 85 | try lowered_inst.encode(emit.code.writer(), .{}); | |
| 86 | try lowered_inst.encode(emit.code.writer(gpa), .{}); | |
| 86 | 87 | const end_offset: u32 = @intCast(emit.code.items.len); |
| 87 | 88 | while (lowered_relocs.len > 0 and |
| 88 | 89 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ |
| ... | ... | @@ -100,7 +101,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 100 | 101 | const zo = elf_file.zigObjectPtr().?; |
| 101 | 102 | const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?; |
| 102 | 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 | 105 | .r_offset = end_offset - 4, |
| 105 | 106 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 106 | 107 | .r_addend = lowered_relocs[0].off - 4, |
| ... | ... | @@ -147,7 +148,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 147 | 148 | const zo = elf_file.zigObjectPtr().?; |
| 148 | 149 | const atom = zo.symbol(emit.atom_index).atom(elf_file).?; |
| 149 | 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 | 152 | .r_offset = end_offset - 4, |
| 152 | 153 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 153 | 154 | .r_addend = lowered_relocs[0].off - 4, |
| ... | ... | @@ -158,7 +159,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 158 | 159 | const zo = elf_file.zigObjectPtr().?; |
| 159 | 160 | const atom = zo.symbol(emit.atom_index).atom(elf_file).?; |
| 160 | 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 | 163 | .r_offset = end_offset - 4, |
| 163 | 164 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 164 | 165 | .r_addend = lowered_relocs[0].off, |
| ... | ... | @@ -173,7 +174,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 173 | 174 | @intFromEnum(std.elf.R_X86_64.GOTPCREL) |
| 174 | 175 | else |
| 175 | 176 | @intFromEnum(std.elf.R_X86_64.PC32); |
| 176 | try atom.addReloc(elf_file.base.comp.gpa, .{ | |
| 177 | try atom.addReloc(gpa, .{ | |
| 177 | 178 | .r_offset = end_offset - 4, |
| 178 | 179 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 179 | 180 | .r_addend = lowered_relocs[0].off - 4, |
| ... | ... | @@ -183,7 +184,7 @@ pub fn emitMir(emit: *Emit) Error!void { |
| 183 | 184 | @intFromEnum(std.elf.R_X86_64.TPOFF32) |
| 184 | 185 | else |
| 185 | 186 | @intFromEnum(std.elf.R_X86_64.@"32"); |
| 186 | try atom.addReloc(elf_file.base.comp.gpa, .{ | |
| 187 | try atom.addReloc(gpa, .{ | |
| 187 | 188 | .r_offset = end_offset - 4, |
| 188 | 189 | .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, |
| 189 | 190 | .r_addend = lowered_relocs[0].off, |
src/codegen.zig+199-181| ... | ... | @@ -2,7 +2,6 @@ const std = @import("std"); |
| 2 | 2 | const build_options = @import("build_options"); |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | const leb128 = std.leb; | |
| 6 | 5 | const link = @import("link.zig"); |
| 7 | 6 | const log = std.log.scoped(.codegen); |
| 8 | 7 | const mem = std.mem; |
| ... | ... | @@ -24,19 +23,13 @@ const Zir = std.zig.Zir; |
| 24 | 23 | const Alignment = InternPool.Alignment; |
| 25 | 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 | 26 | pub const CodeGenError = error{ |
| 36 | 27 | OutOfMemory, |
| 28 | /// Compiler was asked to operate on a number larger than supported. | |
| 37 | 29 | Overflow, |
| 30 | /// Indicates the error is already stored in Zcu `failed_codegen`. | |
| 38 | 31 | CodegenFail, |
| 39 | } || link.File.UpdateDebugInfoError; | |
| 32 | }; | |
| 40 | 33 | |
| 41 | 34 | fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature { |
| 42 | 35 | comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_")); |
| ... | ... | @@ -49,7 +42,6 @@ fn importBackend(comptime backend: std.builtin.CompilerBackend) type { |
| 49 | 42 | .stage2_arm => @import("arch/arm/CodeGen.zig"), |
| 50 | 43 | .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"), |
| 51 | 44 | .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"), |
| 52 | .stage2_wasm => @import("arch/wasm/CodeGen.zig"), | |
| 53 | 45 | .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"), |
| 54 | 46 | else => unreachable, |
| 55 | 47 | }; |
| ... | ... | @@ -62,9 +54,9 @@ pub fn generateFunction( |
| 62 | 54 | func_index: InternPool.Index, |
| 63 | 55 | air: Air, |
| 64 | 56 | liveness: Liveness, |
| 65 | code: *std.ArrayList(u8), | |
| 57 | code: *std.ArrayListUnmanaged(u8), | |
| 66 | 58 | debug_output: link.File.DebugInfoOutput, |
| 67 | ) CodeGenError!Result { | |
| 59 | ) CodeGenError!void { | |
| 68 | 60 | const zcu = pt.zcu; |
| 69 | 61 | const func = zcu.funcInfo(func_index); |
| 70 | 62 | const target = zcu.navFileScope(func.owner_nav).mod.resolved_target.result; |
| ... | ... | @@ -74,7 +66,6 @@ pub fn generateFunction( |
| 74 | 66 | .stage2_arm, |
| 75 | 67 | .stage2_riscv64, |
| 76 | 68 | .stage2_sparc64, |
| 77 | .stage2_wasm, | |
| 78 | 69 | .stage2_x86_64, |
| 79 | 70 | => |backend| { |
| 80 | 71 | dev.check(devFeatureForBackend(backend)); |
| ... | ... | @@ -88,17 +79,15 @@ pub fn generateLazyFunction( |
| 88 | 79 | pt: Zcu.PerThread, |
| 89 | 80 | src_loc: Zcu.LazySrcLoc, |
| 90 | 81 | lazy_sym: link.File.LazySymbol, |
| 91 | code: *std.ArrayList(u8), | |
| 82 | code: *std.ArrayListUnmanaged(u8), | |
| 92 | 83 | debug_output: link.File.DebugInfoOutput, |
| 93 | ) CodeGenError!Result { | |
| 84 | ) CodeGenError!void { | |
| 94 | 85 | const zcu = pt.zcu; |
| 95 | 86 | const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool); |
| 96 | 87 | const target = zcu.fileByIndex(file).mod.resolved_target.result; |
| 97 | 88 | switch (target_util.zigBackend(target, false)) { |
| 98 | 89 | else => unreachable, |
| 99 | inline .stage2_x86_64, | |
| 100 | .stage2_riscv64, | |
| 101 | => |backend| { | |
| 90 | inline .stage2_x86_64, .stage2_riscv64 => |backend| { | |
| 102 | 91 | dev.check(devFeatureForBackend(backend)); |
| 103 | 92 | return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); |
| 104 | 93 | }, |
| ... | ... | @@ -120,20 +109,21 @@ pub fn generateLazySymbol( |
| 120 | 109 | lazy_sym: link.File.LazySymbol, |
| 121 | 110 | // TODO don't use an "out" parameter like this; put it in the result instead |
| 122 | 111 | alignment: *Alignment, |
| 123 | code: *std.ArrayList(u8), | |
| 112 | code: *std.ArrayListUnmanaged(u8), | |
| 124 | 113 | debug_output: link.File.DebugInfoOutput, |
| 125 | 114 | reloc_parent: link.File.RelocInfo.Parent, |
| 126 | ) CodeGenError!Result { | |
| 115 | ) CodeGenError!void { | |
| 127 | 116 | _ = reloc_parent; |
| 128 | 117 | |
| 129 | 118 | const tracy = trace(@src()); |
| 130 | 119 | defer tracy.end(); |
| 131 | 120 | |
| 132 | 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 | 125 | const target = comp.root_mod.resolved_target.result; |
| 135 | 126 | const endian = target.cpu.arch.endian(); |
| 136 | const gpa = comp.gpa; | |
| 137 | 127 | |
| 138 | 128 | log.debug("generateLazySymbol: kind = {s}, ty = {}", .{ |
| 139 | 129 | @tagName(lazy_sym.kind), |
| ... | ... | @@ -150,52 +140,56 @@ pub fn generateLazySymbol( |
| 150 | 140 | const err_names = ip.global_error_set.getNamesFromMainThread(); |
| 151 | 141 | var offset_index: u32 = @intCast(code.items.len); |
| 152 | 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 | 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 | 146 | offset_index += 4; |
| 157 | 147 | for (err_names) |err_name_nts| { |
| 158 | 148 | const err_name = err_name_nts.toSlice(ip); |
| 159 | 149 | mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian); |
| 160 | 150 | offset_index += 4; |
| 161 | try code.ensureUnusedCapacity(err_name.len + 1); | |
| 151 | try code.ensureUnusedCapacity(gpa, err_name.len + 1); | |
| 162 | 152 | code.appendSliceAssumeCapacity(err_name); |
| 163 | 153 | code.appendAssumeCapacity(0); |
| 164 | 154 | string_index += @intCast(err_name.len + 1); |
| 165 | 155 | } |
| 166 | 156 | mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian); |
| 167 | return .ok; | |
| 168 | } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(pt.zcu) == .@"enum") { | |
| 157 | } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") { | |
| 169 | 158 | alignment.* = .@"1"; |
| 170 | 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 | 161 | for (0..tag_names.len) |tag_index| { |
| 173 | 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 | 164 | code.appendSliceAssumeCapacity(tag_name); |
| 176 | 165 | code.appendAssumeCapacity(0); |
| 177 | 166 | } |
| 178 | return .ok; | |
| 179 | } else return .{ .fail = try .create( | |
| 180 | gpa, | |
| 181 | src_loc, | |
| 182 | "TODO implement generateLazySymbol for {s} {}", | |
| 183 | .{ @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt) }, | |
| 184 | ) }; | |
| 167 | } else { | |
| 168 | return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {}", .{ | |
| 169 | @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt), | |
| 170 | }); | |
| 171 | } | |
| 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 | 180 | pub fn generateSymbol( |
| 188 | 181 | bin_file: *link.File, |
| 189 | 182 | pt: Zcu.PerThread, |
| 190 | 183 | src_loc: Zcu.LazySrcLoc, |
| 191 | 184 | val: Value, |
| 192 | code: *std.ArrayList(u8), | |
| 185 | code: *std.ArrayListUnmanaged(u8), | |
| 193 | 186 | reloc_parent: link.File.RelocInfo.Parent, |
| 194 | ) CodeGenError!Result { | |
| 187 | ) GenerateSymbolError!void { | |
| 195 | 188 | const tracy = trace(@src()); |
| 196 | 189 | defer tracy.end(); |
| 197 | 190 | |
| 198 | 191 | const zcu = pt.zcu; |
| 192 | const gpa = zcu.gpa; | |
| 199 | 193 | const ip = &zcu.intern_pool; |
| 200 | 194 | const ty = val.typeOf(zcu); |
| 201 | 195 | |
| ... | ... | @@ -206,8 +200,8 @@ pub fn generateSymbol( |
| 206 | 200 | |
| 207 | 201 | if (val.isUndefDeep(zcu)) { |
| 208 | 202 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 209 | try code.appendNTimes(0xaa, abi_size); | |
| 210 | return .ok; | |
| 203 | try code.appendNTimes(gpa, 0xaa, abi_size); | |
| 204 | return; | |
| 211 | 205 | } |
| 212 | 206 | |
| 213 | 207 | switch (ip.indexToKey(val.toIntern())) { |
| ... | ... | @@ -231,14 +225,13 @@ pub fn generateSymbol( |
| 231 | 225 | |
| 232 | 226 | .undef => unreachable, // handled above |
| 233 | 227 | .simple_value => |simple_value| switch (simple_value) { |
| 234 | .undefined, | |
| 235 | .void, | |
| 236 | .null, | |
| 237 | .empty_tuple, | |
| 238 | .@"unreachable", | |
| 239 | .generic_poison, | |
| 240 | => unreachable, // non-runtime values | |
| 241 | .false, .true => try code.append(switch (simple_value) { | |
| 228 | .undefined => unreachable, // non-runtime value | |
| 229 | .void => unreachable, // non-runtime value | |
| 230 | .null => unreachable, // non-runtime value | |
| 231 | .@"unreachable" => unreachable, // non-runtime value | |
| 232 | .generic_poison => unreachable, // non-runtime value | |
| 233 | .empty_tuple => return, | |
| 234 | .false, .true => try code.append(gpa, switch (simple_value) { | |
| 242 | 235 | .false => 0, |
| 243 | 236 | .true => 1, |
| 244 | 237 | else => unreachable, |
| ... | ... | @@ -254,11 +247,11 @@ pub fn generateSymbol( |
| 254 | 247 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 255 | 248 | var space: Value.BigIntSpace = undefined; |
| 256 | 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 | 252 | .err => |err| { |
| 260 | 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 | 256 | .error_union => |error_union| { |
| 264 | 257 | const payload_ty = ty.errorUnionPayload(zcu); |
| ... | ... | @@ -268,8 +261,8 @@ pub fn generateSymbol( |
| 268 | 261 | }; |
| 269 | 262 | |
| 270 | 263 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 271 | try code.writer().writeInt(u16, err_val, endian); | |
| 272 | return .ok; | |
| 264 | try code.writer(gpa).writeInt(u16, err_val, endian); | |
| 265 | return; | |
| 273 | 266 | } |
| 274 | 267 | |
| 275 | 268 | const payload_align = payload_ty.abiAlignment(zcu); |
| ... | ... | @@ -278,72 +271,57 @@ pub fn generateSymbol( |
| 278 | 271 | |
| 279 | 272 | // error value first when its type is larger than the error union's payload |
| 280 | 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 | 277 | // emit payload part of the error union |
| 285 | 278 | { |
| 286 | 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 | 281 | .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }), |
| 289 | 282 | .payload => |payload| payload, |
| 290 | }), code, reloc_parent)) { | |
| 291 | .ok => {}, | |
| 292 | .fail => |em| return .{ .fail = em }, | |
| 293 | } | |
| 283 | }), code, reloc_parent); | |
| 294 | 284 | const unpadded_end = code.items.len - begin; |
| 295 | 285 | const padded_end = abi_align.forward(unpadded_end); |
| 296 | 286 | const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; |
| 297 | 287 | |
| 298 | 288 | if (padding > 0) { |
| 299 | try code.appendNTimes(0, padding); | |
| 289 | try code.appendNTimes(gpa, 0, padding); | |
| 300 | 290 | } |
| 301 | 291 | } |
| 302 | 292 | |
| 303 | 293 | // Payload size is larger than error set, so emit our error set last |
| 304 | 294 | if (error_align.compare(.lte, payload_align)) { |
| 305 | 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 | 297 | const unpadded_end = code.items.len - begin; |
| 308 | 298 | const padded_end = abi_align.forward(unpadded_end); |
| 309 | 299 | const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; |
| 310 | 300 | |
| 311 | 301 | if (padding > 0) { |
| 312 | try code.appendNTimes(0, padding); | |
| 302 | try code.appendNTimes(gpa, 0, padding); | |
| 313 | 303 | } |
| 314 | 304 | } |
| 315 | 305 | }, |
| 316 | 306 | .enum_tag => |enum_tag| { |
| 317 | 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)) { | |
| 319 | .ok => {}, | |
| 320 | .fail => |em| return .{ .fail = em }, | |
| 321 | } | |
| 308 | try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent); | |
| 322 | 309 | }, |
| 323 | 310 | .float => |float| switch (float.storage) { |
| 324 | .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(2)), | |
| 325 | .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(4)), | |
| 326 | .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(8)), | |
| 311 | .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)), | |
| 312 | .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)), | |
| 313 | .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)), | |
| 327 | 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 | 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)), | |
| 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 }, | |
| 319 | .f128 => |f128_val| writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)), | |
| 337 | 320 | }, |
| 321 | .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0), | |
| 338 | 322 | .slice => |slice| { |
| 339 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent)) { | |
| 340 | .ok => {}, | |
| 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 | } | |
| 323 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent); | |
| 324 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent); | |
| 347 | 325 | }, |
| 348 | 326 | .opt => { |
| 349 | 327 | const payload_type = ty.optionalChild(zcu); |
| ... | ... | @@ -352,12 +330,9 @@ pub fn generateSymbol( |
| 352 | 330 | |
| 353 | 331 | if (ty.optionalReprIsPayload(zcu)) { |
| 354 | 332 | if (payload_val) |value| { |
| 355 | switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) { | |
| 356 | .ok => {}, | |
| 357 | .fail => |em| return Result{ .fail = em }, | |
| 358 | } | |
| 333 | try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent); | |
| 359 | 334 | } else { |
| 360 | try code.appendNTimes(0, abi_size); | |
| 335 | try code.appendNTimes(gpa, 0, abi_size); | |
| 361 | 336 | } |
| 362 | 337 | } else { |
| 363 | 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 | 340 | const value = payload_val orelse Value.fromInterned(try pt.intern(.{ |
| 366 | 341 | .undef = payload_type.toIntern(), |
| 367 | 342 | })); |
| 368 | switch (try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent)) { | |
| 369 | .ok => {}, | |
| 370 | .fail => |em| return Result{ .fail = em }, | |
| 371 | } | |
| 343 | try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent); | |
| 372 | 344 | } |
| 373 | try code.writer().writeByte(@intFromBool(payload_val != null)); | |
| 374 | try code.appendNTimes(0, padding); | |
| 345 | try code.writer(gpa).writeByte(@intFromBool(payload_val != null)); | |
| 346 | try code.appendNTimes(gpa, 0, padding); | |
| 375 | 347 | } |
| 376 | 348 | }, |
| 377 | 349 | .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { |
| 378 | 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 | 352 | .elems, .repeated_elem => { |
| 381 | 353 | var index: u64 = 0; |
| 382 | 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 | 356 | .bytes => unreachable, |
| 385 | 357 | .elems => |elems| elems[@intCast(index)], |
| 386 | 358 | .repeated_elem => |elem| if (index < array_type.len) |
| 387 | 359 | elem |
| 388 | 360 | else |
| 389 | 361 | array_type.sentinel, |
| 390 | }), code, reloc_parent)) { | |
| 391 | .ok => {}, | |
| 392 | .fail => |em| return .{ .fail = em }, | |
| 393 | } | |
| 362 | }), code, reloc_parent); | |
| 394 | 363 | } |
| 395 | 364 | }, |
| 396 | 365 | }, |
| 397 | 366 | .vector_type => |vector_type| { |
| 398 | 367 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 399 | 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 | 370 | @memset(bytes, 0xaa); |
| 402 | 371 | var index: usize = 0; |
| 403 | 372 | const len = math.cast(usize, vector_type.len) orelse return error.Overflow; |
| ... | ... | @@ -436,20 +405,17 @@ pub fn generateSymbol( |
| 436 | 405 | } |
| 437 | 406 | } else { |
| 438 | 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 | 409 | .elems, .repeated_elem => { |
| 441 | 410 | var index: u64 = 0; |
| 442 | 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 | 413 | .bytes => unreachable, |
| 445 | 414 | .elems => |elems| elems[ |
| 446 | 415 | math.cast(usize, index) orelse return error.Overflow |
| 447 | 416 | ], |
| 448 | 417 | .repeated_elem => |elem| elem, |
| 449 | }), code, reloc_parent)) { | |
| 450 | .ok => {}, | |
| 451 | .fail => |em| return .{ .fail = em }, | |
| 452 | } | |
| 418 | }), code, reloc_parent); | |
| 453 | 419 | } |
| 454 | 420 | }, |
| 455 | 421 | } |
| ... | ... | @@ -457,7 +423,7 @@ pub fn generateSymbol( |
| 457 | 423 | const padding = abi_size - |
| 458 | 424 | (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse |
| 459 | 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 | 429 | .tuple_type => |tuple| { |
| ... | ... | @@ -479,10 +445,7 @@ pub fn generateSymbol( |
| 479 | 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)) { | |
| 483 | .ok => {}, | |
| 484 | .fail => |em| return Result{ .fail = em }, | |
| 485 | } | |
| 448 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent); | |
| 486 | 449 | const unpadded_field_end = code.items.len - struct_begin; |
| 487 | 450 | |
| 488 | 451 | // Pad struct members if required |
| ... | ... | @@ -491,7 +454,7 @@ pub fn generateSymbol( |
| 491 | 454 | return error.Overflow; |
| 492 | 455 | |
| 493 | 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 | 464 | .@"packed" => { |
| 502 | 465 | const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; |
| 503 | 466 | const current_pos = code.items.len; |
| 504 | try code.appendNTimes(0, abi_size); | |
| 467 | try code.appendNTimes(gpa, 0, abi_size); | |
| 505 | 468 | var bits: u16 = 0; |
| 506 | 469 | |
| 507 | 470 | for (struct_type.field_types.get(ip), 0..) |field_ty, index| { |
| ... | ... | @@ -519,12 +482,10 @@ pub fn generateSymbol( |
| 519 | 482 | if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) { |
| 520 | 483 | const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse |
| 521 | 484 | return error.Overflow; |
| 522 | var tmp_list = try std.ArrayList(u8).initCapacity(code.allocator, field_size); | |
| 523 | defer tmp_list.deinit(); | |
| 524 | switch (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), | |
| 526 | .fail => |em| return Result{ .fail = em }, | |
| 527 | } | |
| 485 | var tmp_list = try std.ArrayListUnmanaged(u8).initCapacity(gpa, field_size); | |
| 486 | defer tmp_list.deinit(gpa); | |
| 487 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent); | |
| 488 | @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items); | |
| 528 | 489 | } else { |
| 529 | 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 | 515 | usize, |
| 555 | 516 | offsets[field_index] - (code.items.len - struct_begin), |
| 556 | 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)) { | |
| 560 | .ok => {}, | |
| 561 | .fail => |em| return Result{ .fail = em }, | |
| 562 | } | |
| 520 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent); | |
| 563 | 521 | } |
| 564 | 522 | |
| 565 | 523 | const size = struct_type.sizeUnordered(ip); |
| ... | ... | @@ -570,7 +528,7 @@ pub fn generateSymbol( |
| 570 | 528 | std.mem.alignForward(u64, size, @max(alignment, 1)) - |
| 571 | 529 | (code.items.len - struct_begin), |
| 572 | 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 | 543 | |
| 586 | 544 | // Check if we should store the tag first. |
| 587 | 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)) { | |
| 589 | .ok => {}, | |
| 590 | .fail => |em| return Result{ .fail = em }, | |
| 591 | } | |
| 546 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent); | |
| 592 | 547 | } |
| 593 | 548 | |
| 594 | 549 | const union_obj = zcu.typeToUnion(ty).?; |
| ... | ... | @@ -596,39 +551,29 @@ pub fn generateSymbol( |
| 596 | 551 | const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?; |
| 597 | 552 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 598 | 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 | 555 | } else { |
| 601 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) { | |
| 602 | .ok => {}, | |
| 603 | .fail => |em| return Result{ .fail = em }, | |
| 604 | } | |
| 556 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent); | |
| 605 | 557 | |
| 606 | 558 | const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow; |
| 607 | 559 | if (padding > 0) { |
| 608 | try code.appendNTimes(0, padding); | |
| 560 | try code.appendNTimes(gpa, 0, padding); | |
| 609 | 561 | } |
| 610 | 562 | } |
| 611 | 563 | } else { |
| 612 | switch (try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent)) { | |
| 613 | .ok => {}, | |
| 614 | .fail => |em| return Result{ .fail = em }, | |
| 615 | } | |
| 564 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent); | |
| 616 | 565 | } |
| 617 | 566 | |
| 618 | 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)) { | |
| 620 | .ok => {}, | |
| 621 | .fail => |em| return Result{ .fail = em }, | |
| 622 | } | |
| 568 | try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent); | |
| 623 | 569 | |
| 624 | 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 | 575 | .memoized_call => unreachable, |
| 630 | 576 | } |
| 631 | return .ok; | |
| 632 | 577 | } |
| 633 | 578 | |
| 634 | 579 | fn lowerPtr( |
| ... | ... | @@ -636,15 +581,15 @@ fn lowerPtr( |
| 636 | 581 | pt: Zcu.PerThread, |
| 637 | 582 | src_loc: Zcu.LazySrcLoc, |
| 638 | 583 | ptr_val: InternPool.Index, |
| 639 | code: *std.ArrayList(u8), | |
| 584 | code: *std.ArrayListUnmanaged(u8), | |
| 640 | 585 | reloc_parent: link.File.RelocInfo.Parent, |
| 641 | 586 | prev_offset: u64, |
| 642 | ) CodeGenError!Result { | |
| 587 | ) GenerateSymbolError!void { | |
| 643 | 588 | const zcu = pt.zcu; |
| 644 | 589 | const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; |
| 645 | 590 | const offset: u64 = prev_offset + ptr.byte_offset; |
| 646 | 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 | 593 | .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset), |
| 649 | 594 | .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent), |
| 650 | 595 | .eu_payload => |eu_ptr| try lowerPtr( |
| ... | ... | @@ -689,29 +634,62 @@ fn lowerUavRef( |
| 689 | 634 | pt: Zcu.PerThread, |
| 690 | 635 | src_loc: Zcu.LazySrcLoc, |
| 691 | 636 | uav: InternPool.Key.Ptr.BaseAddr.Uav, |
| 692 | code: *std.ArrayList(u8), | |
| 637 | code: *std.ArrayListUnmanaged(u8), | |
| 693 | 638 | reloc_parent: link.File.RelocInfo.Parent, |
| 694 | 639 | offset: u64, |
| 695 | ) CodeGenError!Result { | |
| 640 | ) GenerateSymbolError!void { | |
| 696 | 641 | const zcu = pt.zcu; |
| 642 | const gpa = zcu.gpa; | |
| 697 | 643 | const ip = &zcu.intern_pool; |
| 698 | const target = lf.comp.root_mod.resolved_target.result; | |
| 699 | ||
| 644 | const comp = lf.comp; | |
| 645 | const target = &comp.root_mod.resolved_target.result; | |
| 700 | 646 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); |
| 647 | const is_obj = comp.config.output_mode == .Obj; | |
| 701 | 648 | const uav_val = uav.val; |
| 702 | 649 | const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); |
| 703 | log.debug("lowerUavRef: ty = {}", .{uav_ty.fmt(pt)}); | |
| 704 | 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 | 655 | if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) { |
| 706 | try code.appendNTimes(0xaa, ptr_width_bytes); | |
| 707 | return Result.ok; | |
| 656 | code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes); | |
| 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 | 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); | |
| 712 | switch (res) { | |
| 690 | switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) { | |
| 713 | 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 | 695 | const vaddr = try lf.getUavVAddr(uav_val, .{ |
| ... | ... | @@ -721,51 +699,91 @@ fn lowerUavRef( |
| 721 | 699 | }); |
| 722 | 700 | const endian = target.cpu.arch.endian(); |
| 723 | 701 | switch (ptr_width_bytes) { |
| 724 | 2 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian), | |
| 725 | 4 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian), | |
| 726 | 8 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian), | |
| 702 | 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian), | |
| 703 | 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian), | |
| 704 | 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian), | |
| 727 | 705 | else => unreachable, |
| 728 | 706 | } |
| 729 | ||
| 730 | return Result.ok; | |
| 731 | 707 | } |
| 732 | 708 | |
| 733 | 709 | fn lowerNavRef( |
| 734 | 710 | lf: *link.File, |
| 735 | 711 | pt: Zcu.PerThread, |
| 736 | src_loc: Zcu.LazySrcLoc, | |
| 737 | 712 | nav_index: InternPool.Nav.Index, |
| 738 | code: *std.ArrayList(u8), | |
| 713 | code: *std.ArrayListUnmanaged(u8), | |
| 739 | 714 | reloc_parent: link.File.RelocInfo.Parent, |
| 740 | 715 | offset: u64, |
| 741 | ) CodeGenError!Result { | |
| 742 | _ = src_loc; | |
| 716 | ) GenerateSymbolError!void { | |
| 743 | 717 | const zcu = pt.zcu; |
| 718 | const gpa = zcu.gpa; | |
| 744 | 719 | const ip = &zcu.intern_pool; |
| 745 | 720 | const target = zcu.navFileScope(nav_index).mod.resolved_target.result; |
| 746 | ||
| 747 | const ptr_width = target.ptrBitWidth(); | |
| 721 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); | |
| 722 | const is_obj = lf.comp.config.output_mode == .Obj; | |
| 748 | 723 | const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip)); |
| 749 | 724 | const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; |
| 725 | ||
| 726 | try code.ensureUnusedCapacity(gpa, ptr_width_bytes); | |
| 727 | ||
| 750 | 728 | if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) { |
| 751 | try code.appendNTimes(0xaa, @divExact(ptr_width, 8)); | |
| 752 | return Result.ok; | |
| 729 | code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes); | |
| 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 | 776 | .parent = reloc_parent, |
| 757 | 777 | .offset = code.items.len, |
| 758 | 778 | .addend = @intCast(offset), |
| 759 | }); | |
| 779 | }) catch @panic("TODO rework getNavVAddr"); | |
| 760 | 780 | const endian = target.cpu.arch.endian(); |
| 761 | switch (ptr_width) { | |
| 762 | 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian), | |
| 763 | 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian), | |
| 764 | 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian), | |
| 781 | switch (ptr_width_bytes) { | |
| 782 | 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian), | |
| 783 | 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian), | |
| 784 | 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian), | |
| 765 | 785 | else => unreachable, |
| 766 | 786 | } |
| 767 | ||
| 768 | return Result.ok; | |
| 769 | 787 | } |
| 770 | 788 | |
| 771 | 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 | 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 | 3056 | const zcu = dg.pt.zcu; |
| 3057 | 3057 | const ip = &zcu.intern_pool; |
| 3058 | 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 | 3061 | try fwd.writeAll("#define "); |
| 3062 | 3062 | switch (exported) { |
| 3063 | 3063 | .nav => |nav| try dg.renderNavName(fwd, nav), |
| ... | ... | @@ -3069,7 +3069,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3069 | 3069 | |
| 3070 | 3070 | const exported_val = exported.getValue(zcu); |
| 3071 | 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 | 3073 | try fwd.writeAll("zig_extern "); |
| 3074 | 3074 | if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); |
| 3075 | 3075 | try dg.renderFunctionSignature( |
| ... | ... | @@ -3091,7 +3091,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3091 | 3091 | else => true, |
| 3092 | 3092 | }; |
| 3093 | 3093 | for (export_indices) |export_index| { |
| 3094 | const @"export" = &zcu.all_exports.items[export_index]; | |
| 3094 | const @"export" = export_index.ptr(zcu); | |
| 3095 | 3095 | try fwd.writeAll("zig_extern "); |
| 3096 | 3096 | if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); |
| 3097 | 3097 | const extern_name = @"export".opts.name.toSlice(ip); |
src/codegen/llvm.zig+22-32| ... | ... | @@ -1059,9 +1059,10 @@ pub const Object = struct { |
| 1059 | 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 | 1063 | const zcu = o.pt.zcu; |
| 1064 | 1064 | const comp = zcu.comp; |
| 1065 | const diags = &comp.link_diags; | |
| 1065 | 1066 | |
| 1066 | 1067 | { |
| 1067 | 1068 | try o.genErrorNameTable(); |
| ... | ... | @@ -1223,27 +1224,30 @@ pub const Object = struct { |
| 1223 | 1224 | o.builder.clearAndFree(); |
| 1224 | 1225 | |
| 1225 | 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 | 1229 | defer file.close(); |
| 1228 | 1230 | |
| 1229 | 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 | 1236 | if (options.asm_path == null and options.bin_path == null and |
| 1234 | 1237 | options.post_ir_path == null and options.post_bc_path == null) return; |
| 1235 | 1238 | |
| 1236 | 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 | 1242 | defer file.close(); |
| 1239 | 1243 | |
| 1240 | 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 | 1249 | if (!build_options.have_llvm or !comp.config.use_lib_llvm) { |
| 1245 | log.err("emitting without libllvm not implemented", .{}); | |
| 1246 | return error.FailedToEmit; | |
| 1250 | return diags.fail("emitting without libllvm not implemented", .{}); | |
| 1247 | 1251 | } |
| 1248 | 1252 | |
| 1249 | 1253 | initializeLLVMTarget(comp.root_mod.resolved_target.result.cpu.arch); |
| ... | ... | @@ -1263,8 +1267,7 @@ pub const Object = struct { |
| 1263 | 1267 | |
| 1264 | 1268 | var module: *llvm.Module = undefined; |
| 1265 | 1269 | if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool() or context.getBrokenDebugInfo()) { |
| 1266 | log.err("Failed to parse bitcode", .{}); | |
| 1267 | return error.FailedToEmit; | |
| 1270 | return diags.fail("Failed to parse bitcode", .{}); | |
| 1268 | 1271 | } |
| 1269 | 1272 | break :emit .{ context, module }; |
| 1270 | 1273 | }; |
| ... | ... | @@ -1274,12 +1277,7 @@ pub const Object = struct { |
| 1274 | 1277 | var error_message: [*:0]const u8 = undefined; |
| 1275 | 1278 | if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) { |
| 1276 | 1279 | defer llvm.disposeMessage(error_message); |
| 1277 | ||
| 1278 | log.err("LLVM failed to parse '{s}': {s}", .{ | |
| 1279 | target_triple_sentinel, | |
| 1280 | error_message, | |
| 1281 | }); | |
| 1282 | @panic("Invalid LLVM triple"); | |
| 1280 | return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message }); | |
| 1283 | 1281 | } |
| 1284 | 1282 | |
| 1285 | 1283 | const optimize_mode = comp.root_mod.optimize_mode; |
| ... | ... | @@ -1374,10 +1372,9 @@ pub const Object = struct { |
| 1374 | 1372 | if (options.asm_path != null and options.bin_path != null) { |
| 1375 | 1373 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { |
| 1376 | 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 | 1376 | emit_bin_msg, post_llvm_ir_msg, error_message, |
| 1379 | 1377 | }); |
| 1380 | return error.FailedToEmit; | |
| 1381 | 1378 | } |
| 1382 | 1379 | lowered_options.bin_filename = null; |
| 1383 | 1380 | lowered_options.llvm_ir_filename = null; |
| ... | ... | @@ -1386,11 +1383,9 @@ pub const Object = struct { |
| 1386 | 1383 | lowered_options.asm_filename = options.asm_path; |
| 1387 | 1384 | if (target_machine.emitToFile(module, &error_message, &lowered_options)) { |
| 1388 | 1385 | defer llvm.disposeMessage(error_message); |
| 1389 | log.err("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, | |
| 1391 | error_message, | |
| 1386 | return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ | |
| 1387 | emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message, | |
| 1392 | 1388 | }); |
| 1393 | return error.FailedToEmit; | |
| 1394 | 1389 | } |
| 1395 | 1390 | } |
| 1396 | 1391 | |
| ... | ... | @@ -1815,7 +1810,7 @@ pub const Object = struct { |
| 1815 | 1810 | self: *Object, |
| 1816 | 1811 | pt: Zcu.PerThread, |
| 1817 | 1812 | exported: Zcu.Exported, |
| 1818 | export_indices: []const u32, | |
| 1813 | export_indices: []const Zcu.Export.Index, | |
| 1819 | 1814 | ) link.File.UpdateExportsError!void { |
| 1820 | 1815 | assert(std.meta.eql(pt, self.pt)); |
| 1821 | 1816 | const zcu = pt.zcu; |
| ... | ... | @@ -1843,11 +1838,11 @@ pub const Object = struct { |
| 1843 | 1838 | o: *Object, |
| 1844 | 1839 | zcu: *Zcu, |
| 1845 | 1840 | exported_value: InternPool.Index, |
| 1846 | export_indices: []const u32, | |
| 1841 | export_indices: []const Zcu.Export.Index, | |
| 1847 | 1842 | ) link.File.UpdateExportsError!void { |
| 1848 | 1843 | const gpa = zcu.gpa; |
| 1849 | 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 | 1846 | const global_index = i: { |
| 1852 | 1847 | const gop = try o.uav_map.getOrPut(gpa, exported_value); |
| 1853 | 1848 | if (gop.found_existing) { |
| ... | ... | @@ -1878,11 +1873,11 @@ pub const Object = struct { |
| 1878 | 1873 | o: *Object, |
| 1879 | 1874 | zcu: *Zcu, |
| 1880 | 1875 | global_index: Builder.Global.Index, |
| 1881 | export_indices: []const u32, | |
| 1876 | export_indices: []const Zcu.Export.Index, | |
| 1882 | 1877 | ) link.File.UpdateExportsError!void { |
| 1883 | 1878 | const comp = zcu.comp; |
| 1884 | 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 | 1882 | // We will rename this global to have a name matching `first_export`. |
| 1888 | 1883 | // Successive exports become aliases. |
| ... | ... | @@ -1939,7 +1934,7 @@ pub const Object = struct { |
| 1939 | 1934 | // Until then we iterate over existing aliases and make them point |
| 1940 | 1935 | // to the correct decl, or otherwise add a new alias. Old aliases are leaked. |
| 1941 | 1936 | for (export_indices[1..]) |export_idx| { |
| 1942 | const exp = zcu.all_exports.items[export_idx]; | |
| 1937 | const exp = export_idx.ptr(zcu); | |
| 1943 | 1938 | const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip)); |
| 1944 | 1939 | if (o.builder.getGlobal(exp_name)) |global| { |
| 1945 | 1940 | switch (global.ptrConst(&o.builder).kind) { |
| ... | ... | @@ -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 | 1965 | fn getDebugFile(o: *Object, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata { |
| 1976 | 1966 | const gpa = o.gpa; |
| 1977 | 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 | 30 | /// - `zig build-* -fno-llvm -fno-lld -target riscv64-linux` |
| 31 | 31 | @"riscv64-linux", |
| 32 | 32 | |
| 33 | /// - sema | |
| 34 | /// - `zig build-* -fno-llvm -fno-lld -target wasm32-* --listen=-` | |
| 35 | wasm, | |
| 36 | ||
| 33 | 37 | pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool { |
| 34 | 38 | return switch (dev_env) { |
| 35 | 39 | .full => true, |
| ... | ... | @@ -144,6 +148,14 @@ pub const Env = enum { |
| 144 | 148 | => true, |
| 145 | 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 | 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 | 1232 | fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { |
| 1221 | 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 | 38 | flags: Flags, |
| 39 | 39 | lld: std.ArrayListUnmanaged(Lld), |
| 40 | 40 | |
| 41 | pub const SourceLocation = union(enum) { | |
| 42 | none, | |
| 43 | wasm: File.Wasm.SourceLocation, | |
| 44 | }; | |
| 45 | ||
| 41 | 46 | pub const Flags = packed struct { |
| 42 | 47 | no_entry_point_found: bool = false, |
| 43 | 48 | missing_libc: bool = false, |
| ... | ... | @@ -70,9 +75,25 @@ pub const Diags = struct { |
| 70 | 75 | }; |
| 71 | 76 | |
| 72 | 77 | pub const Msg = struct { |
| 78 | source_location: SourceLocation = .none, | |
| 73 | 79 | msg: []const u8, |
| 74 | 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 | 97 | pub fn deinit(self: *Msg, gpa: Allocator) void { |
| 77 | 98 | for (self.notes) |*note| note.deinit(gpa); |
| 78 | 99 | gpa.free(self.notes); |
| ... | ... | @@ -97,15 +118,12 @@ pub const Diags = struct { |
| 97 | 118 | err_msg.msg = try std.fmt.allocPrint(gpa, format, args); |
| 98 | 119 | } |
| 99 | 120 | |
| 100 | pub fn addNote( | |
| 101 | err: *ErrorWithNotes, | |
| 102 | comptime format: []const u8, | |
| 103 | args: anytype, | |
| 104 | ) error{OutOfMemory}!void { | |
| 121 | pub fn addNote(err: *ErrorWithNotes, comptime format: []const u8, args: anytype) void { | |
| 105 | 122 | const gpa = err.diags.gpa; |
| 123 | const msg = std.fmt.allocPrint(gpa, format, args) catch return err.diags.setAllocFailure(); | |
| 106 | 124 | const err_msg = &err.diags.msgs.items[err.index]; |
| 107 | 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 | 127 | err.note_slot += 1; |
| 110 | 128 | } |
| 111 | 129 | }; |
| ... | ... | @@ -196,22 +214,35 @@ pub const Diags = struct { |
| 196 | 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 | 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 | 228 | @branchHint(.cold); |
| 201 | 229 | const gpa = diags.gpa; |
| 202 | 230 | const eu_main_msg = std.fmt.allocPrint(gpa, format, args); |
| 203 | 231 | diags.mutex.lock(); |
| 204 | 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 | 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 | 239 | const gpa = diags.gpa; |
| 212 | 240 | const main_msg = try eu_main_msg; |
| 213 | 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 | 248 | pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { |
| ... | ... | @@ -329,16 +360,16 @@ pub const Diags = struct { |
| 329 | 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 | 364 | for (diags.msgs.items) |link_err| { |
| 334 | 365 | try bundle.addRootErrorMessage(.{ |
| 335 | .msg = try bundle.addString(link_err.msg), | |
| 366 | .msg = try link_err.string(bundle, base), | |
| 336 | 367 | .notes_len = @intCast(link_err.notes.len), |
| 337 | 368 | }); |
| 338 | 369 | const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); |
| 339 | 370 | for (link_err.notes, 0..) |note, i| { |
| 340 | 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 | 395 | build_id: std.zig.BuildId, |
| 365 | 396 | allow_shlib_undefined: bool, |
| 366 | 397 | stack_size: u64, |
| 398 | post_prelink: bool = false, | |
| 367 | 399 | |
| 368 | 400 | /// Prevents other processes from clobbering files in the output directory |
| 369 | 401 | /// of this linking operation. |
| ... | ... | @@ -400,6 +432,7 @@ pub const File = struct { |
| 400 | 432 | export_table: bool, |
| 401 | 433 | initial_memory: ?u64, |
| 402 | 434 | max_memory: ?u64, |
| 435 | object_host_name: ?[]const u8, | |
| 403 | 436 | export_symbol_names: []const []const u8, |
| 404 | 437 | global_base: ?u64, |
| 405 | 438 | build_id: std.zig.BuildId, |
| ... | ... | @@ -632,43 +665,15 @@ pub const File = struct { |
| 632 | 665 | pub const UpdateDebugInfoError = Dwarf.UpdateError; |
| 633 | 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 | 670 | pub const UpdateNavError = error{ |
| 636 | OutOfMemory, | |
| 637 | 671 | Overflow, |
| 638 | Underflow, | |
| 639 | FileTooBig, | |
| 640 | InputOutput, | |
| 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, | |
| 672 | OutOfMemory, | |
| 673 | /// Indicates the error is already reported and stored in | |
| 674 | /// `failed_codegen` on the Zcu. | |
| 662 | 675 | CodegenFail, |
| 663 | EmitFail, | |
| 664 | NameTooLong, | |
| 665 | CurrentWorkingDirectoryUnlinked, | |
| 666 | LockViolation, | |
| 667 | NetNameDeleted, | |
| 668 | DeviceBusy, | |
| 669 | InvalidArgument, | |
| 670 | HotSwapUnavailableOnHostOperatingSystem, | |
| 671 | } || UpdateDebugInfoError; | |
| 676 | }; | |
| 672 | 677 | |
| 673 | 678 | /// Called from within CodeGen to retrieve the symbol index of a global symbol. |
| 674 | 679 | /// If no symbol exists yet with this name, a new undefined global symbol will |
| ... | ... | @@ -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 | 716 | switch (base.tag) { |
| 706 | 717 | else => {}, |
| 707 | 718 | inline .elf => |tag| { |
| ... | ... | @@ -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 | 747 | /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because |
| 731 | 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 | 751 | const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; |
| 735 | 752 | const file = pt.zcu.fileByIndex(ti.file); |
| ... | ... | @@ -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 | 791 | pub const FlushError = error{ |
| 777 | CacheCheckFailed, | |
| 778 | CurrentWorkingDirectoryUnlinked, | |
| 779 | DivisionByZero, | |
| 780 | DllImportLibraryNotFound, | |
| 781 | ExpectedFuncType, | |
| 782 | FailedToEmit, | |
| 783 | FileSystem, | |
| 784 | FilesOpenedWithWrongFlags, | |
| 785 | /// Deprecated. Use `LinkFailure` instead. | |
| 786 | /// Formerly used to indicate an error will be present in `Compilation.link_errors`. | |
| 787 | FlushFailure, | |
| 788 | /// Indicates an error will be present in `Compilation.link_errors`. | |
| 792 | /// Indicates an error will be present in `Compilation.link_diags`. | |
| 789 | 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 | 794 | OutOfMemory, |
| 822 | Overflow, | |
| 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; | |
| 795 | }; | |
| 851 | 796 | |
| 852 | 797 | /// Commit pending changes and write headers. Takes into account final output mode |
| 853 | 798 | /// and `use_lld`, not only `effectiveOutputMode`. |
| ... | ... | @@ -864,10 +809,17 @@ pub const File = struct { |
| 864 | 809 | assert(comp.c_object_table.count() == 1); |
| 865 | 810 | const the_key = comp.c_object_table.keys()[0]; |
| 866 | 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 | 818 | return; |
| 869 | 819 | } |
| 870 | 820 | |
| 821 | assert(base.post_prelink); | |
| 822 | ||
| 871 | 823 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 872 | 824 | const output_mode = comp.config.output_mode; |
| 873 | 825 | const link_mode = comp.config.link_mode; |
| ... | ... | @@ -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 | 848 | pub const UpdateExportsError = error{ |
| 907 | 849 | OutOfMemory, |
| 908 | 850 | AnalysisFail, |
| ... | ... | @@ -916,7 +858,7 @@ pub const File = struct { |
| 916 | 858 | base: *File, |
| 917 | 859 | pt: Zcu.PerThread, |
| 918 | 860 | exported: Zcu.Exported, |
| 919 | export_indices: []const u32, | |
| 861 | export_indices: []const Zcu.Export.Index, | |
| 920 | 862 | ) UpdateExportsError!void { |
| 921 | 863 | switch (base.tag) { |
| 922 | 864 | inline else => |tag| { |
| ... | ... | @@ -932,6 +874,7 @@ pub const File = struct { |
| 932 | 874 | addend: u32, |
| 933 | 875 | |
| 934 | 876 | pub const Parent = union(enum) { |
| 877 | none, | |
| 935 | 878 | atom_index: u32, |
| 936 | 879 | debug_output: DebugInfoOutput, |
| 937 | 880 | }; |
| ... | ... | @@ -948,6 +891,7 @@ pub const File = struct { |
| 948 | 891 | .c => unreachable, |
| 949 | 892 | .spirv => unreachable, |
| 950 | 893 | .nvptx => unreachable, |
| 894 | .wasm => unreachable, | |
| 951 | 895 | inline else => |tag| { |
| 952 | 896 | dev.check(tag.devFeature()); |
| 953 | 897 | return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info); |
| ... | ... | @@ -966,6 +910,7 @@ pub const File = struct { |
| 966 | 910 | .c => unreachable, |
| 967 | 911 | .spirv => unreachable, |
| 968 | 912 | .nvptx => unreachable, |
| 913 | .wasm => unreachable, | |
| 969 | 914 | inline else => |tag| { |
| 970 | 915 | dev.check(tag.devFeature()); |
| 971 | 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 | 923 | .c => unreachable, |
| 979 | 924 | .spirv => unreachable, |
| 980 | 925 | .nvptx => unreachable, |
| 926 | .wasm => unreachable, | |
| 981 | 927 | inline else => |tag| { |
| 982 | 928 | dev.check(tag.devFeature()); |
| 983 | 929 | return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info); |
| ... | ... | @@ -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 | 1069 | pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { |
| 1103 | 1070 | dev.check(.lld_linker); |
| 1104 | 1071 | |
| 1105 | 1072 | const tracy = trace(@src()); |
| 1106 | 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 | 1086 | const comp = base.comp; |
| 1109 | 1087 | |
| 1110 | 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 | 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 | 1355 | pub const C = @import("link/C.zig"); |
| 1368 | 1356 | pub const Coff = @import("link/Coff.zig"); |
| 1369 | 1357 | pub const Plan9 = @import("link/Plan9.zig"); |
| ... | ... | @@ -1379,12 +1367,32 @@ pub const File = struct { |
| 1379 | 1367 | /// from the rest of compilation. All tasks performed here are |
| 1380 | 1368 | /// single-threaded with respect to one another. |
| 1381 | 1369 | pub fn flushTaskQueue(tid: usize, comp: *Compilation) void { |
| 1370 | const diags = &comp.link_diags; | |
| 1382 | 1371 | // As soon as check() is called, another `flushTaskQueue` call could occur, |
| 1383 | 1372 | // so the safety lock must go after the check. |
| 1384 | 1373 | while (comp.link_task_queue.check()) |tasks| { |
| 1385 | 1374 | comp.link_task_queue_safety.lock(); |
| 1386 | 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 | 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 | 1436 | const diags = &comp.link_diags; |
| 1429 | 1437 | switch (task) { |
| 1430 | 1438 | .load_explicitly_provided => if (comp.bin_file) |base| { |
| 1439 | comp.remaining_prelink_tasks -= 1; | |
| 1431 | 1440 | const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len); |
| 1432 | 1441 | defer prog_node.end(); |
| 1433 | 1442 | for (comp.link_inputs) |input| { |
| ... | ... | @@ -1445,6 +1454,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1445 | 1454 | } |
| 1446 | 1455 | }, |
| 1447 | 1456 | .load_host_libc => if (comp.bin_file) |base| { |
| 1457 | comp.remaining_prelink_tasks -= 1; | |
| 1448 | 1458 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0); |
| 1449 | 1459 | defer prog_node.end(); |
| 1450 | 1460 | |
| ... | ... | @@ -1504,6 +1514,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1504 | 1514 | } |
| 1505 | 1515 | }, |
| 1506 | 1516 | .load_object => |path| if (comp.bin_file) |base| { |
| 1517 | comp.remaining_prelink_tasks -= 1; | |
| 1507 | 1518 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0); |
| 1508 | 1519 | defer prog_node.end(); |
| 1509 | 1520 | base.openLoadObject(path) catch |err| switch (err) { |
| ... | ... | @@ -1512,6 +1523,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1512 | 1523 | }; |
| 1513 | 1524 | }, |
| 1514 | 1525 | .load_archive => |path| if (comp.bin_file) |base| { |
| 1526 | comp.remaining_prelink_tasks -= 1; | |
| 1515 | 1527 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0); |
| 1516 | 1528 | defer prog_node.end(); |
| 1517 | 1529 | base.openLoadArchive(path, null) catch |err| switch (err) { |
| ... | ... | @@ -1520,6 +1532,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1520 | 1532 | }; |
| 1521 | 1533 | }, |
| 1522 | 1534 | .load_dso => |path| if (comp.bin_file) |base| { |
| 1535 | comp.remaining_prelink_tasks -= 1; | |
| 1523 | 1536 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0); |
| 1524 | 1537 | defer prog_node.end(); |
| 1525 | 1538 | base.openLoadDso(path, .{ |
| ... | ... | @@ -1531,6 +1544,7 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1531 | 1544 | }; |
| 1532 | 1545 | }, |
| 1533 | 1546 | .load_input => |input| if (comp.bin_file) |base| { |
| 1547 | comp.remaining_prelink_tasks -= 1; | |
| 1534 | 1548 | const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0); |
| 1535 | 1549 | defer prog_node.end(); |
| 1536 | 1550 | base.loadInput(input) catch |err| switch (err) { |
| ... | ... | @@ -1545,26 +1559,38 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { |
| 1545 | 1559 | }; |
| 1546 | 1560 | }, |
| 1547 | 1561 | .codegen_nav => |nav_index| { |
| 1548 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | |
| 1549 | defer pt.deactivate(); | |
| 1550 | pt.linkerUpdateNav(nav_index) catch |err| switch (err) { | |
| 1551 | error.OutOfMemory => diags.setAllocFailure(), | |
| 1552 | }; | |
| 1562 | if (comp.remaining_prelink_tasks == 0) { | |
| 1563 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | |
| 1564 | defer pt.deactivate(); | |
| 1565 | pt.linkerUpdateNav(nav_index) catch |err| switch (err) { | |
| 1566 | error.OutOfMemory => diags.setAllocFailure(), | |
| 1567 | }; | |
| 1568 | } else { | |
| 1569 | comp.link_task_queue_postponed.appendAssumeCapacity(task); | |
| 1570 | } | |
| 1553 | 1571 | }, |
| 1554 | 1572 | .codegen_func => |func| { |
| 1555 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | |
| 1556 | defer pt.deactivate(); | |
| 1557 | // This call takes ownership of `func.air`. | |
| 1558 | pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) { | |
| 1559 | error.OutOfMemory => diags.setAllocFailure(), | |
| 1560 | }; | |
| 1573 | if (comp.remaining_prelink_tasks == 0) { | |
| 1574 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | |
| 1575 | defer pt.deactivate(); | |
| 1576 | // This call takes ownership of `func.air`. | |
| 1577 | pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) { | |
| 1578 | error.OutOfMemory => diags.setAllocFailure(), | |
| 1579 | }; | |
| 1580 | } else { | |
| 1581 | comp.link_task_queue_postponed.appendAssumeCapacity(task); | |
| 1582 | } | |
| 1561 | 1583 | }, |
| 1562 | 1584 | .codegen_type => |ty| { |
| 1563 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | |
| 1564 | defer pt.deactivate(); | |
| 1565 | pt.linkerUpdateContainerType(ty) catch |err| switch (err) { | |
| 1566 | error.OutOfMemory => diags.setAllocFailure(), | |
| 1567 | }; | |
| 1585 | if (comp.remaining_prelink_tasks == 0) { | |
| 1586 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); | |
| 1587 | defer pt.deactivate(); | |
| 1588 | pt.linkerUpdateContainerType(ty) catch |err| switch (err) { | |
| 1589 | error.OutOfMemory => diags.setAllocFailure(), | |
| 1590 | }; | |
| 1591 | } else { | |
| 1592 | comp.link_task_queue_postponed.appendAssumeCapacity(task); | |
| 1593 | } | |
| 1568 | 1594 | }, |
| 1569 | 1595 | .update_line_number => |ti| { |
| 1570 | 1596 | const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); |
| ... | ... | @@ -1593,7 +1619,7 @@ pub fn spawnLld( |
| 1593 | 1619 | const exit_code = try lldMain(arena, argv, false); |
| 1594 | 1620 | if (exit_code == 0) return; |
| 1595 | 1621 | if (comp.clang_passthrough_mode) std.process.exit(exit_code); |
| 1596 | return error.LLDReportedFailure; | |
| 1622 | return error.LinkFailure; | |
| 1597 | 1623 | } |
| 1598 | 1624 | |
| 1599 | 1625 | var stderr: []u8 = &.{}; |
| ... | ... | @@ -1670,17 +1696,16 @@ pub fn spawnLld( |
| 1670 | 1696 | return error.UnableToSpawnSelf; |
| 1671 | 1697 | }; |
| 1672 | 1698 | |
| 1699 | const diags = &comp.link_diags; | |
| 1673 | 1700 | switch (term) { |
| 1674 | 1701 | .Exited => |code| if (code != 0) { |
| 1675 | 1702 | if (comp.clang_passthrough_mode) std.process.exit(code); |
| 1676 | const diags = &comp.link_diags; | |
| 1677 | 1703 | diags.lockAndParseLldStderr(argv[1], stderr); |
| 1678 | return error.LLDReportedFailure; | |
| 1704 | return error.LinkFailure; | |
| 1679 | 1705 | }, |
| 1680 | 1706 | else => { |
| 1681 | 1707 | if (comp.clang_passthrough_mode) std.process.abort(); |
| 1682 | log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); | |
| 1683 | return error.LLDCrashed; | |
| 1708 | return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); | |
| 1684 | 1709 | }, |
| 1685 | 1710 | } |
| 1686 | 1711 | |
| ... | ... | @@ -2239,7 +2264,7 @@ fn resolvePathInputLib( |
| 2239 | 2264 | try wip_errors.init(gpa); |
| 2240 | 2265 | defer wip_errors.deinit(); |
| 2241 | 2266 | |
| 2242 | try diags.addMessagesToBundle(&wip_errors); | |
| 2267 | try diags.addMessagesToBundle(&wip_errors, null); | |
| 2243 | 2268 | |
| 2244 | 2269 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 2245 | 2270 | defer error_bundle.deinit(gpa); |
src/link/C.zig+11-16| ... | ... | @@ -175,21 +175,13 @@ pub fn deinit(self: *C) void { |
| 175 | 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 | 178 | pub fn updateFunc( |
| 187 | 179 | self: *C, |
| 188 | 180 | pt: Zcu.PerThread, |
| 189 | 181 | func_index: InternPool.Index, |
| 190 | 182 | air: Air, |
| 191 | 183 | liveness: Liveness, |
| 192 | ) !void { | |
| 184 | ) link.File.UpdateNavError!void { | |
| 193 | 185 | const zcu = pt.zcu; |
| 194 | 186 | const gpa = zcu.gpa; |
| 195 | 187 | const func = zcu.funcInfo(func_index); |
| ... | ... | @@ -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 | 309 | const tracy = trace(@src()); |
| 318 | 310 | defer tracy.end(); |
| 319 | 311 | |
| ... | ... | @@ -390,7 +382,7 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn |
| 390 | 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 | 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 | 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 | 405 | _ = arena; // Has the same lifetime as the call to Compilation.update. |
| 414 | 406 | |
| 415 | 407 | const tracy = trace(@src()); |
| ... | ... | @@ -419,6 +411,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 419 | 411 | defer sub_prog_node.end(); |
| 420 | 412 | |
| 421 | 413 | const comp = self.base.comp; |
| 414 | const diags = &comp.link_diags; | |
| 422 | 415 | const gpa = comp.gpa; |
| 423 | 416 | const zcu = self.base.comp.zcu.?; |
| 424 | 417 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -476,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 476 | 469 | defer export_names.deinit(gpa); |
| 477 | 470 | try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count())); |
| 478 | 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 | 474 | for (zcu.multi_exports.values()) |info| { |
| 482 | 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 | 547 | }, self.getString(av_block.code)); |
| 555 | 548 | |
| 556 | 549 | const file = self.base.file.?; |
| 557 | try file.setEndPos(f.file_size); | |
| 558 | try file.pwritevAll(f.all_buffers.items, 0); | |
| 550 | file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)}); | |
| 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 | 556 | const Flush = struct { |
| ... | ... | @@ -845,7 +840,7 @@ pub fn updateExports( |
| 845 | 840 | self: *C, |
| 846 | 841 | pt: Zcu.PerThread, |
| 847 | 842 | exported: Zcu.Exported, |
| 848 | export_indices: []const u32, | |
| 843 | export_indices: []const Zcu.Export.Index, | |
| 849 | 844 | ) !void { |
| 850 | 845 | const zcu = pt.zcu; |
| 851 | 846 | const gpa = zcu.gpa; |
src/link/Coff.zig+126-85| ... | ... | @@ -408,7 +408,7 @@ pub fn createEmpty( |
| 408 | 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 | 414 | return coff; |
| ... | ... | @@ -858,7 +858,7 @@ fn writeAtom(coff: *Coff, atom_index: Atom.Index, code: []u8) !void { |
| 858 | 858 | } |
| 859 | 859 | |
| 860 | 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 | 863 | // Now we can mark the relocs as resolved. |
| 864 | 864 | while (relocs.popOrNull()) |reloc| { |
| ... | ... | @@ -891,7 +891,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void { |
| 891 | 891 | const sect_id = coff.got_section_index.?; |
| 892 | 892 | |
| 893 | 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 | 895 | try coff.growSection(sect_id, needed_size); |
| 896 | 896 | coff.got_table_count_dirty = false; |
| 897 | 897 | } |
| ... | ... | @@ -908,7 +908,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void { |
| 908 | 908 | switch (coff.ptr_width) { |
| 909 | 909 | .p32 => { |
| 910 | 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 | 912 | try coff.base.file.?.pwriteAll(&buf, file_offset); |
| 913 | 913 | }, |
| 914 | 914 | .p64 => { |
| ... | ... | @@ -1093,7 +1093,13 @@ fn freeAtom(coff: *Coff, atom_index: Atom.Index) void { |
| 1093 | 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 | 1103 | if (build_options.skip_non_native and builtin.object_format != .coff) { |
| 1098 | 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 | 1112 | const zcu = pt.zcu; |
| 1107 | 1113 | const gpa = zcu.gpa; |
| 1108 | 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 | 1118 | coff.freeRelocations(atom_index); |
| 1112 | 1119 | |
| 1113 | 1120 | coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?; |
| 1114 | 1121 | |
| 1115 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1116 | defer code_buffer.deinit(); | |
| 1122 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1123 | defer code_buffer.deinit(gpa); | |
| 1117 | 1124 | |
| 1118 | const res = try codegen.generateFunction( | |
| 1125 | codegen.generateFunction( | |
| 1119 | 1126 | &coff.base, |
| 1120 | 1127 | pt, |
| 1121 | zcu.navSrcLoc(func.owner_nav), | |
| 1128 | zcu.navSrcLoc(nav_index), | |
| 1122 | 1129 | func_index, |
| 1123 | 1130 | air, |
| 1124 | 1131 | liveness, |
| 1125 | 1132 | &code_buffer, |
| 1126 | 1133 | .none, |
| 1127 | ); | |
| 1128 | const code = switch (res) { | |
| 1129 | .ok => code_buffer.items, | |
| 1130 | .fail => |em| { | |
| 1131 | try zcu.failed_codegen.put(zcu.gpa, func.owner_nav, em); | |
| 1132 | return; | |
| 1134 | ) catch |err| switch (err) { | |
| 1135 | error.CodegenFail => return error.CodegenFail, | |
| 1136 | error.OutOfMemory => return error.OutOfMemory, | |
| 1137 | error.Overflow => |e| { | |
| 1138 | try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create( | |
| 1139 | gpa, | |
| 1140 | zcu.navSrcLoc(nav_index), | |
| 1141 | "unable to codegen: {s}", | |
| 1142 | .{@errorName(e)}, | |
| 1143 | )); | |
| 1144 | try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index })); | |
| 1145 | return error.CodegenFail; | |
| 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 | 1151 | // Exports will be updated by `Zcu.processExports` after the update. |
| 1139 | 1152 | } |
| ... | ... | @@ -1154,24 +1167,21 @@ fn lowerConst( |
| 1154 | 1167 | ) !LowerConstResult { |
| 1155 | 1168 | const gpa = coff.base.comp.gpa; |
| 1156 | 1169 | |
| 1157 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1158 | defer code_buffer.deinit(); | |
| 1170 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1171 | defer code_buffer.deinit(gpa); | |
| 1159 | 1172 | |
| 1160 | 1173 | const atom_index = try coff.createAtom(); |
| 1161 | 1174 | const sym = coff.getAtom(atom_index).getSymbolPtr(coff); |
| 1162 | 1175 | try coff.setSymbolName(sym, name); |
| 1163 | 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 | 1179 | .atom_index = coff.getAtom(atom_index).getSymbolIndex().?, |
| 1167 | 1180 | }); |
| 1168 | const code = switch (res) { | |
| 1169 | .ok => code_buffer.items, | |
| 1170 | .fail => |em| return .{ .fail = em }, | |
| 1171 | }; | |
| 1181 | const code = code_buffer.items; | |
| 1172 | 1182 | |
| 1173 | 1183 | const atom = coff.getAtomPtr(atom_index); |
| 1174 | atom.size = @as(u32, @intCast(code.len)); | |
| 1184 | atom.size = @intCast(code.len); | |
| 1175 | 1185 | atom.getSymbolPtr(coff).value = try coff.allocateAtom( |
| 1176 | 1186 | atom_index, |
| 1177 | 1187 | atom.size, |
| ... | ... | @@ -1227,10 +1237,10 @@ pub fn updateNav( |
| 1227 | 1237 | |
| 1228 | 1238 | coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index); |
| 1229 | 1239 | |
| 1230 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1231 | defer code_buffer.deinit(); | |
| 1240 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1241 | defer code_buffer.deinit(gpa); | |
| 1232 | 1242 | |
| 1233 | const res = try codegen.generateSymbol( | |
| 1243 | try codegen.generateSymbol( | |
| 1234 | 1244 | &coff.base, |
| 1235 | 1245 | pt, |
| 1236 | 1246 | zcu.navSrcLoc(nav_index), |
| ... | ... | @@ -1238,15 +1248,8 @@ pub fn updateNav( |
| 1238 | 1248 | &code_buffer, |
| 1239 | 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 | 1255 | // Exports will be updated by `Zcu.processExports` after the update. |
| ... | ... | @@ -1260,11 +1263,12 @@ fn updateLazySymbolAtom( |
| 1260 | 1263 | section_index: u16, |
| 1261 | 1264 | ) !void { |
| 1262 | 1265 | const zcu = pt.zcu; |
| 1263 | const gpa = zcu.gpa; | |
| 1266 | const comp = coff.base.comp; | |
| 1267 | const gpa = comp.gpa; | |
| 1264 | 1268 | |
| 1265 | 1269 | var required_alignment: InternPool.Alignment = .none; |
| 1266 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1267 | defer code_buffer.deinit(); | |
| 1270 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1271 | defer code_buffer.deinit(gpa); | |
| 1268 | 1272 | |
| 1269 | 1273 | const name = try allocPrint(gpa, "__lazy_{s}_{}", .{ |
| 1270 | 1274 | @tagName(sym.kind), |
| ... | ... | @@ -1276,7 +1280,7 @@ fn updateLazySymbolAtom( |
| 1276 | 1280 | const local_sym_index = atom.getSymbolIndex().?; |
| 1277 | 1281 | |
| 1278 | 1282 | const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1279 | const res = try codegen.generateLazySymbol( | |
| 1283 | try codegen.generateLazySymbol( | |
| 1280 | 1284 | &coff.base, |
| 1281 | 1285 | pt, |
| 1282 | 1286 | src, |
| ... | ... | @@ -1286,13 +1290,7 @@ fn updateLazySymbolAtom( |
| 1286 | 1290 | .none, |
| 1287 | 1291 | .{ .atom_index = local_sym_index }, |
| 1288 | 1292 | ); |
| 1289 | const code = switch (res) { | |
| 1290 | .ok => code_buffer.items, | |
| 1291 | .fail => |em| { | |
| 1292 | log.err("{s}", .{em.msg}); | |
| 1293 | return error.CodegenFail; | |
| 1294 | }, | |
| 1295 | }; | |
| 1293 | const code = code_buffer.items; | |
| 1296 | 1294 | |
| 1297 | 1295 | const code_len: u32 = @intCast(code.len); |
| 1298 | 1296 | const symbol = atom.getSymbolPtr(coff); |
| ... | ... | @@ -1387,7 +1385,7 @@ fn updateNavCode( |
| 1387 | 1385 | nav_index: InternPool.Nav.Index, |
| 1388 | 1386 | code: []u8, |
| 1389 | 1387 | complex_type: coff_util.ComplexType, |
| 1390 | ) !void { | |
| 1388 | ) link.File.UpdateNavError!void { | |
| 1391 | 1389 | const zcu = pt.zcu; |
| 1392 | 1390 | const ip = &zcu.intern_pool; |
| 1393 | 1391 | const nav = ip.getNav(nav_index); |
| ... | ... | @@ -1405,18 +1403,21 @@ fn updateNavCode( |
| 1405 | 1403 | const atom = coff.getAtom(atom_index); |
| 1406 | 1404 | const sym_index = atom.getSymbolIndex().?; |
| 1407 | 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 | 1408 | if (atom.size != 0) { |
| 1411 | 1409 | const sym = atom.getSymbolPtr(coff); |
| 1412 | 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 | 1412 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; |
| 1415 | 1413 | |
| 1416 | 1414 | const capacity = atom.capacity(coff); |
| 1417 | 1415 | const need_realloc = code.len > capacity or !required_alignment.check(sym.value); |
| 1418 | 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 | 1421 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), sym.value, vaddr }); |
| 1421 | 1422 | log.debug(" (required alignment 0x{x}", .{required_alignment}); |
| 1422 | 1423 | |
| ... | ... | @@ -1424,7 +1425,10 @@ fn updateNavCode( |
| 1424 | 1425 | sym.value = vaddr; |
| 1425 | 1426 | log.debug(" (updating GOT entry)", .{}); |
| 1426 | 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 | 1432 | coff.markRelocsDirtyByTarget(.{ .sym_index = sym_index }); |
| 1429 | 1433 | } |
| 1430 | 1434 | } else if (code_len < atom.size) { |
| ... | ... | @@ -1434,26 +1438,34 @@ fn updateNavCode( |
| 1434 | 1438 | } else { |
| 1435 | 1439 | const sym = atom.getSymbolPtr(coff); |
| 1436 | 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 | 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 | 1448 | errdefer coff.freeAtom(atom_index); |
| 1442 | 1449 | log.debug("allocated atom for {} at 0x{x}", .{ nav.fqn.fmt(ip), vaddr }); |
| 1443 | 1450 | coff.getAtomPtr(atom_index).size = code_len; |
| 1444 | 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 | 1465 | pub fn freeNav(coff: *Coff, nav_index: InternPool.NavIndex) void { |
| 1453 | 1466 | if (coff.llvm_object) |llvm_object| return llvm_object.freeNav(nav_index); |
| 1454 | 1467 | |
| 1455 | 1468 | const gpa = coff.base.comp.gpa; |
| 1456 | log.debug("freeDecl 0x{x}", .{nav_index}); | |
| 1457 | 1469 | |
| 1458 | 1470 | if (coff.decls.fetchOrderedRemove(nav_index)) |const_kv| { |
| 1459 | 1471 | var kv = const_kv; |
| ... | ... | @@ -1466,7 +1478,7 @@ pub fn updateExports( |
| 1466 | 1478 | coff: *Coff, |
| 1467 | 1479 | pt: Zcu.PerThread, |
| 1468 | 1480 | exported: Zcu.Exported, |
| 1469 | export_indices: []const u32, | |
| 1481 | export_indices: []const Zcu.Export.Index, | |
| 1470 | 1482 | ) link.File.UpdateExportsError!void { |
| 1471 | 1483 | if (build_options.skip_non_native and builtin.object_format != .coff) { |
| 1472 | 1484 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | ... | @@ -1481,7 +1493,7 @@ pub fn updateExports( |
| 1481 | 1493 | // Even in the case of LLVM, we need to notice certain exported symbols in order to |
| 1482 | 1494 | // detect the default subsystem. |
| 1483 | 1495 | for (export_indices) |export_idx| { |
| 1484 | const exp = zcu.all_exports.items[export_idx]; | |
| 1496 | const exp = export_idx.ptr(zcu); | |
| 1485 | 1497 | const exported_nav_index = switch (exp.exported) { |
| 1486 | 1498 | .nav => |nav| nav, |
| 1487 | 1499 | .uav => continue, |
| ... | ... | @@ -1524,7 +1536,7 @@ pub fn updateExports( |
| 1524 | 1536 | break :blk coff.navs.getPtr(nav).?; |
| 1525 | 1537 | }, |
| 1526 | 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 | 1540 | const res = try coff.lowerUav(pt, uav, .none, first_exp.src); |
| 1529 | 1541 | switch (res) { |
| 1530 | 1542 | .mcv => {}, |
| ... | ... | @@ -1543,7 +1555,7 @@ pub fn updateExports( |
| 1543 | 1555 | const atom = coff.getAtom(atom_index); |
| 1544 | 1556 | |
| 1545 | 1557 | for (export_indices) |export_idx| { |
| 1546 | const exp = zcu.all_exports.items[export_idx]; | |
| 1558 | const exp = export_idx.ptr(zcu); | |
| 1547 | 1559 | log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)}); |
| 1548 | 1560 | |
| 1549 | 1561 | if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| { |
| ... | ... | @@ -1671,12 +1683,17 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void { |
| 1671 | 1683 | pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 1672 | 1684 | const comp = coff.base.comp; |
| 1673 | 1685 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 1686 | const diags = &comp.link_diags; | |
| 1674 | 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 | 1694 | switch (comp.config.output_mode) { |
| 1678 | 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 | 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 | 2233 | const tracy = trace(@src()); |
| 2212 | 2234 | defer tracy.end(); |
| 2213 | 2235 | |
| 2214 | 2236 | const comp = coff.base.comp; |
| 2215 | const gpa = comp.gpa; | |
| 2216 | 2237 | const diags = &comp.link_diags; |
| 2217 | 2238 | |
| 2218 | 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 | 2244 | const sub_prog_node = prog_node.start("COFF Flush", 0); |
| 2224 | 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 | 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 | 2263 | tid, |
| 2229 | 2264 | ); |
| 2230 | 2265 | defer pt.deactivate(); |
| ... | ... | @@ -2232,24 +2267,18 @@ pub fn flushModule(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no |
| 2232 | 2267 | if (coff.lazy_syms.getPtr(.anyerror_type)) |metadata| { |
| 2233 | 2268 | // Most lazy symbols can be updated on first use, but |
| 2234 | 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 | 2271 | pt, |
| 2237 | 2272 | .{ .kind = .code, .ty = .anyerror_type }, |
| 2238 | 2273 | metadata.text_atom, |
| 2239 | 2274 | coff.text_section_index.?, |
| 2240 | ) catch |err| return switch (err) { | |
| 2241 | error.CodegenFail => error.FlushFailure, | |
| 2242 | else => |e| e, | |
| 2243 | }; | |
| 2244 | if (metadata.rdata_state != .unused) coff.updateLazySymbolAtom( | |
| 2275 | ); | |
| 2276 | if (metadata.rdata_state != .unused) try coff.updateLazySymbolAtom( | |
| 2245 | 2277 | pt, |
| 2246 | 2278 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 2247 | 2279 | metadata.rdata_atom, |
| 2248 | 2280 | coff.rdata_section_index.?, |
| 2249 | ) catch |err| return switch (err) { | |
| 2250 | error.CodegenFail => error.FlushFailure, | |
| 2251 | else => |e| e, | |
| 2252 | }; | |
| 2281 | ); | |
| 2253 | 2282 | } |
| 2254 | 2283 | for (coff.lazy_syms.values()) |*metadata| { |
| 2255 | 2284 | if (metadata.text_state != .unused) metadata.text_state = .flushed; |
| ... | ... | @@ -2594,7 +2623,7 @@ fn writeBaseRelocations(coff: *Coff) !void { |
| 2594 | 2623 | const needed_size = @as(u32, @intCast(buffer.items.len)); |
| 2595 | 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 | 2628 | coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.BASERELOC)] = .{ |
| 2600 | 2629 | .virtual_address = header.virtual_address, |
| ... | ... | @@ -2727,7 +2756,7 @@ fn writeImportTables(coff: *Coff) !void { |
| 2727 | 2756 | |
| 2728 | 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 | 2761 | coff.data_directories[@intFromEnum(coff_util.DirectoryEntry.IMPORT)] = .{ |
| 2733 | 2762 | .virtual_address = header.virtual_address + iat_size, |
| ... | ... | @@ -2744,17 +2773,19 @@ fn writeImportTables(coff: *Coff) !void { |
| 2744 | 2773 | fn writeStrtab(coff: *Coff) !void { |
| 2745 | 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 | 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 | 2782 | if (needed_size > allocated_size) { |
| 2751 | 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 | 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 | 2789 | var buffer = std.ArrayList(u8).init(gpa); |
| 2759 | 2790 | defer buffer.deinit(); |
| 2760 | 2791 | try buffer.ensureTotalCapacityPrecise(needed_size); |
| ... | ... | @@ -2763,17 +2794,19 @@ fn writeStrtab(coff: *Coff) !void { |
| 2763 | 2794 | // we write the length of the strtab to a temporary buffer that goes to file. |
| 2764 | 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 | 2802 | fn writeSectionHeaders(coff: *Coff) !void { |
| 2770 | 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 | 2807 | fn writeDataDirectoriesHeaders(coff: *Coff) !void { |
| 2775 | 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 | 2812 | 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 | 2952 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| ... | ... | @@ -3710,6 +3743,14 @@ const ImportTable = struct { |
| 3710 | 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 | 3754 | const Coff = @This(); |
| 3714 | 3755 | |
| 3715 | 3756 | const std = @import("std"); |
src/link/Dwarf.zig+35-15| ... | ... | @@ -21,7 +21,6 @@ debug_rnglists: DebugRngLists, |
| 21 | 21 | debug_str: StringSection, |
| 22 | 22 | |
| 23 | 23 | pub const UpdateError = error{ |
| 24 | CodegenFail, | |
| 25 | 24 | ReinterpretDeclRef, |
| 26 | 25 | Unimplemented, |
| 27 | 26 | OutOfMemory, |
| ... | ... | @@ -451,7 +450,6 @@ pub const Section = struct { |
| 451 | 450 | const zo = elf_file.zigObjectPtr().?; |
| 452 | 451 | const atom = zo.symbol(sec.index).atom(elf_file).?; |
| 453 | 452 | if (atom.prevAtom(elf_file)) |_| { |
| 454 | // FIXME:JK trimming/shrinking has to be reworked on ZigObject/Elf level | |
| 455 | 453 | atom.value += len; |
| 456 | 454 | } else { |
| 457 | 455 | const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index]; |
| ... | ... | @@ -600,12 +598,13 @@ const Unit = struct { |
| 600 | 598 | |
| 601 | 599 | fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void { |
| 602 | 600 | if (unit.off == new_off) return; |
| 603 | if (try dwarf.getFile().?.copyRangeAll( | |
| 601 | const n = try dwarf.getFile().?.copyRangeAll( | |
| 604 | 602 | sec.off(dwarf) + unit.off, |
| 605 | 603 | dwarf.getFile().?, |
| 606 | 604 | sec.off(dwarf) + new_off, |
| 607 | 605 | unit.len, |
| 608 | ) != unit.len) return error.InputOutput; | |
| 606 | ); | |
| 607 | if (n != unit.len) return error.InputOutput; | |
| 609 | 608 | unit.off = new_off; |
| 610 | 609 | } |
| 611 | 610 | |
| ... | ... | @@ -1891,19 +1890,16 @@ pub const WipNav = struct { |
| 1891 | 1890 | const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0; |
| 1892 | 1891 | try uleb128(diw, bytes); |
| 1893 | 1892 | if (bytes == 0) return; |
| 1894 | var dim = wip_nav.debug_info.toManaged(wip_nav.dwarf.gpa); | |
| 1895 | defer wip_nav.debug_info = dim.moveToUnmanaged(); | |
| 1896 | switch (try codegen.generateSymbol( | |
| 1893 | const old_len = wip_nav.debug_info.items.len; | |
| 1894 | try codegen.generateSymbol( | |
| 1897 | 1895 | wip_nav.dwarf.bin_file, |
| 1898 | 1896 | wip_nav.pt, |
| 1899 | 1897 | src_loc, |
| 1900 | 1898 | val, |
| 1901 | &dim, | |
| 1899 | &wip_nav.debug_info, | |
| 1902 | 1900 | .{ .debug_output = .{ .dwarf = wip_nav } }, |
| 1903 | )) { | |
| 1904 | .ok => assert(dim.items.len == wip_nav.debug_info.items.len + bytes), | |
| 1905 | .fail => unreachable, | |
| 1906 | } | |
| 1901 | ); | |
| 1902 | assert(old_len + bytes == wip_nav.debug_info.items.len); | |
| 1907 | 1903 | } |
| 1908 | 1904 | |
| 1909 | 1905 | const AbbrevCodeForForm = struct { |
| ... | ... | @@ -2278,7 +2274,7 @@ pub fn deinit(dwarf: *Dwarf) void { |
| 2278 | 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 | 2278 | const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); |
| 2283 | 2279 | const unit: Unit.Index = @enumFromInt(mod_gop.index); |
| 2284 | 2280 | if (!mod_gop.found_existing) { |
| ... | ... | @@ -2338,7 +2334,24 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo { |
| 2338 | 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 | 2355 | const zcu = pt.zcu; |
| 2343 | 2356 | const ip = &zcu.intern_pool; |
| 2344 | 2357 | |
| ... | ... | @@ -2667,7 +2680,14 @@ pub fn finishWipNav( |
| 2667 | 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 | 2691 | const zcu = pt.zcu; |
| 2672 | 2692 | const ip = &zcu.intern_pool; |
| 2673 | 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 | 795 | } |
| 796 | 796 | |
| 797 | 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 | 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 | 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 | 813 | defer tracy.end(); |
| 808 | 814 | |
| 809 | 815 | const comp = self.base.comp; |
| 810 | const gpa = comp.gpa; | |
| 811 | 816 | const diags = &comp.link_diags; |
| 812 | 817 | |
| 813 | 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 | 826 | const sub_prog_node = prog_node.start("ELF Flush", 0); |
| 822 | 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 | 841 | const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ |
| 825 | 842 | .root_dir = self.base.emit.root_dir, |
| 826 | 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 | 859 | .Exe => {}, |
| 843 | 860 | } |
| 844 | 861 | |
| 845 | if (diags.hasErrors()) return error.FlushFailure; | |
| 862 | if (diags.hasErrors()) return error.LinkFailure; | |
| 846 | 863 | |
| 847 | 864 | // If we haven't already, create a linker-generated input file comprising of |
| 848 | 865 | // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. |
| 849 | 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 | 868 | self.files.set(index, .{ .linker_defined = .{ .index = index } }); |
| 852 | 869 | self.linker_defined_index = index; |
| 853 | 870 | const object = self.linkerDefinedPtr().?; |
| ... | ... | @@ -878,7 +895,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 878 | 895 | } |
| 879 | 896 | |
| 880 | 897 | self.checkDuplicates() catch |err| switch (err) { |
| 881 | error.HasDuplicates => return error.FlushFailure, | |
| 898 | error.HasDuplicates => return error.LinkFailure, | |
| 882 | 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 | 973 | error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, |
| 957 | 974 | error.UnsupportedCpuArch => { |
| 958 | 975 | try self.reportUnsupportedCpuArch(); |
| 959 | return error.FlushFailure; | |
| 976 | return error.LinkFailure; | |
| 960 | 977 | }, |
| 961 | 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 | 986 | try self.writePhdrTable(); |
| ... | ... | @@ -972,10 +989,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod |
| 972 | 989 | try self.writeMergeSections(); |
| 973 | 990 | |
| 974 | 991 | self.writeSyntheticSections() catch |err| switch (err) { |
| 975 | error.RelocFailure => return error.FlushFailure, | |
| 992 | error.RelocFailure => return error.LinkFailure, | |
| 976 | 993 | error.UnsupportedCpuArch => { |
| 977 | 994 | try self.reportUnsupportedCpuArch(); |
| 978 | return error.FlushFailure; | |
| 995 | return error.LinkFailure; | |
| 979 | 996 | }, |
| 980 | 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 | 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 | 1012 | fn dumpArgvInit(self: *Elf, arena: Allocator) !void { |
| ... | ... | @@ -1389,7 +1406,7 @@ fn scanRelocs(self: *Elf) !void { |
| 1389 | 1406 | error.RelaxFailure => unreachable, |
| 1390 | 1407 | error.UnsupportedCpuArch => { |
| 1391 | 1408 | try self.reportUnsupportedCpuArch(); |
| 1392 | return error.FlushFailure; | |
| 1409 | return error.LinkFailure; | |
| 1393 | 1410 | }, |
| 1394 | 1411 | error.RelocFailure => has_reloc_errors = true, |
| 1395 | 1412 | else => |e| return e, |
| ... | ... | @@ -1400,7 +1417,7 @@ fn scanRelocs(self: *Elf) !void { |
| 1400 | 1417 | error.RelaxFailure => unreachable, |
| 1401 | 1418 | error.UnsupportedCpuArch => { |
| 1402 | 1419 | try self.reportUnsupportedCpuArch(); |
| 1403 | return error.FlushFailure; | |
| 1420 | return error.LinkFailure; | |
| 1404 | 1421 | }, |
| 1405 | 1422 | error.RelocFailure => has_reloc_errors = true, |
| 1406 | 1423 | else => |e| return e, |
| ... | ... | @@ -1409,7 +1426,7 @@ fn scanRelocs(self: *Elf) !void { |
| 1409 | 1426 | |
| 1410 | 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 | 1431 | if (self.zigObjectPtr()) |zo| { |
| 1415 | 1432 | try zo.asFile().createSymbolIndirection(self); |
| ... | ... | @@ -2117,7 +2134,7 @@ pub fn writeShdrTable(self: *Elf) !void { |
| 2117 | 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 | 2139 | .p64 => { |
| 2123 | 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 | 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 | 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 | 2179 | .p64 => { |
| 2163 | 2180 | const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len); |
| ... | ... | @@ -2169,7 +2186,7 @@ fn writePhdrTable(self: *Elf) !void { |
| 2169 | 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 | 2336 | |
| 2320 | 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 | 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 | 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 | 2354 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2332 | 2355 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 2333 | 2356 | } |
| ... | ... | @@ -2351,19 +2374,32 @@ pub fn updateContainerType( |
| 2351 | 2374 | self: *Elf, |
| 2352 | 2375 | pt: Zcu.PerThread, |
| 2353 | 2376 | ty: InternPool.Index, |
| 2354 | ) link.File.UpdateNavError!void { | |
| 2377 | ) link.File.UpdateContainerTypeError!void { | |
| 2355 | 2378 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2356 | 2379 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 2357 | 2380 | } |
| 2358 | 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 | 2398 | pub fn updateExports( |
| 2363 | 2399 | self: *Elf, |
| 2364 | 2400 | pt: Zcu.PerThread, |
| 2365 | 2401 | exported: Zcu.Exported, |
| 2366 | export_indices: []const u32, | |
| 2402 | export_indices: []const Zcu.Export.Index, | |
| 2367 | 2403 | ) link.File.UpdateExportsError!void { |
| 2368 | 2404 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2369 | 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 | 2477 | }; |
| 2442 | 2478 | } |
| 2443 | 2479 | |
| 2444 | if (has_errors) return error.FlushFailure; | |
| 2480 | if (has_errors) return error.LinkFailure; | |
| 2445 | 2481 | |
| 2446 | 2482 | for (self.objects.items) |index| { |
| 2447 | 2483 | const object = self.file(index).?.object; |
| ... | ... | @@ -2491,8 +2527,8 @@ pub fn writeMergeSections(self: *Elf) !void { |
| 2491 | 2527 | |
| 2492 | 2528 | for (self.merge_sections.items) |*msec| { |
| 2493 | 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; | |
| 2495 | const size = math.cast(usize, msec.size) orelse return error.Overflow; | |
| 2530 | const fileoff = try self.cast(usize, msec.value + shdr.sh_offset); | |
| 2531 | const size = try self.cast(usize, msec.size); | |
| 2496 | 2532 | try buffer.ensureTotalCapacity(size); |
| 2497 | 2533 | buffer.appendNTimesAssumeCapacity(0, size); |
| 2498 | 2534 | |
| ... | ... | @@ -2500,11 +2536,11 @@ pub fn writeMergeSections(self: *Elf) !void { |
| 2500 | 2536 | const msub = msec.mergeSubsection(msub_index); |
| 2501 | 2537 | assert(msub.alive); |
| 2502 | 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 | 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 | 2544 | buffer.clearRetainingCapacity(); |
| 2509 | 2545 | } |
| 2510 | 2546 | } |
| ... | ... | @@ -3121,9 +3157,6 @@ pub fn sortShdrs( |
| 3121 | 3157 | fileLookup(files, ref.file, zig_object_ptr).?.atom(ref.index).?.output_section_index = atom_list.output_section_index; |
| 3122 | 3158 | } |
| 3123 | 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 | 3160 | shdr.sh_link = section_indexes.symtab.?; |
| 3128 | 3161 | shdr.sh_info = backlinks[shdr.sh_info]; |
| 3129 | 3162 | } |
| ... | ... | @@ -3211,7 +3244,7 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3211 | 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 | 3248 | for (self.thunks.items) |*th| { |
| 3216 | 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 | 3330 | self.updateShStrtabSize(); |
| 3298 | 3331 | } |
| 3299 | 3332 | |
| 3300 | // FIXME:JK this is very much obsolete, remove! | |
| 3301 | 3333 | pub fn updateShStrtabSize(self: *Elf) void { |
| 3302 | 3334 | if (self.section_indexes.shstrtab) |index| { |
| 3303 | 3335 | self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len; |
| ... | ... | @@ -3362,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void { |
| 3362 | 3394 | // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op |
| 3363 | 3395 | var err = try diags.addErrorWithNotes(1); |
| 3364 | 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 | 3400 | phdr_table_load.p_filesz = needed_size + ehsize; |
| ... | ... | @@ -3658,7 +3690,7 @@ fn writeAtoms(self: *Elf) !void { |
| 3658 | 3690 | atom_list.write(&buffer, &undefs, self) catch |err| switch (err) { |
| 3659 | 3691 | error.UnsupportedCpuArch => { |
| 3660 | 3692 | try self.reportUnsupportedCpuArch(); |
| 3661 | return error.FlushFailure; | |
| 3693 | return error.LinkFailure; | |
| 3662 | 3694 | }, |
| 3663 | 3695 | error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, |
| 3664 | 3696 | else => |e| return e, |
| ... | ... | @@ -3666,7 +3698,7 @@ fn writeAtoms(self: *Elf) !void { |
| 3666 | 3698 | } |
| 3667 | 3699 | |
| 3668 | 3700 | try self.reportUndefinedSymbols(&undefs); |
| 3669 | if (has_reloc_errors) return error.FlushFailure; | |
| 3701 | if (has_reloc_errors) return error.LinkFailure; | |
| 3670 | 3702 | |
| 3671 | 3703 | if (self.requiresThunks()) { |
| 3672 | 3704 | for (self.thunks.items) |th| { |
| ... | ... | @@ -3676,7 +3708,7 @@ fn writeAtoms(self: *Elf) !void { |
| 3676 | 3708 | const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset; |
| 3677 | 3709 | try th.write(self, buffer.writer()); |
| 3678 | 3710 | assert(buffer.items.len == thunk_size); |
| 3679 | try self.base.file.?.pwriteAll(buffer.items, offset); | |
| 3711 | try self.pwriteAll(buffer.items, offset); | |
| 3680 | 3712 | buffer.clearRetainingCapacity(); |
| 3681 | 3713 | } |
| 3682 | 3714 | } |
| ... | ... | @@ -3784,12 +3816,12 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3784 | 3816 | const contents = buffer[0 .. interp.len + 1]; |
| 3785 | 3817 | const shdr = slice.items(.shdr)[shndx]; |
| 3786 | 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 | 3822 | if (self.section_indexes.hash) |shndx| { |
| 3791 | 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 | 3827 | if (self.section_indexes.gnu_hash) |shndx| { |
| ... | ... | @@ -3797,12 +3829,12 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3797 | 3829 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size()); |
| 3798 | 3830 | defer buffer.deinit(); |
| 3799 | 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 | 3835 | if (self.section_indexes.versym) |shndx| { |
| 3804 | 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 | 3840 | if (self.section_indexes.verneed) |shndx| { |
| ... | ... | @@ -3810,7 +3842,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3810 | 3842 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size()); |
| 3811 | 3843 | defer buffer.deinit(); |
| 3812 | 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 | 3848 | if (self.section_indexes.dynamic) |shndx| { |
| ... | ... | @@ -3818,7 +3850,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3818 | 3850 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self)); |
| 3819 | 3851 | defer buffer.deinit(); |
| 3820 | 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 | 3856 | if (self.section_indexes.dynsymtab) |shndx| { |
| ... | ... | @@ -3826,12 +3858,12 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3826 | 3858 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size()); |
| 3827 | 3859 | defer buffer.deinit(); |
| 3828 | 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 | 3864 | if (self.section_indexes.dynstrtab) |shndx| { |
| 3833 | 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 | 3869 | if (self.section_indexes.eh_frame) |shndx| { |
| ... | ... | @@ -3841,21 +3873,21 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3841 | 3873 | break :existing_size sym.atom(self).?.size; |
| 3842 | 3874 | }; |
| 3843 | 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 | 3877 | var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size)); |
| 3846 | 3878 | defer buffer.deinit(); |
| 3847 | 3879 | try eh_frame.writeEhFrame(self, buffer.writer()); |
| 3848 | 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 | 3884 | if (self.section_indexes.eh_frame_hdr) |shndx| { |
| 3853 | 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 | 3887 | var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); |
| 3856 | 3888 | defer buffer.deinit(); |
| 3857 | 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 | 3893 | if (self.section_indexes.got) |index| { |
| ... | ... | @@ -3863,7 +3895,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3863 | 3895 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); |
| 3864 | 3896 | defer buffer.deinit(); |
| 3865 | 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 | 3901 | if (self.section_indexes.rela_dyn) |shndx| { |
| ... | ... | @@ -3871,7 +3903,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3871 | 3903 | try self.got.addRela(self); |
| 3872 | 3904 | try self.copy_rel.addRela(self); |
| 3873 | 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 | 3909 | if (self.section_indexes.plt) |shndx| { |
| ... | ... | @@ -3879,7 +3911,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3879 | 3911 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self)); |
| 3880 | 3912 | defer buffer.deinit(); |
| 3881 | 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 | 3917 | if (self.section_indexes.got_plt) |shndx| { |
| ... | ... | @@ -3887,7 +3919,7 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3887 | 3919 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self)); |
| 3888 | 3920 | defer buffer.deinit(); |
| 3889 | 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 | 3925 | if (self.section_indexes.plt_got) |shndx| { |
| ... | ... | @@ -3895,25 +3927,24 @@ fn writeSyntheticSections(self: *Elf) !void { |
| 3895 | 3927 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self)); |
| 3896 | 3928 | defer buffer.deinit(); |
| 3897 | 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 | 3933 | if (self.section_indexes.rela_plt) |shndx| { |
| 3902 | 3934 | const shdr = slice.items(.shdr)[shndx]; |
| 3903 | 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 | 3939 | try self.writeSymtab(); |
| 3908 | 3940 | try self.writeShStrtab(); |
| 3909 | 3941 | } |
| 3910 | 3942 | |
| 3911 | // FIXME:JK again, why is this needed? | |
| 3912 | 3943 | pub fn writeShStrtab(self: *Elf) !void { |
| 3913 | 3944 | if (self.section_indexes.shstrtab) |index| { |
| 3914 | 3945 | const shdr = self.sections.items(.shdr)[index]; |
| 3915 | 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 | 3959 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 3929 | 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 | 3964 | log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{ |
| 3934 | 3965 | nsyms, |
| ... | ... | @@ -3941,7 +3972,7 @@ pub fn writeSymtab(self: *Elf) !void { |
| 3941 | 3972 | }); |
| 3942 | 3973 | |
| 3943 | 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 | 3976 | // TODO we could resize instead and in ZigObject/Object always access as slice |
| 3946 | 3977 | self.strtab.clearRetainingCapacity(); |
| 3947 | 3978 | self.strtab.appendAssumeCapacity(0); |
| ... | ... | @@ -4010,17 +4041,17 @@ pub fn writeSymtab(self: *Elf) !void { |
| 4010 | 4041 | }; |
| 4011 | 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 | 4046 | .p64 => { |
| 4016 | 4047 | if (foreign_endian) { |
| 4017 | 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 | 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 | 4545 | for (refs.items[0..nrefs]) |ref| { |
| 4515 | 4546 | const atom_ptr = self.atom(ref).?; |
| 4516 | 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 | 4551 | if (refs.items.len > max_notes) { |
| 4521 | 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 | 4567 | |
| 4537 | 4568 | var err = try diags.addErrorWithNotes(nnotes + 1); |
| 4538 | 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 | 4572 | var inote: usize = 0; |
| 4542 | 4573 | while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { |
| 4543 | 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 | 4578 | if (notes.items.len > max_notes) { |
| 4548 | 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 | 4601 | const diags = &self.base.comp.link_diags; |
| 4571 | 4602 | var err = try diags.addErrorWithNotes(1); |
| 4572 | 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 | 4607 | pub fn failFile( |
| ... | ... | @@ -5184,6 +5215,30 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 { |
| 5184 | 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 | 5242 | const std = @import("std"); |
| 5188 | 5243 | const build_options = @import("build_options"); |
| 5189 | 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 | 523 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), |
| 524 | 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 | 527 | return error.RelocFailure; |
| 528 | 528 | } |
| 529 | 529 | |
| ... | ... | @@ -539,7 +539,7 @@ fn reportTextRelocError( |
| 539 | 539 | rel.r_offset, |
| 540 | 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 | 543 | return error.RelocFailure; |
| 544 | 544 | } |
| 545 | 545 | |
| ... | ... | @@ -555,8 +555,8 @@ fn reportPicError( |
| 555 | 555 | rel.r_offset, |
| 556 | 556 | symbol.name(elf_file), |
| 557 | 557 | }); |
| 558 | try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | |
| 559 | try err.addNote("recompile with -fPIC", .{}); | |
| 558 | err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | |
| 559 | err.addNote("recompile with -fPIC", .{}); | |
| 560 | 560 | return error.RelocFailure; |
| 561 | 561 | } |
| 562 | 562 | |
| ... | ... | @@ -572,8 +572,8 @@ fn reportNoPicError( |
| 572 | 572 | rel.r_offset, |
| 573 | 573 | symbol.name(elf_file), |
| 574 | 574 | }); |
| 575 | try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | |
| 576 | try err.addNote("recompile with -fno-PIC", .{}); | |
| 575 | err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); | |
| 576 | err.addNote("recompile with -fno-PIC", .{}); | |
| 577 | 577 | return error.RelocFailure; |
| 578 | 578 | } |
| 579 | 579 | |
| ... | ... | @@ -1187,7 +1187,7 @@ const x86_64 = struct { |
| 1187 | 1187 | x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch { |
| 1188 | 1188 | var err = try diags.addErrorWithNotes(1); |
| 1189 | 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 | 1191 | atom.file(elf_file).?.fmtPath(), |
| 1192 | 1192 | atom.name(elf_file), |
| 1193 | 1193 | rel.r_offset, |
| ... | ... | @@ -1332,7 +1332,7 @@ const x86_64 = struct { |
| 1332 | 1332 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1333 | 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 | 1336 | self.file(elf_file).?.fmtPath(), |
| 1337 | 1337 | self.name(elf_file), |
| 1338 | 1338 | rels[0].r_offset, |
| ... | ... | @@ -1388,7 +1388,7 @@ const x86_64 = struct { |
| 1388 | 1388 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1389 | 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 | 1392 | self.file(elf_file).?.fmtPath(), |
| 1393 | 1393 | self.name(elf_file), |
| 1394 | 1394 | rels[0].r_offset, |
| ... | ... | @@ -1485,7 +1485,7 @@ const x86_64 = struct { |
| 1485 | 1485 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1486 | 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 | 1489 | self.file(elf_file).?.fmtPath(), |
| 1490 | 1490 | self.name(elf_file), |
| 1491 | 1491 | rels[0].r_offset, |
| ... | ... | @@ -1672,7 +1672,7 @@ const aarch64 = struct { |
| 1672 | 1672 | // TODO: relax |
| 1673 | 1673 | var err = try diags.addErrorWithNotes(1); |
| 1674 | 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 | 1676 | atom.file(elf_file).?.fmtPath(), |
| 1677 | 1677 | atom.name(elf_file), |
| 1678 | 1678 | r_offset, |
| ... | ... | @@ -1959,7 +1959,7 @@ const riscv = struct { |
| 1959 | 1959 | // TODO: implement searching forward |
| 1960 | 1960 | var err = try diags.addErrorWithNotes(1); |
| 1961 | 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 | 1963 | atom.file(elf_file).?.fmtPath(), |
| 1964 | 1964 | atom.name(elf_file), |
| 1965 | 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 | 58 | if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref(); |
| 59 | 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 | 63 | var idx: usize = 0; |
| 64 | 64 | while (idx < list.atoms.keys().len) : (idx += 1) { |
| ... | ... | @@ -78,7 +78,8 @@ pub fn allocate(list: *AtomList, elf_file: *Elf) !void { |
| 78 | 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 | 83 | for (list.atoms.keys()) |ref| { |
| 83 | 84 | const atom_ptr = elf_file.atom(ref).?; |
| 84 | 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 | 797 | if (!isNull(data[end .. end + sh_entsize])) { |
| 798 | 798 | var err = try diags.addErrorWithNotes(1); |
| 799 | 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 | 801 | return error.LinkFailure; |
| 802 | 802 | } |
| 803 | 803 | end += sh_entsize; |
| ... | ... | @@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { |
| 812 | 812 | if (shdr.sh_size % sh_entsize != 0) { |
| 813 | 813 | var err = try diags.addErrorWithNotes(1); |
| 814 | 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 | 816 | return error.LinkFailure; |
| 817 | 817 | } |
| 818 | 818 | |
| ... | ... | @@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ |
| 889 | 889 | const res = imsec.findSubsection(@intCast(esym.st_value)) orelse { |
| 890 | 890 | var err = try diags.addErrorWithNotes(2); |
| 891 | 891 | try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); |
| 892 | try err.addNote("for symbol {s}", .{sym.name(elf_file)}); | |
| 893 | try err.addNote("in {}", .{self.fmtPath()}); | |
| 892 | err.addNote("for symbol {s}", .{sym.name(elf_file)}); | |
| 893 | err.addNote("in {}", .{self.fmtPath()}); | |
| 894 | 894 | return error.LinkFailure; |
| 895 | 895 | }; |
| 896 | 896 | |
| ... | ... | @@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ |
| 915 | 915 | const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { |
| 916 | 916 | var err = try diags.addErrorWithNotes(1); |
| 917 | 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 | 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 | 278 | .{ .kind = .code, .ty = .anyerror_type }, |
| 279 | 279 | metadata.text_symbol_index, |
| 280 | 280 | ) catch |err| return switch (err) { |
| 281 | error.CodegenFail => error.FlushFailure, | |
| 282 | else => |e| e, | |
| 281 | error.CodegenFail => error.LinkFailure, | |
| 282 | else => |e| return e, | |
| 283 | 283 | }; |
| 284 | 284 | if (metadata.rodata_state != .unused) self.updateLazySymbol( |
| 285 | 285 | elf_file, |
| ... | ... | @@ -287,8 +287,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { |
| 287 | 287 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 288 | 288 | metadata.rodata_symbol_index, |
| 289 | 289 | ) catch |err| return switch (err) { |
| 290 | error.CodegenFail => error.FlushFailure, | |
| 291 | else => |e| e, | |
| 290 | error.CodegenFail => error.LinkFailure, | |
| 291 | else => |e| return e, | |
| 292 | 292 | }; |
| 293 | 293 | } |
| 294 | 294 | for (self.lazy_syms.values()) |*metadata| { |
| ... | ... | @@ -933,6 +933,7 @@ pub fn getNavVAddr( |
| 933 | 933 | const this_sym = self.symbol(this_sym_index); |
| 934 | 934 | const vaddr = this_sym.address(.{}, elf_file); |
| 935 | 935 | switch (reloc_info.parent) { |
| 936 | .none => unreachable, | |
| 936 | 937 | .atom_index => |atom_index| { |
| 937 | 938 | const parent_atom = self.symbol(atom_index).atom(elf_file).?; |
| 938 | 939 | const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); |
| ... | ... | @@ -965,6 +966,7 @@ pub fn getUavVAddr( |
| 965 | 966 | const sym = self.symbol(sym_index); |
| 966 | 967 | const vaddr = sym.address(.{}, elf_file); |
| 967 | 968 | switch (reloc_info.parent) { |
| 969 | .none => unreachable, | |
| 968 | 970 | .atom_index => |atom_index| { |
| 969 | 971 | const parent_atom = self.symbol(atom_index).atom(elf_file).?; |
| 970 | 972 | const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); |
| ... | ... | @@ -1261,7 +1263,7 @@ fn updateNavCode( |
| 1261 | 1263 | shdr_index: u32, |
| 1262 | 1264 | code: []const u8, |
| 1263 | 1265 | stt_bits: u8, |
| 1264 | ) !void { | |
| 1266 | ) link.File.UpdateNavError!void { | |
| 1265 | 1267 | const zcu = pt.zcu; |
| 1266 | 1268 | const gpa = zcu.gpa; |
| 1267 | 1269 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -1298,7 +1300,9 @@ fn updateNavCode( |
| 1298 | 1300 | const capacity = atom_ptr.capacity(elf_file); |
| 1299 | 1301 | const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value)); |
| 1300 | 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 | 1306 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value }); |
| 1303 | 1307 | if (old_vaddr != atom_ptr.value) { |
| 1304 | 1308 | sym.value = 0; |
| ... | ... | @@ -1308,7 +1312,9 @@ fn updateNavCode( |
| 1308 | 1312 | // TODO shrink section size |
| 1309 | 1313 | } |
| 1310 | 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 | 1318 | errdefer self.freeNavMetadata(elf_file, sym_index); |
| 1313 | 1319 | sym.value = 0; |
| 1314 | 1320 | esym.st_value = 0; |
| ... | ... | @@ -1333,14 +1339,15 @@ fn updateNavCode( |
| 1333 | 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 | 1346 | const shdr = elf_file.sections.items(.shdr)[shdr_index]; |
| 1341 | 1347 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 1342 | 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 | 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 | 1360 | sym_index: Symbol.Index, |
| 1354 | 1361 | shndx: u32, |
| 1355 | 1362 | code: []const u8, |
| 1356 | ) !void { | |
| 1363 | ) link.File.UpdateNavError!void { | |
| 1357 | 1364 | const zcu = pt.zcu; |
| 1358 | 1365 | const ip = &zcu.intern_pool; |
| 1359 | 1366 | const gpa = zcu.gpa; |
| ... | ... | @@ -1383,7 +1390,8 @@ fn updateTlv( |
| 1383 | 1390 | const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index); |
| 1384 | 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 | 1395 | sym.value = 0; |
| 1388 | 1396 | esym.st_value = 0; |
| 1389 | 1397 | |
| ... | ... | @@ -1392,7 +1400,8 @@ fn updateTlv( |
| 1392 | 1400 | const shdr = elf_file.sections.items(.shdr)[shndx]; |
| 1393 | 1401 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 1394 | 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 | 1405 | log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{ |
| 1397 | 1406 | atom_ptr.name(elf_file), |
| 1398 | 1407 | file_offset, |
| ... | ... | @@ -1408,7 +1417,7 @@ pub fn updateFunc( |
| 1408 | 1417 | func_index: InternPool.Index, |
| 1409 | 1418 | air: Air, |
| 1410 | 1419 | liveness: Liveness, |
| 1411 | ) !void { | |
| 1420 | ) link.File.UpdateNavError!void { | |
| 1412 | 1421 | const tracy = trace(@src()); |
| 1413 | 1422 | defer tracy.end(); |
| 1414 | 1423 | |
| ... | ... | @@ -1422,13 +1431,13 @@ pub fn updateFunc( |
| 1422 | 1431 | const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav); |
| 1423 | 1432 | self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self); |
| 1424 | 1433 | |
| 1425 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1426 | defer code_buffer.deinit(); | |
| 1434 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1435 | defer code_buffer.deinit(gpa); | |
| 1427 | 1436 | |
| 1428 | 1437 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; |
| 1429 | 1438 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 1430 | 1439 | |
| 1431 | const res = try codegen.generateFunction( | |
| 1440 | try codegen.generateFunction( | |
| 1432 | 1441 | &elf_file.base, |
| 1433 | 1442 | pt, |
| 1434 | 1443 | zcu.navSrcLoc(func.owner_nav), |
| ... | ... | @@ -1438,14 +1447,7 @@ pub fn updateFunc( |
| 1438 | 1447 | &code_buffer, |
| 1439 | 1448 | if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none, |
| 1440 | 1449 | ); |
| 1441 | ||
| 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 | }; | |
| 1450 | const code = code_buffer.items; | |
| 1449 | 1451 | |
| 1450 | 1452 | const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code); |
| 1451 | 1453 | log.debug("setting shdr({x},{s}) for {}", .{ |
| ... | ... | @@ -1463,7 +1465,8 @@ pub fn updateFunc( |
| 1463 | 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 | 1471 | // Exports will be updated by `Zcu.processExports` after the update. |
| 1469 | 1472 | |
| ... | ... | @@ -1511,7 +1514,8 @@ pub fn updateFunc( |
| 1511 | 1514 | target_sym.flags.has_trampoline = true; |
| 1512 | 1515 | } |
| 1513 | 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 | 1551 | if (self.dwarf) |*dwarf| dwarf: { |
| 1548 | 1552 | var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; |
| 1549 | 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 | 1560 | return; |
| 1553 | 1561 | }, |
| ... | ... | @@ -1558,13 +1566,13 @@ pub fn updateNav( |
| 1558 | 1566 | const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index); |
| 1559 | 1567 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); |
| 1560 | 1568 | |
| 1561 | var code_buffer = std.ArrayList(u8).init(zcu.gpa); | |
| 1562 | defer code_buffer.deinit(); | |
| 1569 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1570 | defer code_buffer.deinit(zcu.gpa); | |
| 1563 | 1571 | |
| 1564 | 1572 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null; |
| 1565 | 1573 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 1566 | 1574 | |
| 1567 | const res = try codegen.generateSymbol( | |
| 1575 | try codegen.generateSymbol( | |
| 1568 | 1576 | &elf_file.base, |
| 1569 | 1577 | pt, |
| 1570 | 1578 | zcu.navSrcLoc(nav_index), |
| ... | ... | @@ -1572,14 +1580,7 @@ pub fn updateNav( |
| 1572 | 1580 | &code_buffer, |
| 1573 | 1581 | .{ .atom_index = sym_index }, |
| 1574 | 1582 | ); |
| 1575 | ||
| 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 | const code = code_buffer.items; | |
| 1583 | 1584 | |
| 1584 | 1585 | const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code); |
| 1585 | 1586 | log.debug("setting shdr({x},{s}) for {}", .{ |
| ... | ... | @@ -1592,7 +1593,11 @@ pub fn updateNav( |
| 1592 | 1593 | else |
| 1593 | 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 | 1601 | } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); |
| 1597 | 1602 | |
| 1598 | 1603 | // Exports will be updated by `Zcu.processExports` after the update. |
| ... | ... | @@ -1602,7 +1607,7 @@ pub fn updateContainerType( |
| 1602 | 1607 | self: *ZigObject, |
| 1603 | 1608 | pt: Zcu.PerThread, |
| 1604 | 1609 | ty: InternPool.Index, |
| 1605 | ) link.File.UpdateNavError!void { | |
| 1610 | ) !void { | |
| 1606 | 1611 | const tracy = trace(@src()); |
| 1607 | 1612 | defer tracy.end(); |
| 1608 | 1613 | |
| ... | ... | @@ -1620,8 +1625,8 @@ fn updateLazySymbol( |
| 1620 | 1625 | const gpa = zcu.gpa; |
| 1621 | 1626 | |
| 1622 | 1627 | var required_alignment: InternPool.Alignment = .none; |
| 1623 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1624 | defer code_buffer.deinit(); | |
| 1628 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1629 | defer code_buffer.deinit(gpa); | |
| 1625 | 1630 | |
| 1626 | 1631 | const name_str_index = blk: { |
| 1627 | 1632 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ |
| ... | ... | @@ -1633,7 +1638,7 @@ fn updateLazySymbol( |
| 1633 | 1638 | }; |
| 1634 | 1639 | |
| 1635 | 1640 | const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1636 | const res = try codegen.generateLazySymbol( | |
| 1641 | try codegen.generateLazySymbol( | |
| 1637 | 1642 | &elf_file.base, |
| 1638 | 1643 | pt, |
| 1639 | 1644 | src, |
| ... | ... | @@ -1643,13 +1648,7 @@ fn updateLazySymbol( |
| 1643 | 1648 | .none, |
| 1644 | 1649 | .{ .atom_index = symbol_index }, |
| 1645 | 1650 | ); |
| 1646 | const code = switch (res) { | |
| 1647 | .ok => code_buffer.items, | |
| 1648 | .fail => |em| { | |
| 1649 | log.err("{s}", .{em.msg}); | |
| 1650 | return error.CodegenFail; | |
| 1651 | }, | |
| 1652 | }; | |
| 1651 | const code = code_buffer.items; | |
| 1653 | 1652 | |
| 1654 | 1653 | const output_section_index = switch (sym.kind) { |
| 1655 | 1654 | .code => if (self.text_index) |sym_index| |
| ... | ... | @@ -1696,7 +1695,7 @@ fn updateLazySymbol( |
| 1696 | 1695 | local_sym.value = 0; |
| 1697 | 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 | 1701 | const LowerConstResult = union(enum) { |
| ... | ... | @@ -1716,13 +1715,13 @@ fn lowerConst( |
| 1716 | 1715 | ) !LowerConstResult { |
| 1717 | 1716 | const gpa = pt.zcu.gpa; |
| 1718 | 1717 | |
| 1719 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1720 | defer code_buffer.deinit(); | |
| 1718 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1719 | defer code_buffer.deinit(gpa); | |
| 1721 | 1720 | |
| 1722 | 1721 | const name_off = try self.addString(gpa, name); |
| 1723 | 1722 | const sym_index = try self.newSymbolWithAtom(gpa, name_off); |
| 1724 | 1723 | |
| 1725 | const res = try codegen.generateSymbol( | |
| 1724 | try codegen.generateSymbol( | |
| 1726 | 1725 | &elf_file.base, |
| 1727 | 1726 | pt, |
| 1728 | 1727 | src_loc, |
| ... | ... | @@ -1730,10 +1729,7 @@ fn lowerConst( |
| 1730 | 1729 | &code_buffer, |
| 1731 | 1730 | .{ .atom_index = sym_index }, |
| 1732 | 1731 | ); |
| 1733 | const code = switch (res) { | |
| 1734 | .ok => code_buffer.items, | |
| 1735 | .fail => |em| return .{ .fail = em }, | |
| 1736 | }; | |
| 1732 | const code = code_buffer.items; | |
| 1737 | 1733 | |
| 1738 | 1734 | const local_sym = self.symbol(sym_index); |
| 1739 | 1735 | const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index]; |
| ... | ... | @@ -1748,7 +1744,7 @@ fn lowerConst( |
| 1748 | 1744 | try self.allocateAtom(atom_ptr, true, elf_file); |
| 1749 | 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 | 1749 | return .{ .ok = sym_index }; |
| 1754 | 1750 | } |
| ... | ... | @@ -1758,7 +1754,7 @@ pub fn updateExports( |
| 1758 | 1754 | elf_file: *Elf, |
| 1759 | 1755 | pt: Zcu.PerThread, |
| 1760 | 1756 | exported: Zcu.Exported, |
| 1761 | export_indices: []const u32, | |
| 1757 | export_indices: []const Zcu.Export.Index, | |
| 1762 | 1758 | ) link.File.UpdateExportsError!void { |
| 1763 | 1759 | const tracy = trace(@src()); |
| 1764 | 1760 | defer tracy.end(); |
| ... | ... | @@ -1771,7 +1767,7 @@ pub fn updateExports( |
| 1771 | 1767 | break :blk self.navs.getPtr(nav).?; |
| 1772 | 1768 | }, |
| 1773 | 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 | 1771 | const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src); |
| 1776 | 1772 | switch (res) { |
| 1777 | 1773 | .mcv => {}, |
| ... | ... | @@ -1792,7 +1788,7 @@ pub fn updateExports( |
| 1792 | 1788 | const esym_shndx = self.symtab.items(.shndx)[esym_index]; |
| 1793 | 1789 | |
| 1794 | 1790 | for (export_indices) |export_idx| { |
| 1795 | const exp = zcu.all_exports.items[export_idx]; | |
| 1791 | const exp = export_idx.ptr(zcu); | |
| 1796 | 1792 | if (exp.opts.section.unwrap()) |section_name| { |
| 1797 | 1793 | if (!section_name.eqlSlice(".text", &zcu.intern_pool)) { |
| 1798 | 1794 | try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); |
| ... | ... | @@ -1849,7 +1845,13 @@ pub fn updateExports( |
| 1849 | 1845 | |
| 1850 | 1846 | pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { |
| 1851 | 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 | 1937 | const shdr = &slice.items(.shdr)[atom_ptr.output_section_index]; |
| 1936 | 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 | |
| 1939 | // In every other case, we need to redo the prev/next links | |
| 1940 | // This only works if this atom is the only atom in the output section. In | |
| 1941 | // every other case, we need to redo the prev/next links. | |
| 1940 | 1942 | if (last_atom_ref.eql(atom_ptr.ref())) last_atom_ref.* = .{}; |
| 1941 | 1943 | |
| 1942 | 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 | 611 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), |
| 612 | 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 | 615 | return error.RelocFailure; |
| 616 | 616 | } |
| 617 | 617 |
src/link/Elf/relocatable.zig+6-6| ... | ... | @@ -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 | 2 | const gpa = comp.gpa; |
| 3 | 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 | 7 | // First, we flush relocatable object file generated with our backends. |
| 8 | 8 | if (elf_file.zigObjectPtr()) |zig_object| { |
| ... | ... | @@ -127,13 +127,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v |
| 127 | 127 | try elf_file.base.file.?.setEndPos(total_size); |
| 128 | 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 | 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 | 138 | // Now, we are ready to resolve the symbols across all input files. |
| 139 | 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 | 179 | try elf_file.writeShdrTable(); |
| 180 | 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 | 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 | 434 | // libc/libSystem dep |
| 435 | 435 | self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { |
| 436 | 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 | 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 | 481 | } |
| 482 | 482 | }; |
| 483 | 483 | |
| 484 | if (diags.hasErrors()) return error.FlushFailure; | |
| 484 | if (diags.hasErrors()) return error.LinkFailure; | |
| 485 | 485 | |
| 486 | 486 | { |
| 487 | 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 | 494 | |
| 495 | 495 | try self.resolveSymbols(); |
| 496 | 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 | 502 | if (self.base.gc_sections) { |
| 500 | 503 | try dead_strip.gcAtoms(self); |
| 501 | 504 | } |
| 502 | 505 | |
| 503 | 506 | self.checkDuplicates() catch |err| switch (err) { |
| 504 | error.HasDuplicates => return error.FlushFailure, | |
| 507 | error.HasDuplicates => return error.LinkFailure, | |
| 505 | 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 | 519 | self.claimUnresolved(); |
| 517 | 520 | |
| 518 | 521 | self.scanRelocs() catch |err| switch (err) { |
| 519 | error.HasUndefinedSymbols => return error.FlushFailure, | |
| 522 | error.HasUndefinedSymbols => return error.LinkFailure, | |
| 520 | 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 | 532 | try self.generateUnwindInfo(); |
| 530 | 533 | |
| 531 | 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 | 539 | self.allocateSegments(); |
| 534 | 540 | self.allocateSyntheticSymbols(); |
| 535 | 541 | |
| ... | ... | @@ -543,7 +549,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 543 | 549 | |
| 544 | 550 | if (self.getZigObject()) |zo| { |
| 545 | 551 | zo.resolveRelocs(self) catch |err| switch (err) { |
| 546 | error.ResolveFailed => return error.FlushFailure, | |
| 552 | error.ResolveFailed => return error.LinkFailure, | |
| 547 | 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 | 557 | |
| 552 | 558 | try self.writeSectionsToFile(); |
| 553 | 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 | 566 | var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: { |
| 557 | 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 | 571 | // where the code signature goes into. |
| 562 | 572 | var codesig = CodeSignature.init(self.getPageSize()); |
| 563 | 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 | 576 | try self.writeCodeSignaturePadding(&codesig); |
| 566 | 577 | break :blk codesig; |
| 567 | 578 | } else null; |
| ... | ... | @@ -573,15 +584,34 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 573 | 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 | 592 | try self.writeHeader(ncmds, sizeofcmds); |
| 578 | try self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()); | |
| 579 | if (self.getDebugSymbols()) |dsym| try dsym.flushModule(self); | |
| 593 | self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) { | |
| 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 | 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 | 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 | 1575 | try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); |
| 1546 | 1576 | |
| 1547 | 1577 | switch (notes) { |
| 1548 | .force_undefined => try err.addNote("referenced with linker flag -u", .{}), | |
| 1549 | .entry => try err.addNote("referenced with linker flag -e", .{}), | |
| 1550 | .dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}), | |
| 1578 | .force_undefined => err.addNote("referenced with linker flag -u", .{}), | |
| 1579 | .entry => err.addNote("referenced with linker flag -e", .{}), | |
| 1580 | .dyld_stub_binder, .objc_msgsend => err.addNote("referenced implicitly", .{}), | |
| 1551 | 1581 | .refs => |refs| { |
| 1552 | 1582 | var inote: usize = 0; |
| 1553 | 1583 | while (inote < @min(refs.items.len, max_notes)) : (inote += 1) { |
| 1554 | 1584 | const ref = refs.items[inote]; |
| 1555 | 1585 | const file = self.getFile(ref.file).?; |
| 1556 | 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 | 1590 | if (refs.items.len > max_notes) { |
| 1561 | 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 | 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 | 2206 | vmaddr = mem.alignForward(u64, vmaddr, alignment); |
| 2177 | 2207 | header.addr = vmaddr; |
| ... | ... | @@ -2327,7 +2357,7 @@ fn allocateLinkeditSegment(self: *MachO) !void { |
| 2327 | 2357 | seg.vmaddr = mem.alignForward(u64, vmaddr, page_size); |
| 2328 | 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 | 2361 | // DYLD_INFO_ONLY |
| 2332 | 2362 | { |
| 2333 | 2363 | const cmd = &self.dyld_info_cmd; |
| ... | ... | @@ -2392,7 +2422,7 @@ fn resizeSections(self: *MachO) !void { |
| 2392 | 2422 | if (header.isZerofill()) continue; |
| 2393 | 2423 | if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible |
| 2394 | 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 | 2426 | try out.resize(self.base.comp.gpa, size); |
| 2397 | 2427 | const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; |
| 2398 | 2428 | @memset(out.items, padding_byte); |
| ... | ... | @@ -2489,7 +2519,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void { |
| 2489 | 2519 | |
| 2490 | 2520 | const doWork = struct { |
| 2491 | 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 | 2523 | const size = th.size(); |
| 2494 | 2524 | var stream = std.io.fixedBufferStream(buffer[off..][0..size]); |
| 2495 | 2525 | try th.write(macho_file, stream.writer()); |
| ... | ... | @@ -2601,7 +2631,7 @@ fn writeSectionsToFile(self: *MachO) !void { |
| 2601 | 2631 | |
| 2602 | 2632 | const slice = self.sections.slice(); |
| 2603 | 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 | 2674 | try self.lazy_bind_section.write(writer); |
| 2645 | 2675 | try stream.seekTo(cmd.export_off - base_off); |
| 2646 | 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 | 2680 | pub fn writeDataInCode(self: *MachO) !void { |
| ... | ... | @@ -2655,7 +2685,7 @@ pub fn writeDataInCode(self: *MachO) !void { |
| 2655 | 2685 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size()); |
| 2656 | 2686 | defer buffer.deinit(); |
| 2657 | 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 | 2691 | fn writeIndsymtab(self: *MachO) !void { |
| ... | ... | @@ -2667,15 +2697,15 @@ fn writeIndsymtab(self: *MachO) !void { |
| 2667 | 2697 | var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); |
| 2668 | 2698 | defer buffer.deinit(); |
| 2669 | 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 | 2703 | pub fn writeSymtabToFile(self: *MachO) !void { |
| 2674 | 2704 | const tracy = trace(@src()); |
| 2675 | 2705 | defer tracy.end(); |
| 2676 | 2706 | const cmd = self.symtab_cmd; |
| 2677 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); | |
| 2678 | try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff); | |
| 2707 | try self.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); | |
| 2708 | try self.pwriteAll(self.strtab.items, cmd.stroff); | |
| 2679 | 2709 | } |
| 2680 | 2710 | |
| 2681 | 2711 | fn writeUnwindInfo(self: *MachO) !void { |
| ... | ... | @@ -2686,20 +2716,20 @@ fn writeUnwindInfo(self: *MachO) !void { |
| 2686 | 2716 | |
| 2687 | 2717 | if (self.eh_frame_sect_index) |index| { |
| 2688 | 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 | 2720 | const buffer = try gpa.alloc(u8, size); |
| 2691 | 2721 | defer gpa.free(buffer); |
| 2692 | 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 | 2726 | if (self.unwind_info_sect_index) |index| { |
| 2697 | 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 | 2729 | const buffer = try gpa.alloc(u8, size); |
| 2700 | 2730 | defer gpa.free(buffer); |
| 2701 | 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 | 2920 | |
| 2891 | 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 | 2925 | return .{ ncmds, buffer.len, uuid_cmd_offset }; |
| 2896 | 2926 | } |
| ... | ... | @@ -2944,7 +2974,7 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void { |
| 2944 | 2974 | |
| 2945 | 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 | 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 | 2984 | } else self.codesig_cmd.dataoff; |
| 2955 | 2985 | try calcUuid(self.base.comp, self.base.file.?, file_size, &self.uuid_cmd.uuid); |
| 2956 | 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 | 2990 | pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| ... | ... | @@ -2968,7 +2998,7 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 2968 | 2998 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); |
| 2969 | 2999 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| 2970 | 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 | 3003 | self.codesig_cmd.dataoff = @as(u32, @intCast(offset)); |
| 2974 | 3004 | self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); |
| ... | ... | @@ -2995,10 +3025,16 @@ pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { |
| 2995 | 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 | 3038 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 3003 | 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 | 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 | 3046 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 3011 | 3047 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 3012 | 3048 | } |
| ... | ... | @@ -3023,7 +3059,7 @@ pub fn updateExports( |
| 3023 | 3059 | self: *MachO, |
| 3024 | 3060 | pt: Zcu.PerThread, |
| 3025 | 3061 | exported: Zcu.Exported, |
| 3026 | export_indices: []const u32, | |
| 3062 | export_indices: []const Zcu.Export.Index, | |
| 3027 | 3063 | ) link.File.UpdateExportsError!void { |
| 3028 | 3064 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 3029 | 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 | 3235 | const gpa = self.base.comp.gpa; |
| 3200 | 3236 | try self.copyRangeAll(old_offset, new_offset, size); |
| 3201 | 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 | 3239 | defer gpa.free(zeroes); |
| 3204 | 3240 | @memset(zeroes, 0); |
| 3205 | 3241 | try self.base.file.?.pwriteAll(zeroes, old_offset); |
| ... | ... | @@ -3306,10 +3342,9 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3306 | 3342 | const allocSect = struct { |
| 3307 | 3343 | fn allocSect(macho_file: *MachO, sect_id: u8, size: u64) !void { |
| 3308 | 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 | 3346 | if (!sect.isZerofill()) { |
| 3311 | sect.offset = math.cast(u32, try macho_file.findFreeSpace(size, alignment)) orelse | |
| 3312 | return error.Overflow; | |
| 3347 | sect.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(size, alignment)); | |
| 3313 | 3348 | } |
| 3314 | 3349 | sect.addr = macho_file.findFreeSpaceVirtual(size, alignment); |
| 3315 | 3350 | sect.size = size; |
| ... | ... | @@ -3441,8 +3476,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo |
| 3441 | 3476 | seg_id, |
| 3442 | 3477 | seg.segName(), |
| 3443 | 3478 | }); |
| 3444 | try 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", .{}); | |
| 3479 | err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{}); | |
| 3480 | err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); | |
| 3446 | 3481 | } |
| 3447 | 3482 | |
| 3448 | 3483 | seg.vmsize = needed_size; |
| ... | ... | @@ -3744,7 +3779,7 @@ pub fn reportParseError2( |
| 3744 | 3779 | const diags = &self.base.comp.link_diags; |
| 3745 | 3780 | var err = try diags.addErrorWithNotes(1); |
| 3746 | 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 | 3785 | fn reportMissingDependencyError( |
| ... | ... | @@ -3758,10 +3793,10 @@ fn reportMissingDependencyError( |
| 3758 | 3793 | const diags = &self.base.comp.link_diags; |
| 3759 | 3794 | var err = try diags.addErrorWithNotes(2 + checked_paths.len); |
| 3760 | 3795 | try err.addMsg(format, args); |
| 3761 | try err.addNote("while resolving {s}", .{path}); | |
| 3762 | try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | |
| 3796 | err.addNote("while resolving {s}", .{path}); | |
| 3797 | err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | |
| 3763 | 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 | 3810 | const diags = &self.base.comp.link_diags; |
| 3776 | 3811 | var err = try diags.addErrorWithNotes(2); |
| 3777 | 3812 | try err.addMsg(format, args); |
| 3778 | try err.addNote("while parsing {s}", .{path}); | |
| 3779 | try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | |
| 3813 | err.addNote("while parsing {s}", .{path}); | |
| 3814 | err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); | |
| 3780 | 3815 | } |
| 3781 | 3816 | |
| 3782 | 3817 | fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { |
| ... | ... | @@ -3806,17 +3841,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { |
| 3806 | 3841 | |
| 3807 | 3842 | var err = try diags.addErrorWithNotes(nnotes + 1); |
| 3808 | 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 | 3846 | var inote: usize = 0; |
| 3812 | 3847 | while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { |
| 3813 | 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 | 3852 | if (notes.items.len > max_notes) { |
| 3818 | 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 | 3857 | return error.HasDuplicates; |
| ... | ... | @@ -5310,6 +5345,40 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { |
| 5310 | 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 | 5382 | /// Branch instruction has 26 bits immediate but is 4 byte aligned. |
| 5314 | 5383 | const jump_bits = @bitSizeOf(i28); |
| 5315 | 5384 | const max_distance = (1 << (jump_bits - 1)); |
src/link/MachO/Atom.zig+7-7| ... | ... | @@ -909,8 +909,8 @@ const x86_64 = struct { |
| 909 | 909 | rel.offset, |
| 910 | 910 | rel.fmtPretty(.x86_64), |
| 911 | 911 | }); |
| 912 | try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); | |
| 913 | try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); | |
| 912 | err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); | |
| 913 | err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); | |
| 914 | 914 | return error.RelaxFailUnexpectedInstruction; |
| 915 | 915 | }, |
| 916 | 916 | } |
| ... | ... | @@ -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 | 975 | const tracy = trace(@src()); |
| 976 | 976 | defer tracy.end(); |
| 977 | 977 | |
| ... | ... | @@ -983,15 +983,15 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r |
| 983 | 983 | var i: usize = 0; |
| 984 | 984 | for (relocs) |rel| { |
| 985 | 985 | defer i += 1; |
| 986 | const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; | |
| 987 | const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; | |
| 986 | const rel_offset = try macho_file.cast(usize, rel.offset - self.off); | |
| 987 | const r_address: i32 = try macho_file.cast(i32, self.value + rel_offset); | |
| 988 | 988 | assert(r_address >= 0); |
| 989 | 989 | const r_symbolnum = r_symbolnum: { |
| 990 | 990 | const r_symbolnum: u32 = switch (rel.tag) { |
| 991 | 991 | .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1, |
| 992 | 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 | 996 | const r_extern = rel.tag == .@"extern"; |
| 997 | 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 | 1027 | } else if (addend > 0) { |
| 1028 | 1028 | buffer[i] = .{ |
| 1029 | 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 | 1031 | .r_pcrel = 0, |
| 1032 | 1032 | .r_length = 2, |
| 1033 | 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 | 414 | const rel = relocs[0]; |
| 415 | 415 | assert(rel.tag == .@"extern"); |
| 416 | 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 | 418 | try buffer.ensureUnusedCapacity(target_size); |
| 419 | 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 | 422 | const res = try lp.insert(gpa, header.type(), buffer.items); |
| 422 | 423 | buffer.clearRetainingCapacity(); |
| 423 | 424 | if (!res.found_existing) { |
| ... | ... | @@ -607,10 +608,11 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void { |
| 607 | 608 | if (!atom.isAlive()) continue; |
| 608 | 609 | const sect = atom.getInputSection(macho_file); |
| 609 | 610 | if (sect.isZerofill()) continue; |
| 610 | const off = std.math.cast(usize, atom.value) orelse return error.Overflow; | |
| 611 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | |
| 611 | const off = try macho_file.cast(usize, atom.value); | |
| 612 | const size = try macho_file.cast(usize, atom.size); | |
| 612 | 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 | 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 | 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 | 650 | const slice = self.sections.slice(); |
| 649 | 651 | assert(index < slice.items(.header).len); |
| 650 | 652 | const sect = slice.items(.header)[index]; |
| 651 | 653 | const extra = slice.items(.extra)[index]; |
| 652 | 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 | 656 | return self.objc_methnames.items[sect.offset..][0..size]; |
| 655 | 657 | } else if (extra.is_objc_selref) |
| 656 | 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 | 582 | ); |
| 583 | 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 | 587 | for (0..num_ptrs) |i| { |
| 588 | 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 | 650 | |
| 651 | 651 | for (subs.items) |sub| { |
| 652 | 652 | const atom = self.getAtom(sub.atom).?; |
| 653 | const atom_off = math.cast(usize, atom.off) orelse return error.Overflow; | |
| 654 | const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; | |
| 653 | const atom_off = try macho_file.cast(usize, atom.off); | |
| 654 | const atom_size = try macho_file.cast(usize, atom.size); | |
| 655 | 655 | const atom_data = data[atom_off..][0..atom_size]; |
| 656 | 656 | const res = try lp.insert(gpa, header.type(), atom_data); |
| 657 | 657 | if (!res.found_existing) { |
| ... | ... | @@ -674,8 +674,8 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO |
| 674 | 674 | .local => rel.getTargetAtom(atom.*, macho_file), |
| 675 | 675 | .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?, |
| 676 | 676 | }; |
| 677 | const addend = math.cast(u32, rel.addend) orelse return error.Overflow; | |
| 678 | const target_size = math.cast(usize, target.size) orelse return error.Overflow; | |
| 677 | const addend = try macho_file.cast(u32, rel.addend); | |
| 678 | const target_size = try macho_file.cast(usize, target.size); | |
| 679 | 679 | try buffer.ensureUnusedCapacity(target_size); |
| 680 | 680 | buffer.resize(target_size) catch unreachable; |
| 681 | 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 | 683 | gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect)); |
| 684 | 684 | } |
| 685 | 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 | 687 | @memcpy(buffer.items, data[target_off..][0..target_size]); |
| 688 | 688 | const res = try lp.insert(gpa, header.type(), buffer.items[addend..]); |
| 689 | 689 | buffer.clearRetainingCapacity(); |
| ... | ... | @@ -1033,7 +1033,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi |
| 1033 | 1033 | const sect = slice.items(.header)[sect_id]; |
| 1034 | 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 | 1037 | try self.eh_frame_data.resize(allocator, size); |
| 1038 | 1038 | const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset); |
| 1039 | 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 | 1696 | |
| 1697 | 1697 | pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { |
| 1698 | 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 | 1700 | const basename = std.fs.path.basename(self.path.sub_path); |
| 1701 | 1701 | try Archive.writeHeader(basename, size, ar_format, writer); |
| 1702 | 1702 | // Data |
| ... | ... | @@ -1826,7 +1826,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { |
| 1826 | 1826 | |
| 1827 | 1827 | for (headers, 0..) |header, n_sect| { |
| 1828 | 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 | 1830 | const data = try gpa.alloc(u8, size); |
| 1831 | 1831 | const amt = try file.preadAll(data, header.offset + self.offset); |
| 1832 | 1832 | if (amt != data.len) return error.InputOutput; |
| ... | ... | @@ -1837,9 +1837,9 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { |
| 1837 | 1837 | if (!atom.isAlive()) continue; |
| 1838 | 1838 | const sect = atom.getInputSection(macho_file); |
| 1839 | 1839 | if (sect.isZerofill()) continue; |
| 1840 | const value = math.cast(usize, atom.value) orelse return error.Overflow; | |
| 1841 | const off = math.cast(usize, atom.off) orelse return error.Overflow; | |
| 1842 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | |
| 1840 | const value = try macho_file.cast(usize, atom.value); | |
| 1841 | const off = try macho_file.cast(usize, atom.off); | |
| 1842 | const size = try macho_file.cast(usize, atom.size); | |
| 1843 | 1843 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 1844 | 1844 | const data = sections_data[atom.n_sect]; |
| 1845 | 1845 | @memcpy(buffer[value..][0..size], data[off..][0..size]); |
| ... | ... | @@ -1865,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1865 | 1865 | |
| 1866 | 1866 | for (headers, 0..) |header, n_sect| { |
| 1867 | 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 | 1869 | const data = try gpa.alloc(u8, size); |
| 1870 | 1870 | const amt = try file.preadAll(data, header.offset + self.offset); |
| 1871 | 1871 | if (amt != data.len) return error.InputOutput; |
| ... | ... | @@ -1876,9 +1876,9 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1876 | 1876 | if (!atom.isAlive()) continue; |
| 1877 | 1877 | const sect = atom.getInputSection(macho_file); |
| 1878 | 1878 | if (sect.isZerofill()) continue; |
| 1879 | const value = math.cast(usize, atom.value) orelse return error.Overflow; | |
| 1880 | const off = math.cast(usize, atom.off) orelse return error.Overflow; | |
| 1881 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | |
| 1879 | const value = try macho_file.cast(usize, atom.value); | |
| 1880 | const off = try macho_file.cast(usize, atom.off); | |
| 1881 | const size = try macho_file.cast(usize, atom.size); | |
| 1882 | 1882 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 1883 | 1883 | const data = sections_data[atom.n_sect]; |
| 1884 | 1884 | @memcpy(buffer[value..][0..size], data[off..][0..size]); |
| ... | ... | @@ -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 | 1927 | pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1913 | 1928 | const tracy = trace(@src()); |
| 1914 | 1929 | defer tracy.end(); |
| 1915 | 1930 | |
| 1916 | 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 | 1933 | const nsect = macho_file.unwind_info_sect_index.?; |
| 1936 | 1934 | const buffer = macho_file.sections.items(.out)[nsect].items; |
| 1937 | 1935 | const relocs = macho_file.sections.items(.relocs)[nsect].items; |
| ... | ... | @@ -1967,7 +1965,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { |
| 1967 | 1965 | |
| 1968 | 1966 | // Personality function |
| 1969 | 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 | 1969 | var reloc = try addReloc(offset + 16, cpu_arch); |
| 1972 | 1970 | reloc.r_symbolnum = r_symbolnum; |
| 1973 | 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 | 290 | /// We need this so that we can write to an archive. |
| 291 | 291 | /// TODO implement writing ZigObject data directly to a buffer instead. |
| 292 | 292 | pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void { |
| 293 | const diags = &macho_file.base.comp.link_diags; | |
| 293 | 294 | // Size of the output object file is always the offset + size of the strtab |
| 294 | 295 | const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize; |
| 295 | 296 | const gpa = macho_file.base.comp.gpa; |
| 296 | 297 | try self.data.resize(gpa, size); |
| 297 | const amt = try macho_file.base.file.?.preadAll(self.data.items, 0); | |
| 298 | if (amt != size) return error.InputOutput; | |
| 298 | const amt = macho_file.base.file.?.preadAll(self.data.items, 0) catch |err| | |
| 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 | 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 | 379 | if (atom.getRelocs(macho_file).len == 0) continue; |
| 377 | 380 | // TODO: we will resolve and write ZigObject's TLS data twice: |
| 378 | 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 | 383 | const code = try gpa.alloc(u8, atom_size); |
| 381 | 384 | defer gpa.free(code); |
| 382 | 385 | self.getAtomData(macho_file, atom.*, code) catch |err| { |
| ... | ... | @@ -400,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { |
| 400 | 403 | has_error = true; |
| 401 | 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 | 409 | if (has_error) return error.ResolveFailed; |
| ... | ... | @@ -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 | 426 | const gpa = macho_file.base.comp.gpa; |
| 424 | 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 | 435 | if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; |
| 433 | 436 | if (atom.getRelocs(macho_file).len == 0) continue; |
| 434 | 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 | 439 | const code = try gpa.alloc(u8, atom_size); |
| 437 | 440 | defer gpa.free(code); |
| 438 | 441 | self.getAtomData(macho_file, atom.*, code) catch |err| |
| 439 | 442 | return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) }); |
| 440 | 443 | const file_offset = header.offset + atom.value; |
| 441 | 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 | 460 | if (sect.isZerofill()) continue; |
| 458 | 461 | if (macho_file.isZigSection(atom.out_n_sect)) continue; |
| 459 | 462 | if (atom.getRelocs(macho_file).len == 0) continue; |
| 460 | const off = std.math.cast(usize, atom.value) orelse return error.Overflow; | |
| 461 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | |
| 463 | const off = try macho_file.cast(usize, atom.value); | |
| 464 | const size = try macho_file.cast(usize, atom.size); | |
| 462 | 465 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 463 | 466 | try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); |
| 464 | 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 | 483 | const sect = atom.getInputSection(macho_file); |
| 481 | 484 | if (sect.isZerofill()) continue; |
| 482 | 485 | if (macho_file.isZigSection(atom.out_n_sect)) continue; |
| 483 | const off = std.math.cast(usize, atom.value) orelse return error.Overflow; | |
| 484 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | |
| 486 | const off = try macho_file.cast(usize, atom.value); | |
| 487 | const size = try macho_file.cast(usize, atom.size); | |
| 485 | 488 | const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; |
| 486 | 489 | try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); |
| 487 | 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 | 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 | 555 | // Handle any lazy symbols that were emitted by incremental compilation. |
| 551 | 556 | if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| { |
| 552 | 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 | 564 | pt, |
| 560 | 565 | .{ .kind = .code, .ty = .anyerror_type }, |
| 561 | 566 | metadata.text_symbol_index, |
| 562 | ) catch |err| return switch (err) { | |
| 563 | error.CodegenFail => error.FlushFailure, | |
| 564 | else => |e| e, | |
| 567 | ) catch |err| switch (err) { | |
| 568 | error.OutOfMemory => return error.OutOfMemory, | |
| 569 | error.LinkFailure => return error.LinkFailure, | |
| 570 | else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}), | |
| 565 | 571 | }; |
| 566 | 572 | if (metadata.const_state != .unused) self.updateLazySymbol( |
| 567 | 573 | macho_file, |
| 568 | 574 | pt, |
| 569 | 575 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 570 | 576 | metadata.const_symbol_index, |
| 571 | ) catch |err| return switch (err) { | |
| 572 | error.CodegenFail => error.FlushFailure, | |
| 573 | else => |e| e, | |
| 577 | ) catch |err| switch (err) { | |
| 578 | error.OutOfMemory => return error.OutOfMemory, | |
| 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 | 583 | for (self.lazy_syms.values()) |*metadata| { |
| ... | ... | @@ -581,7 +588,10 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) |
| 581 | 588 | if (self.dwarf) |*dwarf| { |
| 582 | 589 | const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid); |
| 583 | 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 | 596 | self.debug_abbrev_dirty = false; |
| 587 | 597 | self.debug_aranges_dirty = false; |
| ... | ... | @@ -616,6 +626,7 @@ pub fn getNavVAddr( |
| 616 | 626 | const sym = self.symbols.items[sym_index]; |
| 617 | 627 | const vaddr = sym.getAddress(.{}, macho_file); |
| 618 | 628 | switch (reloc_info.parent) { |
| 629 | .none => unreachable, | |
| 619 | 630 | .atom_index => |atom_index| { |
| 620 | 631 | const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?; |
| 621 | 632 | try parent_atom.addReloc(macho_file, .{ |
| ... | ... | @@ -655,6 +666,7 @@ pub fn getUavVAddr( |
| 655 | 666 | const sym = self.symbols.items[sym_index]; |
| 656 | 667 | const vaddr = sym.getAddress(.{}, macho_file); |
| 657 | 668 | switch (reloc_info.parent) { |
| 669 | .none => unreachable, | |
| 658 | 670 | .atom_index => |atom_index| { |
| 659 | 671 | const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?; |
| 660 | 672 | try parent_atom.addReloc(macho_file, .{ |
| ... | ... | @@ -766,7 +778,7 @@ pub fn updateFunc( |
| 766 | 778 | func_index: InternPool.Index, |
| 767 | 779 | air: Air, |
| 768 | 780 | liveness: Liveness, |
| 769 | ) !void { | |
| 781 | ) link.File.UpdateNavError!void { | |
| 770 | 782 | const tracy = trace(@src()); |
| 771 | 783 | defer tracy.end(); |
| 772 | 784 | |
| ... | ... | @@ -777,13 +789,13 @@ pub fn updateFunc( |
| 777 | 789 | const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav); |
| 778 | 790 | self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); |
| 779 | 791 | |
| 780 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 781 | defer code_buffer.deinit(); | |
| 792 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 793 | defer code_buffer.deinit(gpa); | |
| 782 | 794 | |
| 783 | 795 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null; |
| 784 | 796 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 785 | 797 | |
| 786 | const res = try codegen.generateFunction( | |
| 798 | try codegen.generateFunction( | |
| 787 | 799 | &macho_file.base, |
| 788 | 800 | pt, |
| 789 | 801 | zcu.navSrcLoc(func.owner_nav), |
| ... | ... | @@ -793,14 +805,7 @@ pub fn updateFunc( |
| 793 | 805 | &code_buffer, |
| 794 | 806 | if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, |
| 795 | 807 | ); |
| 796 | ||
| 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 | }; | |
| 808 | const code = code_buffer.items; | |
| 804 | 809 | |
| 805 | 810 | const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code); |
| 806 | 811 | const old_rva, const old_alignment = blk: { |
| ... | ... | @@ -813,7 +818,8 @@ pub fn updateFunc( |
| 813 | 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 | 824 | // Exports will be updated by `Zcu.processExports` after the update. |
| 819 | 825 | if (old_rva != new_rva and old_rva > 0) { |
| ... | ... | @@ -850,7 +856,8 @@ pub fn updateFunc( |
| 850 | 856 | } |
| 851 | 857 | const target_sym = self.symbols.items[sym_index]; |
| 852 | 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 | 890 | if (self.dwarf) |*dwarf| dwarf: { |
| 884 | 891 | var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf; |
| 885 | 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 | 899 | return; |
| 889 | 900 | }, |
| ... | ... | @@ -894,13 +905,13 @@ pub fn updateNav( |
| 894 | 905 | const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index); |
| 895 | 906 | self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); |
| 896 | 907 | |
| 897 | var code_buffer = std.ArrayList(u8).init(zcu.gpa); | |
| 898 | defer code_buffer.deinit(); | |
| 908 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 909 | defer code_buffer.deinit(zcu.gpa); | |
| 899 | 910 | |
| 900 | 911 | var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null; |
| 901 | 912 | defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit(); |
| 902 | 913 | |
| 903 | const res = try codegen.generateSymbol( | |
| 914 | try codegen.generateSymbol( | |
| 904 | 915 | &macho_file.base, |
| 905 | 916 | pt, |
| 906 | 917 | zcu.navSrcLoc(nav_index), |
| ... | ... | @@ -908,21 +919,19 @@ pub fn updateNav( |
| 908 | 919 | &code_buffer, |
| 909 | 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 | 924 | const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code); |
| 920 | 925 | if (isThreadlocal(macho_file, nav_index)) |
| 921 | 926 | try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code) |
| 922 | 927 | else |
| 923 | 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 | 935 | } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); |
| 927 | 936 | |
| 928 | 937 | // Exports will be updated by `Zcu.processExports` after the update. |
| ... | ... | @@ -936,7 +945,7 @@ fn updateNavCode( |
| 936 | 945 | sym_index: Symbol.Index, |
| 937 | 946 | sect_index: u8, |
| 938 | 947 | code: []const u8, |
| 939 | ) !void { | |
| 948 | ) link.File.UpdateNavError!void { | |
| 940 | 949 | const zcu = pt.zcu; |
| 941 | 950 | const gpa = zcu.gpa; |
| 942 | 951 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -978,7 +987,8 @@ fn updateNavCode( |
| 978 | 987 | const need_realloc = code.len > capacity or !required_alignment.check(atom.value); |
| 979 | 988 | |
| 980 | 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 | 992 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value }); |
| 983 | 993 | if (old_vaddr != atom.value) { |
| 984 | 994 | sym.value = 0; |
| ... | ... | @@ -991,7 +1001,8 @@ fn updateNavCode( |
| 991 | 1001 | sect.size = needed_size; |
| 992 | 1002 | } |
| 993 | 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 | 1006 | errdefer self.freeNavMetadata(macho_file, sym_index); |
| 996 | 1007 | |
| 997 | 1008 | sym.value = 0; |
| ... | ... | @@ -1000,7 +1011,8 @@ fn updateNavCode( |
| 1000 | 1011 | |
| 1001 | 1012 | if (!sect.isZerofill()) { |
| 1002 | 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 | 1210 | ) !LowerConstResult { |
| 1199 | 1211 | const gpa = macho_file.base.comp.gpa; |
| 1200 | 1212 | |
| 1201 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1202 | defer code_buffer.deinit(); | |
| 1213 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1214 | defer code_buffer.deinit(gpa); | |
| 1203 | 1215 | |
| 1204 | 1216 | const name_str = try self.addString(gpa, name); |
| 1205 | 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 | 1220 | &macho_file.base, |
| 1209 | 1221 | pt, |
| 1210 | 1222 | src_loc, |
| ... | ... | @@ -1212,10 +1224,7 @@ fn lowerConst( |
| 1212 | 1224 | &code_buffer, |
| 1213 | 1225 | .{ .atom_index = sym_index }, |
| 1214 | 1226 | ); |
| 1215 | const code = switch (res) { | |
| 1216 | .ok => code_buffer.items, | |
| 1217 | .fail => |em| return .{ .fail = em }, | |
| 1218 | }; | |
| 1227 | const code = code_buffer.items; | |
| 1219 | 1228 | |
| 1220 | 1229 | const sym = &self.symbols.items[sym_index]; |
| 1221 | 1230 | sym.out_n_sect = output_section_index; |
| ... | ... | @@ -1236,7 +1245,7 @@ fn lowerConst( |
| 1236 | 1245 | |
| 1237 | 1246 | const sect = macho_file.sections.items(.header)[output_section_index]; |
| 1238 | 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 | 1250 | return .{ .ok = sym_index }; |
| 1242 | 1251 | } |
| ... | ... | @@ -1246,7 +1255,7 @@ pub fn updateExports( |
| 1246 | 1255 | macho_file: *MachO, |
| 1247 | 1256 | pt: Zcu.PerThread, |
| 1248 | 1257 | exported: Zcu.Exported, |
| 1249 | export_indices: []const u32, | |
| 1258 | export_indices: []const Zcu.Export.Index, | |
| 1250 | 1259 | ) link.File.UpdateExportsError!void { |
| 1251 | 1260 | const tracy = trace(@src()); |
| 1252 | 1261 | defer tracy.end(); |
| ... | ... | @@ -1259,7 +1268,7 @@ pub fn updateExports( |
| 1259 | 1268 | break :blk self.navs.getPtr(nav).?; |
| 1260 | 1269 | }, |
| 1261 | 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 | 1272 | const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src); |
| 1264 | 1273 | switch (res) { |
| 1265 | 1274 | .mcv => {}, |
| ... | ... | @@ -1279,7 +1288,7 @@ pub fn updateExports( |
| 1279 | 1288 | const nlist = self.symtab.items(.nlist)[nlist_idx]; |
| 1280 | 1289 | |
| 1281 | 1290 | for (export_indices) |export_idx| { |
| 1282 | const exp = zcu.all_exports.items[export_idx]; | |
| 1291 | const exp = export_idx.ptr(zcu); | |
| 1283 | 1292 | if (exp.opts.section.unwrap()) |section_name| { |
| 1284 | 1293 | if (!section_name.eqlSlice("__text", &zcu.intern_pool)) { |
| 1285 | 1294 | try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); |
| ... | ... | @@ -1352,8 +1361,8 @@ fn updateLazySymbol( |
| 1352 | 1361 | const gpa = zcu.gpa; |
| 1353 | 1362 | |
| 1354 | 1363 | var required_alignment: Atom.Alignment = .none; |
| 1355 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1356 | defer code_buffer.deinit(); | |
| 1364 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1365 | defer code_buffer.deinit(gpa); | |
| 1357 | 1366 | |
| 1358 | 1367 | const name_str = blk: { |
| 1359 | 1368 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ |
| ... | ... | @@ -1365,7 +1374,7 @@ fn updateLazySymbol( |
| 1365 | 1374 | }; |
| 1366 | 1375 | |
| 1367 | 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 | 1378 | &macho_file.base, |
| 1370 | 1379 | pt, |
| 1371 | 1380 | src, |
| ... | ... | @@ -1375,13 +1384,7 @@ fn updateLazySymbol( |
| 1375 | 1384 | .none, |
| 1376 | 1385 | .{ .atom_index = symbol_index }, |
| 1377 | 1386 | ); |
| 1378 | const code = switch (res) { | |
| 1379 | .ok => code_buffer.items, | |
| 1380 | .fail => |em| { | |
| 1381 | log.err("{s}", .{em.msg}); | |
| 1382 | return error.CodegenFail; | |
| 1383 | }, | |
| 1384 | }; | |
| 1387 | const code = code_buffer.items; | |
| 1385 | 1388 | |
| 1386 | 1389 | const output_section_index = switch (lazy_sym.kind) { |
| 1387 | 1390 | .code => macho_file.zig_text_sect_index.?, |
| ... | ... | @@ -1412,12 +1415,18 @@ fn updateLazySymbol( |
| 1412 | 1415 | |
| 1413 | 1416 | const sect = macho_file.sections.items(.header)[output_section_index]; |
| 1414 | 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 | 1421 | pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { |
| 1419 | 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 | 18 | // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all |
| 19 | 19 | // debug info segments/sections (this is apparently by design by Apple), we copy |
| 20 | 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 | 21 | const path = positionals.items[0].path().?; |
| 24 | const in_file = try path.root_dir.handle.openFile(path.sub_path, .{}); | |
| 25 | const stat = try in_file.stat(); | |
| 26 | const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size); | |
| 27 | if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error | |
| 22 | const in_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| | |
| 23 | return diags.fail("failed to open {}: {s}", .{ path, @errorName(err) }); | |
| 24 | const stat = in_file.stat() catch |err| | |
| 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 | 30 | return; |
| 29 | 31 | } |
| 30 | 32 | |
| ... | ... | @@ -33,14 +35,18 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat |
| 33 | 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 | 40 | try macho_file.parseInputFiles(); |
| 39 | 41 | |
| 40 | if (diags.hasErrors()) return error.FlushFailure; | |
| 42 | if (diags.hasErrors()) return error.LinkFailure; | |
| 41 | 43 | |
| 42 | 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 | 50 | markExports(macho_file); |
| 45 | 51 | claimUnresolved(macho_file); |
| 46 | 52 | try initOutputSections(macho_file); |
| ... | ... | @@ -49,7 +55,10 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat |
| 49 | 55 | try calcSectionSizes(macho_file); |
| 50 | 56 | |
| 51 | 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 | 62 | allocateSegment(macho_file); |
| 54 | 63 | |
| 55 | 64 | if (build_options.enable_logging) { |
| ... | ... | @@ -93,11 +102,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 93 | 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 | 107 | try parseInputFilesAr(macho_file); |
| 99 | 108 | |
| 100 | if (diags.hasErrors()) return error.FlushFailure; | |
| 109 | if (diags.hasErrors()) return error.LinkFailure; | |
| 101 | 110 | |
| 102 | 111 | // First, we flush relocatable object file generated with our backends. |
| 103 | 112 | if (macho_file.getZigObject()) |zo| { |
| ... | ... | @@ -108,7 +117,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 108 | 117 | try macho_file.addAtomsToSections(); |
| 109 | 118 | try calcSectionSizes(macho_file); |
| 110 | 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 | 122 | allocateSegment(macho_file); |
| 113 | 123 | |
| 114 | 124 | if (build_options.enable_logging) { |
| ... | ... | @@ -126,8 +136,6 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 126 | 136 | const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); |
| 127 | 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 | 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 | 160 | |
| 153 | 161 | // Update sizes of contributing objects |
| 154 | 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 | 167 | // Update file offsets of contributing objects |
| ... | ... | @@ -171,7 +180,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 171 | 180 | state.file_off = pos; |
| 172 | 181 | pos += @sizeOf(Archive.ar_hdr); |
| 173 | 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 | 185 | .object => |o| { |
| 177 | 186 | const state = &o.output_ar_state; |
| ... | ... | @@ -179,7 +188,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 179 | 188 | state.file_off = pos; |
| 180 | 189 | pos += @sizeOf(Archive.ar_hdr); |
| 181 | 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 | 193 | else => unreachable, |
| 185 | 194 | } |
| ... | ... | @@ -201,7 +210,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 201 | 210 | try writer.writeAll(Archive.ARMAG); |
| 202 | 211 | |
| 203 | 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 | 218 | // Write object files |
| 207 | 219 | for (files.items) |index| { |
| ... | ... | @@ -210,15 +222,16 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? |
| 210 | 222 | if (padding > 0) { |
| 211 | 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 | 229 | assert(buffer.items.len == total_size); |
| 217 | 230 | |
| 218 | try macho_file.base.file.?.setEndPos(total_size); | |
| 219 | try macho_file.base.file.?.pwriteAll(buffer.items, 0); | |
| 231 | try macho_file.setEndPos(total_size); | |
| 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 | 237 | fn parseInputFilesAr(macho_file: *MachO) !void { |
| ... | ... | @@ -452,11 +465,10 @@ fn allocateSections(macho_file: *MachO) !void { |
| 452 | 465 | for (slice.items(.header)) |*header| { |
| 453 | 466 | const needed_size = header.size; |
| 454 | 467 | header.size = 0; |
| 455 | const alignment = try math.powi(u32, 2, header.@"align"); | |
| 468 | const alignment = try macho_file.alignPow(header.@"align"); | |
| 456 | 469 | if (!header.isZerofill()) { |
| 457 | 470 | if (needed_size > macho_file.allocatedSize(header.offset)) { |
| 458 | header.offset = math.cast(u32, try macho_file.findFreeSpace(needed_size, alignment)) orelse | |
| 459 | return error.Overflow; | |
| 471 | header.offset = try macho_file.cast(u32, try macho_file.findFreeSpace(needed_size, alignment)); | |
| 460 | 472 | } |
| 461 | 473 | } |
| 462 | 474 | if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) { |
| ... | ... | @@ -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 | 588 | const tracy = trace(@src()); |
| 577 | 589 | defer tracy.end(); |
| 578 | 590 | |
| ... | ... | @@ -583,7 +595,7 @@ fn writeSections(macho_file: *MachO) !void { |
| 583 | 595 | for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| { |
| 584 | 596 | if (header.isZerofill()) continue; |
| 585 | 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 | 599 | try out.resize(gpa, size); |
| 588 | 600 | const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; |
| 589 | 601 | @memset(out.items, padding_byte); |
| ... | ... | @@ -662,16 +674,16 @@ fn writeSectionsToFile(macho_file: *MachO) !void { |
| 662 | 674 | |
| 663 | 675 | const slice = macho_file.sections.slice(); |
| 664 | 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); | |
| 666 | try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); | |
| 677 | try macho_file.pwriteAll(out.items, header.offset); | |
| 678 | try macho_file.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); | |
| 667 | 679 | } |
| 668 | 680 | |
| 669 | 681 | try macho_file.writeDataInCode(); |
| 670 | try macho_file.base.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); | |
| 682 | try macho_file.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff); | |
| 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 | 687 | const gpa = macho_file.base.comp.gpa; |
| 676 | 688 | const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); |
| 677 | 689 | const buffer = try gpa.alloc(u8, needed_size); |
| ... | ... | @@ -686,31 +698,45 @@ fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } { |
| 686 | 698 | { |
| 687 | 699 | assert(macho_file.segments.items.len == 1); |
| 688 | 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 | 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 | 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 | 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 | 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 | 723 | ncmds += 1; |
| 702 | 724 | |
| 703 | 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 | 729 | ncmds += 1; |
| 706 | 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 | 734 | ncmds += 1; |
| 709 | 735 | } |
| 710 | 736 | |
| 711 | 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 | 741 | return .{ ncmds, buffer.len }; |
| 716 | 742 | } |
| ... | ... | @@ -742,7 +768,7 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { |
| 742 | 768 | header.ncmds = @intCast(ncmds); |
| 743 | 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 | 774 | const std = @import("std"); |
src/link/NvPtx.zig+9-7| ... | ... | @@ -82,11 +82,17 @@ pub fn deinit(self: *NvPtx) void { |
| 82 | 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 | 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 | 96 | return self.llvm_object.updateNav(pt, nav); |
| 91 | 97 | } |
| 92 | 98 | |
| ... | ... | @@ -94,7 +100,7 @@ pub fn updateExports( |
| 94 | 100 | self: *NvPtx, |
| 95 | 101 | pt: Zcu.PerThread, |
| 96 | 102 | exported: Zcu.Exported, |
| 97 | export_indices: []const u32, | |
| 103 | export_indices: []const Zcu.Export.Index, | |
| 98 | 104 | ) !void { |
| 99 | 105 | if (build_options.skip_non_native and builtin.object_format != .nvptx) |
| 100 | 106 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | ... | @@ -102,10 +108,6 @@ pub fn updateExports( |
| 102 | 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 | 111 | pub fn flush(self: *NvPtx, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 110 | 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 | 60 | data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty, |
| 61 | 61 | /// When `updateExports` is called, we store the export indices here, to be used |
| 62 | 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 | 65 | lazy_syms: LazySymbolTable = .{}, |
| 66 | 66 | |
| ... | ... | @@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void |
| 345 | 345 | try a.writer().writeInt(u16, 1, .big); |
| 346 | 346 | |
| 347 | 347 | // getting the full file path |
| 348 | // TODO don't call getcwd here, that is inappropriate | |
| 348 | 349 | var buf: [std.fs.max_path_bytes]u8 = undefined; |
| 349 | 350 | const full_path = try std.fs.path.join(arena, &.{ |
| 350 | 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 | 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 | 396 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 390 | 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 | 404 | |
| 398 | 405 | const atom_idx = try self.seeNav(pt, func.owner_nav); |
| 399 | 406 | |
| 400 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 401 | defer code_buffer.deinit(); | |
| 407 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 408 | defer code_buffer.deinit(gpa); | |
| 402 | 409 | var dbg_info_output: DebugInfoOutput = .{ |
| 403 | 410 | .dbg_line = std.ArrayList(u8).init(gpa), |
| 404 | 411 | .start_line = null, |
| ... | ... | @@ -409,7 +416,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 409 | 416 | }; |
| 410 | 417 | defer dbg_info_output.dbg_line.deinit(); |
| 411 | 418 | |
| 412 | const res = try codegen.generateFunction( | |
| 419 | try codegen.generateFunction( | |
| 413 | 420 | &self.base, |
| 414 | 421 | pt, |
| 415 | 422 | zcu.navSrcLoc(func.owner_nav), |
| ... | ... | @@ -419,10 +426,7 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 419 | 426 | &code_buffer, |
| 420 | 427 | .{ .plan9 = &dbg_info_output }, |
| 421 | 428 | ); |
| 422 | const code = switch (res) { | |
| 423 | .ok => try code_buffer.toOwnedSlice(), | |
| 424 | .fail => |em| return zcu.failed_codegen.put(gpa, func.owner_nav, em), | |
| 425 | }; | |
| 429 | const code = try code_buffer.toOwnedSlice(gpa); | |
| 426 | 430 | self.getAtomPtr(atom_idx).code = .{ |
| 427 | 431 | .code_ptr = null, |
| 428 | 432 | .other = .{ .nav_index = func.owner_nav }, |
| ... | ... | @@ -433,11 +437,13 @@ pub fn updateFunc(self: *Plan9, pt: Zcu.PerThread, func_index: InternPool.Index, |
| 433 | 437 | .start_line = dbg_info_output.start_line.?, |
| 434 | 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 | 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 | 447 | const zcu = pt.zcu; |
| 442 | 448 | const gpa = zcu.gpa; |
| 443 | 449 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -456,10 +462,10 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde |
| 456 | 462 | if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) { |
| 457 | 463 | const atom_idx = try self.seeNav(pt, nav_index); |
| 458 | 464 | |
| 459 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 460 | defer code_buffer.deinit(); | |
| 465 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 466 | defer code_buffer.deinit(gpa); | |
| 461 | 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 | 469 | &self.base, |
| 464 | 470 | pt, |
| 465 | 471 | zcu.navSrcLoc(nav_index), |
| ... | ... | @@ -467,10 +473,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde |
| 467 | 473 | &code_buffer, |
| 468 | 474 | .{ .atom_index = @intCast(atom_idx) }, |
| 469 | 475 | ); |
| 470 | const code = switch (res) { | |
| 471 | .ok => code_buffer.items, | |
| 472 | .fail => |em| return zcu.failed_codegen.put(gpa, nav_index, em), | |
| 473 | }; | |
| 476 | const code = code_buffer.items; | |
| 474 | 477 | try self.data_nav_table.ensureUnusedCapacity(gpa, 1); |
| 475 | 478 | const duped_code = try gpa.dupe(u8, code); |
| 476 | 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 | 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 | 541 | const comp = self.base.comp; |
| 542 | const diags = &comp.link_diags; | |
| 534 | 543 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 535 | 544 | assert(!use_lld); |
| 536 | 545 | |
| 537 | 546 | switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) { |
| 538 | 547 | .Exe => {}, |
| 539 | // plan9 object files are totally different | |
| 540 | .Obj => return error.TODOImplementPlan9Objs, | |
| 541 | .Lib => return error.TODOImplementWritingLibFiles, | |
| 548 | .Obj => return diags.fail("writing plan9 object files unimplemented", .{}), | |
| 549 | .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}), | |
| 542 | 550 | } |
| 543 | 551 | return self.flushModule(arena, tid, prog_node); |
| 544 | 552 | } |
| ... | ... | @@ -583,7 +591,13 @@ fn atomCount(self: *Plan9) usize { |
| 583 | 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 | 601 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 588 | 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 | 608 | _ = arena; // Has the same lifetime as the call to Compilation.update. |
| 595 | 609 | |
| 596 | 610 | const comp = self.base.comp; |
| 611 | const diags = &comp.link_diags; | |
| 597 | 612 | const gpa = comp.gpa; |
| 598 | 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 | 620 | defer assert(self.hdr.entry != 0x0); |
| 606 | 621 | |
| 607 | 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 | 624 | tid, |
| 610 | 625 | ); |
| 611 | 626 | defer pt.deactivate(); |
| ... | ... | @@ -614,22 +629,16 @@ pub fn flushModule(self: *Plan9, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 614 | 629 | if (self.lazy_syms.getPtr(.none)) |metadata| { |
| 615 | 630 | // Most lazy symbols can be updated on first use, but |
| 616 | 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 | 633 | pt, |
| 619 | 634 | .{ .kind = .code, .ty = .anyerror_type }, |
| 620 | 635 | metadata.text_atom, |
| 621 | ) catch |err| return switch (err) { | |
| 622 | error.CodegenFail => error.FlushFailure, | |
| 623 | else => |e| e, | |
| 624 | }; | |
| 625 | if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( | |
| 636 | ); | |
| 637 | if (metadata.rodata_state != .unused) try self.updateLazySymbolAtom( | |
| 626 | 638 | pt, |
| 627 | 639 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 628 | 640 | metadata.rodata_atom, |
| 629 | ) catch |err| return switch (err) { | |
| 630 | error.CodegenFail => error.FlushFailure, | |
| 631 | else => |e| e, | |
| 632 | }; | |
| 641 | ); | |
| 633 | 642 | } |
| 634 | 643 | for (self.lazy_syms.values()) |*metadata| { |
| 635 | 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 | 911 | } |
| 903 | 912 | } |
| 904 | 913 | } |
| 905 | // write it all! | |
| 906 | try file.pwritevAll(iovecs, 0); | |
| 914 | file.pwritevAll(iovecs, 0) catch |err| return diags.fail("failed to write file: {s}", .{@errorName(err)}); | |
| 907 | 915 | } |
| 908 | 916 | fn addNavExports( |
| 909 | 917 | self: *Plan9, |
| 910 | mod: *Zcu, | |
| 918 | zcu: *Zcu, | |
| 911 | 919 | nav_index: InternPool.Nav.Index, |
| 912 | export_indices: []const u32, | |
| 920 | export_indices: []const Zcu.Export.Index, | |
| 913 | 921 | ) !void { |
| 914 | 922 | const gpa = self.base.comp.gpa; |
| 915 | 923 | const metadata = self.navs.getPtr(nav_index).?; |
| 916 | 924 | const atom = self.getAtom(metadata.index); |
| 917 | 925 | |
| 918 | 926 | for (export_indices) |export_idx| { |
| 919 | const exp = mod.all_exports.items[export_idx]; | |
| 920 | const exp_name = exp.opts.name.toSlice(&mod.intern_pool); | |
| 927 | const exp = export_idx.ptr(zcu); | |
| 928 | const exp_name = exp.opts.name.toSlice(&zcu.intern_pool); | |
| 921 | 929 | // plan9 does not support custom sections |
| 922 | 930 | if (exp.opts.section.unwrap()) |section_name| { |
| 923 | if (!section_name.eqlSlice(".text", &mod.intern_pool) and | |
| 924 | !section_name.eqlSlice(".data", &mod.intern_pool)) | |
| 931 | if (!section_name.eqlSlice(".text", &zcu.intern_pool) and | |
| 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 | 935 | gpa, |
| 928 | mod.navSrcLoc(nav_index), | |
| 936 | zcu.navSrcLoc(nav_index), | |
| 929 | 937 | "plan9 does not support extra sections", |
| 930 | 938 | .{}, |
| 931 | 939 | )); |
| ... | ... | @@ -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 | 958 | fn createAtom(self: *Plan9) !Atom.Index { |
| 995 | 959 | const gpa = self.base.comp.gpa; |
| 996 | 960 | const index = @as(Atom.Index, @intCast(self.atoms.items.len)); |
| ... | ... | @@ -1043,7 +1007,7 @@ pub fn updateExports( |
| 1043 | 1007 | self: *Plan9, |
| 1044 | 1008 | pt: Zcu.PerThread, |
| 1045 | 1009 | exported: Zcu.Exported, |
| 1046 | export_indices: []const u32, | |
| 1010 | export_indices: []const Zcu.Export.Index, | |
| 1047 | 1011 | ) !void { |
| 1048 | 1012 | const gpa = self.base.comp.gpa; |
| 1049 | 1013 | switch (exported) { |
| ... | ... | @@ -1054,7 +1018,7 @@ pub fn updateExports( |
| 1054 | 1018 | gpa.free(kv.value); |
| 1055 | 1019 | } |
| 1056 | 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 | 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 | 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 | 1058 | const gpa = pt.zcu.gpa; |
| 1059 | const comp = self.base.comp; | |
| 1060 | const diags = &comp.link_diags; | |
| 1090 | 1061 | |
| 1091 | 1062 | var required_alignment: InternPool.Alignment = .none; |
| 1092 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1093 | defer code_buffer.deinit(); | |
| 1063 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1064 | defer code_buffer.deinit(gpa); | |
| 1094 | 1065 | |
| 1095 | 1066 | // create the symbol for the name |
| 1096 | 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 | 1078 | |
| 1108 | 1079 | // generate the code |
| 1109 | 1080 | const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1110 | const res = try codegen.generateLazySymbol( | |
| 1081 | codegen.generateLazySymbol( | |
| 1111 | 1082 | &self.base, |
| 1112 | 1083 | pt, |
| 1113 | 1084 | src, |
| ... | ... | @@ -1116,14 +1087,12 @@ fn updateLazySymbolAtom(self: *Plan9, pt: Zcu.PerThread, sym: File.LazySymbol, a |
| 1116 | 1087 | &code_buffer, |
| 1117 | 1088 | .none, |
| 1118 | 1089 | .{ .atom_index = @intCast(atom_index) }, |
| 1119 | ); | |
| 1120 | const code = switch (res) { | |
| 1121 | .ok => code_buffer.items, | |
| 1122 | .fail => |em| { | |
| 1123 | log.err("{s}", .{em.msg}); | |
| 1124 | return error.CodegenFail; | |
| 1125 | }, | |
| 1090 | ) catch |err| switch (err) { | |
| 1091 | error.OutOfMemory => return error.OutOfMemory, | |
| 1092 | error.CodegenFail => return error.LinkFailure, | |
| 1093 | error.Overflow => return diags.fail("codegen failure: encountered number too big for compiler", .{}), | |
| 1126 | 1094 | }; |
| 1095 | const code = code_buffer.items; | |
| 1127 | 1096 | // duped_code is freed when the atom is freed |
| 1128 | 1097 | const duped_code = try gpa.dupe(u8, code); |
| 1129 | 1098 | errdefer gpa.free(duped_code); |
| ... | ... | @@ -1283,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 1283 | 1252 | try self.writeSym(writer, sym); |
| 1284 | 1253 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 1285 | 1254 | for (export_indices) |export_idx| { |
| 1286 | const exp = zcu.all_exports.items[export_idx]; | |
| 1255 | const exp = export_idx.ptr(zcu); | |
| 1287 | 1256 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { |
| 1288 | 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 | 1291 | try self.writeSym(writer, sym); |
| 1323 | 1292 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 1324 | 1293 | for (export_indices) |export_idx| { |
| 1325 | const exp = zcu.all_exports.items[export_idx]; | |
| 1294 | const exp = export_idx.ptr(zcu); | |
| 1326 | 1295 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { |
| 1327 | 1296 | const s = self.syms.items[exp_i]; |
| 1328 | 1297 | if (mem.eql(u8, s.name, "_start")) |
| ... | ... | @@ -1432,19 +1401,16 @@ pub fn lowerUav( |
| 1432 | 1401 | const got_index = self.allocateGotIndex(); |
| 1433 | 1402 | gop.value_ptr.* = index; |
| 1434 | 1403 | // we need to free name latex |
| 1435 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1436 | const res = try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index }); | |
| 1437 | const code = switch (res) { | |
| 1438 | .ok => code_buffer.items, | |
| 1439 | .fail => |em| return .{ .fail = em }, | |
| 1440 | }; | |
| 1404 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1405 | defer code_buffer.deinit(gpa); | |
| 1406 | try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index }); | |
| 1441 | 1407 | const atom_ptr = self.getAtomPtr(index); |
| 1442 | 1408 | atom_ptr.* = .{ |
| 1443 | 1409 | .type = .d, |
| 1444 | 1410 | .offset = undefined, |
| 1445 | 1411 | .sym_index = null, |
| 1446 | 1412 | .got_index = got_index, |
| 1447 | .code = Atom.CodePtr.fromSlice(code), | |
| 1413 | .code = Atom.CodePtr.fromSlice(try code_buffer.toOwnedSlice(gpa)), | |
| 1448 | 1414 | }; |
| 1449 | 1415 | _ = try atom_ptr.getOrCreateSymbolTableEntry(self); |
| 1450 | 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 | 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 | 132 | if (build_options.skip_non_native) { |
| 127 | 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 | 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 | 144 | if (build_options.skip_non_native) { |
| 139 | 145 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 140 | 146 | } |
| ... | ... | @@ -149,7 +155,7 @@ pub fn updateExports( |
| 149 | 155 | self: *SpirV, |
| 150 | 156 | pt: Zcu.PerThread, |
| 151 | 157 | exported: Zcu.Exported, |
| 152 | export_indices: []const u32, | |
| 158 | export_indices: []const Zcu.Export.Index, | |
| 153 | 159 | ) !void { |
| 154 | 160 | const zcu = pt.zcu; |
| 155 | 161 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -184,7 +190,7 @@ pub fn updateExports( |
| 184 | 190 | }; |
| 185 | 191 | |
| 186 | 192 | for (export_indices) |export_idx| { |
| 187 | const exp = zcu.all_exports.items[export_idx]; | |
| 193 | const exp = export_idx.ptr(zcu); | |
| 188 | 194 | try self.object.spv.declareEntryPoint( |
| 189 | 195 | spv_decl_index, |
| 190 | 196 | exp.opts.name.toSlice(ip), |
| ... | ... | @@ -196,16 +202,21 @@ pub fn updateExports( |
| 196 | 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 | 205 | pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 205 | 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 | 220 | if (build_options.skip_non_native) { |
| 210 | 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 | 227 | const sub_prog_node = prog_node.start("Flush Module", 0); |
| 217 | 228 | defer sub_prog_node.end(); |
| 218 | 229 | |
| 219 | const spv = &self.object.spv; | |
| 220 | ||
| 221 | 230 | const comp = self.base.comp; |
| 231 | const spv = &self.object.spv; | |
| 232 | const diags = &comp.link_diags; | |
| 222 | 233 | const gpa = comp.gpa; |
| 223 | 234 | const target = comp.getTarget(); |
| 224 | _ = tid; | |
| 225 | 235 | |
| 226 | 236 | try writeCapabilities(spv, target); |
| 227 | 237 | try writeMemoryModel(spv, target); |
| ... | ... | @@ -264,13 +274,11 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 264 | 274 | |
| 265 | 275 | const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { |
| 266 | 276 | error.OutOfMemory => return error.OutOfMemory, |
| 267 | else => |other| { | |
| 268 | log.err("error while linking: {s}", .{@errorName(other)}); | |
| 269 | return error.FlushFailure; | |
| 270 | }, | |
| 277 | else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}), | |
| 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 | 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 | //! 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 | 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 | 17 | const builtin = @import("builtin"); |
| 5 | 18 | const native_endian = builtin.cpu.arch.endian(); |
| 6 | 19 | |
| 20 | const build_options = @import("build_options"); | |
| 21 | ||
| 7 | 22 | const std = @import("std"); |
| 8 | 23 | const Allocator = std.mem.Allocator; |
| 9 | 24 | const Cache = std.Build.Cache; |
| 10 | 25 | const Path = Cache.Path; |
| 11 | 26 | const assert = std.debug.assert; |
| 12 | 27 | const fs = std.fs; |
| 13 | const gc_log = std.log.scoped(.gc); | |
| 14 | 28 | const leb = std.leb; |
| 15 | 29 | const log = std.log.scoped(.link); |
| 16 | 30 | const mem = std.mem; |
| 17 | 31 | |
| 18 | 32 | const Air = @import("../Air.zig"); |
| 19 | const Archive = @import("Wasm/Archive.zig"); | |
| 33 | const Mir = @import("../arch/wasm/Mir.zig"); | |
| 20 | 34 | const CodeGen = @import("../arch/wasm/CodeGen.zig"); |
| 35 | const abi = @import("../arch/wasm/abi.zig"); | |
| 21 | 36 | const Compilation = @import("../Compilation.zig"); |
| 22 | 37 | const Dwarf = @import("Dwarf.zig"); |
| 23 | 38 | const InternPool = @import("../InternPool.zig"); |
| 24 | 39 | const Liveness = @import("../Liveness.zig"); |
| 25 | 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 | 41 | const Zcu = @import("../Zcu.zig"); |
| 31 | const ZigObject = @import("Wasm/ZigObject.zig"); | |
| 32 | 42 | const codegen = @import("../codegen.zig"); |
| 33 | 43 | const dev = @import("../dev.zig"); |
| 34 | 44 | const link = @import("../link.zig"); |
| 35 | 45 | const lldMain = @import("../main.zig").lldMain; |
| 36 | 46 | const trace = @import("../tracy.zig").trace; |
| 37 | 47 | const wasi_libc = @import("../wasi_libc.zig"); |
| 48 | const Value = @import("../Value.zig"); | |
| 38 | 49 | |
| 39 | 50 | base: link.File, |
| 40 | 51 | /// Null-terminated strings, indexes have type String and string_table provides |
| 41 | 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 | 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 | 65 | /// Omitted when serializing linker state. |
| 44 | 66 | string_table: String.Table, |
| 45 | 67 | /// Symbol name of the entry function to export |
| ... | ... | @@ -62,2525 +84,3230 @@ export_table: bool, |
| 62 | 84 | name: []const u8, |
| 63 | 85 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 64 | 86 | llvm_object: ?LlvmObject.Ptr = null, |
| 65 | zig_object: ?*ZigObject, | |
| 66 | 87 | /// List of relocatable files to be linked into the final binary. |
| 67 | 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 | 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 | |
| 70 | /// to support existing code. | |
| 71 | /// TODO: Allow setting this through a flag? | |
| 72 | host_name: String, | |
| 73 | /// List of symbols generated by the linker. | |
| 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, | |
| 163 | /// LLVM uses "env" by default when none is given. | |
| 164 | /// This value is passed to object files since wasm tooling conventions provides | |
| 165 | /// no way to specify the module name in the symbol table. | |
| 166 | object_host_name: OptionalString, | |
| 167 | ||
| 120 | 168 | /// Memory section |
| 121 | 169 | memories: std.wasm.Memory = .{ .limits = .{ |
| 122 | 170 | .min = 0, |
| 123 | .max = undefined, | |
| 124 | .flags = 0, | |
| 171 | .max = 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 | 175 | /// `--verbose-link` output. |
| 168 | 176 | /// Initialized on creation, appended to as inputs are added, printed during `flush`. |
| 169 | 177 | /// String data is allocated into Compilation arena. |
| 170 | 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 | 180 | preloaded_strings: PreloadedStrings, |
| 176 | 181 | |
| 177 | /// Type reflection is used on the field names to autopopulate each field | |
| 178 | /// during initialization. | |
| 179 | const PreloadedStrings = struct { | |
| 180 | __heap_base: String, | |
| 181 | __heap_end: String, | |
| 182 | __indirect_function_table: String, | |
| 183 | __linear_memory: String, | |
| 184 | __stack_pointer: String, | |
| 185 | __tls_align: String, | |
| 186 | __tls_base: String, | |
| 187 | __tls_size: String, | |
| 188 | __wasm_apply_global_tls_relocs: String, | |
| 189 | __wasm_call_ctors: String, | |
| 190 | __wasm_init_memory: String, | |
| 191 | __wasm_init_memory_flag: String, | |
| 192 | __wasm_init_tls: String, | |
| 193 | __zig_err_name_table: String, | |
| 194 | __zig_err_names: String, | |
| 195 | __zig_errors_len: String, | |
| 196 | _initialize: String, | |
| 197 | _start: String, | |
| 198 | memory: String, | |
| 182 | /// This field is used when emitting an object; `navs_exe` used otherwise. | |
| 183 | /// Does not include externs since that data lives elsewhere. | |
| 184 | navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty, | |
| 185 | /// This field is unused when emitting an object; `navs_obj` used otherwise. | |
| 186 | /// Does not include externs since that data lives elsewhere. | |
| 187 | navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty, | |
| 188 | /// Tracks all InternPool values referenced by codegen. Needed for outputting | |
| 189 | /// the data segment. This one does not track ref count because object files | |
| 190 | /// require using max LEB encoding for these references anyway. | |
| 191 | uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty, | |
| 192 | /// Tracks ref count to optimize LEB encodings for UAV references. | |
| 193 | uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty, | |
| 194 | /// Sparse table of uavs that need to be emitted with greater alignment than | |
| 195 | /// the default for the type. | |
| 196 | overaligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty, | |
| 197 | /// When the key is an enum type, this represents a `@tagName` function. | |
| 198 | zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty, | |
| 199 | nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty, | |
| 200 | uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty, | |
| 201 | imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, | |
| 202 | ||
| 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. | |
| 202 | const CustomSections = struct { | |
| 203 | @".debug_info": CustomSection, | |
| 204 | @".debug_pubtypes": CustomSection, | |
| 205 | @".debug_abbrev": CustomSection, | |
| 206 | @".debug_line": CustomSection, | |
| 207 | @".debug_str": CustomSection, | |
| 208 | @".debug_pubnames": CustomSection, | |
| 209 | @".debug_loc": CustomSection, | |
| 210 | @".debug_ranges": CustomSection, | |
| 312 | /// Index into `Wasm.zcu_indirect_function_set`. | |
| 313 | pub const ZcuIndirectFunctionSetIndex = enum(u32) { | |
| 314 | _, | |
| 211 | 315 | }; |
| 212 | 316 | |
| 213 | const CustomSection = struct { | |
| 214 | name: String, | |
| 215 | index: Segment.OptionalIndex, | |
| 317 | pub const UavFixup = extern struct { | |
| 318 | uavs_exe_index: UavsExeIndex, | |
| 319 | /// Index into `string_bytes`. | |
| 320 | offset: u32, | |
| 321 | addend: u32, | |
| 216 | 322 | }; |
| 217 | 323 | |
| 218 | /// Index into string_bytes | |
| 219 | pub const String = enum(u32) { | |
| 220 | _, | |
| 324 | pub const NavFixup = extern struct { | |
| 325 | navs_exe_index: NavsExeIndex, | |
| 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 { | |
| 225 | bytes: []const u8, | |
| 337 | /// Index into `objects`. | |
| 338 | pub const ObjectIndex = enum(u32) { | |
| 339 | _, | |
| 226 | 340 | |
| 227 | pub fn eql(_: @This(), a: String, b: String) bool { | |
| 228 | return a == b; | |
| 229 | } | |
| 341 | pub fn ptr(index: ObjectIndex, wasm: *const Wasm) *Object { | |
| 342 | return &wasm.objects.items[@intFromEnum(index)]; | |
| 343 | } | |
| 344 | }; | |
| 230 | 345 | |
| 231 | pub fn hash(ctx: @This(), key: String) u64 { | |
| 232 | return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0)); | |
| 233 | } | |
| 234 | }; | |
| 346 | /// Index into `Wasm.functions`. | |
| 347 | pub const FunctionIndex = enum(u32) { | |
| 348 | _, | |
| 235 | 349 | |
| 236 | const TableIndexAdapter = struct { | |
| 237 | bytes: []const u8, | |
| 350 | pub fn ptr(index: FunctionIndex, wasm: *const Wasm) *FunctionImport.Resolution { | |
| 351 | return &wasm.functions.keys()[@intFromEnum(index)]; | |
| 352 | } | |
| 238 | 353 | |
| 239 | pub fn eql(ctx: @This(), a: []const u8, b: String) bool { | |
| 240 | return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0)); | |
| 241 | } | |
| 354 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?FunctionIndex { | |
| 355 | return fromResolution(wasm, .fromIpNav(wasm, nav_index)); | |
| 356 | } | |
| 242 | 357 | |
| 243 | pub fn hash(_: @This(), adapted_key: []const u8) u64 { | |
| 244 | assert(mem.indexOfScalar(u8, adapted_key, 0) == null); | |
| 245 | return std.hash_map.hashString(adapted_key); | |
| 358 | pub fn fromTagNameType(wasm: *const Wasm, tag_type: InternPool.Index) ?FunctionIndex { | |
| 359 | const zcu_func: ZcuFunc.Index = @enumFromInt(wasm.zcu_funcs.getIndex(tag_type) orelse return null); | |
| 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 { | |
| 250 | const result: OptionalString = @enumFromInt(@intFromEnum(i)); | |
| 251 | assert(result != .none); | |
| 252 | return result; | |
| 372 | pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?FunctionIndex { | |
| 373 | const i = wasm.functions.getIndex(resolution) orelse return null; | |
| 374 | return @enumFromInt(i); | |
| 253 | 375 | } |
| 254 | 376 | }; |
| 255 | 377 | |
| 256 | pub const OptionalString = enum(u32) { | |
| 257 | none = std.math.maxInt(u32), | |
| 378 | pub const GlobalExport = extern struct { | |
| 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 { | |
| 261 | if (i == .none) return null; | |
| 262 | return @enumFromInt(@intFromEnum(i)); | |
| 391 | pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?OutputFunctionIndex { | |
| 392 | return fromFunctionIndex(wasm, FunctionIndex.fromResolution(wasm, resolution) orelse return null); | |
| 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. | |
| 267 | pub const ObjectId = enum(u16) { | |
| 268 | zig_object = std.math.maxInt(u16) - 1, | |
| 443 | /// Index into `Wasm.globals`. | |
| 444 | pub const GlobalIndex = enum(u32) { | |
| 269 | 445 | _, |
| 270 | 446 | |
| 271 | pub fn toOptional(i: ObjectId) OptionalObjectId { | |
| 272 | const result: OptionalObjectId = @enumFromInt(@intFromEnum(i)); | |
| 273 | assert(result != .none); | |
| 274 | return result; | |
| 447 | /// This is only accurate when not emitting an object and there is a Zcu. | |
| 448 | pub const stack_pointer: GlobalIndex = @enumFromInt(0); | |
| 449 | ||
| 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. | |
| 279 | pub const OptionalObjectId = enum(u16) { | |
| 280 | zig_object = std.math.maxInt(u16) - 1, | |
| 281 | none = std.math.maxInt(u16), | |
| 485 | /// Index into `tables`. | |
| 486 | pub const TableIndex = enum(u32) { | |
| 282 | 487 | _, |
| 283 | 488 | |
| 284 | pub fn unwrap(i: OptionalObjectId) ?ObjectId { | |
| 285 | if (i == .none) return null; | |
| 286 | return @enumFromInt(@intFromEnum(i)); | |
| 489 | pub fn ptr(index: TableIndex, f: *const Flush) *Wasm.TableImport.Resolution { | |
| 490 | return &f.tables.items[@intFromEnum(index)]; | |
| 287 | 491 | } |
| 288 | }; | |
| 289 | 492 | |
| 290 | /// None of this data is serialized since it can be re-loaded from disk, or if | |
| 291 | /// it has been changed, the data must be discarded. | |
| 292 | const LazyArchive = struct { | |
| 293 | path: Path, | |
| 294 | file_contents: []const u8, | |
| 295 | archive: Archive, | |
| 493 | pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex { | |
| 494 | return @enumFromInt(wasm.tables.getIndex(.fromObjectTable(i)).?); | |
| 495 | } | |
| 296 | 496 | |
| 297 | fn deinit(la: *LazyArchive, gpa: Allocator) void { | |
| 298 | la.archive.deinit(gpa); | |
| 299 | gpa.free(la.path.sub_path); | |
| 300 | gpa.free(la.file_contents); | |
| 301 | la.* = undefined; | |
| 497 | pub fn fromSymbolName(wasm: *const Wasm, name: String) TableIndex { | |
| 498 | const import = wasm.object_table_imports.getPtr(name).?; | |
| 499 | return @enumFromInt(wasm.tables.getIndex(import.resolution).?); | |
| 302 | 500 | } |
| 303 | 501 | }; |
| 304 | 502 | |
| 305 | pub const Segment = struct { | |
| 306 | alignment: Alignment, | |
| 307 | size: u32, | |
| 308 | offset: u32, | |
| 309 | flags: u32, | |
| 503 | /// The first N indexes correspond to input objects (`objects`) array. | |
| 504 | /// After that, the indexes correspond to the `source_locations` array, | |
| 505 | /// representing a location in a Zig source file that can be pinpointed | |
| 506 | /// precisely via AST node and token. | |
| 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) { | |
| 322 | none = std.math.maxInt(u32), | |
| 323 | _, | |
| 324 | ||
| 325 | pub fn unwrap(i: OptionalIndex) ?Index { | |
| 326 | if (i == .none) return null; | |
| 327 | return @enumFromInt(@intFromEnum(i)); | |
| 328 | } | |
| 518 | pub const Unpacked = union(enum) { | |
| 519 | none, | |
| 520 | zig_object_nofile, | |
| 521 | object_index: ObjectIndex, | |
| 522 | source_location_index: Index, | |
| 329 | 523 | }; |
| 330 | 524 | |
| 331 | pub const Flag = enum(u32) { | |
| 332 | WASM_DATA_SEGMENT_IS_PASSIVE = 0x01, | |
| 333 | WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02, | |
| 334 | }; | |
| 525 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) SourceLocation { | |
| 526 | _ = wasm; | |
| 527 | return switch (unpacked) { | |
| 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 { | |
| 337 | return segment.flags & @intFromEnum(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0; | |
| 549 | pub fn fromObject(object_index: ObjectIndex, wasm: *const Wasm) SourceLocation { | |
| 550 | return pack(.{ .object_index = object_index }, wasm); | |
| 338 | 551 | } |
| 339 | 552 | |
| 340 | /// For a given segment, determines if it needs passive initialization | |
| 341 | fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool { | |
| 342 | if (import_mem and !std.mem.eql(u8, name, ".bss")) { | |
| 343 | return true; | |
| 553 | pub fn addError(sl: SourceLocation, wasm: *Wasm, comptime f: []const u8, args: anytype) void { | |
| 554 | const diags = &wasm.base.comp.link_diags; | |
| 555 | switch (sl.unpack(wasm)) { | |
| 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 { | |
| 350 | /// The index of the symbol within the specified file | |
| 351 | index: Symbol.Index, | |
| 352 | /// The index of the object file where the symbol resides. | |
| 353 | file: OptionalObjectId, | |
| 354 | }; | |
| 563 | pub fn addNote( | |
| 564 | sl: SourceLocation, | |
| 565 | err: *link.Diags.ErrorWithNotes, | |
| 566 | comptime f: []const u8, | |
| 567 | args: anytype, | |
| 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 | |
| 357 | pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol { | |
| 358 | if (wasm.discarded.get(loc)) |new_loc| { | |
| 359 | return symbolLocSymbol(wasm, new_loc); | |
| 574 | pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{LinkFailure} { | |
| 575 | return diags.failSourceLocation(.{ .wasm = sl }, format, args); | |
| 360 | 576 | } |
| 361 | return switch (loc.file) { | |
| 362 | .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)], | |
| 363 | .zig_object => wasm.zig_object.?.symbol(loc.index), | |
| 364 | _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)], | |
| 577 | ||
| 578 | pub fn string( | |
| 579 | sl: SourceLocation, | |
| 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. | |
| 369 | pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) [:0]const u8 { | |
| 370 | return wasm.stringSlice(wasm.symbolLocSymbol(loc).name); | |
| 371 | } | |
| 664 | pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void { | |
| 665 | flags.no_strip = no_strip; | |
| 666 | flags.alive = false; | |
| 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. | |
| 374 | /// e.g. when a symbol was resolved and replaced by the symbol | |
| 375 | /// in a different file, this will return said location. | |
| 376 | /// If the symbol wasn't replaced by another, this will return | |
| 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); | |
| 674 | pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool) bool { | |
| 675 | return flags.exported or | |
| 676 | (is_dynamic and !flags.visibility_hidden) or | |
| 677 | (flags.no_strip and flags.must_link); | |
| 381 | 678 | } |
| 382 | return loc; | |
| 383 | } | |
| 384 | 679 | |
| 385 | // Contains the location of the function symbol, as well as | |
| 386 | /// the priority itself of the initialization function. | |
| 387 | pub const InitFuncLoc = struct { | |
| 388 | /// object file index in the list of objects. | |
| 389 | /// Unlike `SymbolLoc` this cannot be `null` as we never define | |
| 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, | |
| 680 | pub fn isExported(flags: SymbolFlags, is_dynamic: bool) bool { | |
| 681 | if (flags.undefined or flags.binding == .local) return false; | |
| 682 | if (is_dynamic and !flags.visibility_hidden) return true; | |
| 683 | return flags.exported; | |
| 684 | } | |
| 396 | 685 | |
| 397 | /// From a given `InitFuncLoc` returns the corresponding function symbol | |
| 398 | fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol { | |
| 399 | return wasm.symbolLocSymbol(getSymbolLoc(loc)); | |
| 686 | /// Returns the name as how it will be output into the final object | |
| 687 | /// file or binary. When `merge` is true, this will return the | |
| 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` | |
| 403 | fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc { | |
| 707 | pub const GlobalType4 = packed struct(u4) { | |
| 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 | 714 | return .{ |
| 405 | .file = loc.file.toOptional(), | |
| 406 | .index = loc.index, | |
| 715 | .valtype = gt.valtype.to(), | |
| 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`. | |
| 411 | fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool { | |
| 412 | _ = ctx; | |
| 413 | return lhs.priority < rhs.priority; | |
| 721 | pub const Valtype3 = enum(u3) { | |
| 722 | i32, | |
| 723 | i64, | |
| 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( | |
| 418 | arena: Allocator, | |
| 419 | comp: *Compilation, | |
| 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 | } | |
| 749 | /// Index into `Wasm.navs_obj`. | |
| 750 | pub const NavsObjIndex = enum(u32) { | |
| 751 | _, | |
| 427 | 752 | |
| 428 | pub fn createEmpty( | |
| 429 | arena: Allocator, | |
| 430 | comp: *Compilation, | |
| 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); | |
| 753 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index { | |
| 754 | return &wasm.navs_obj.keys()[@intFromEnum(i)]; | |
| 755 | } | |
| 437 | 756 | |
| 438 | const use_lld = build_options.have_llvm and comp.config.use_lld; | |
| 439 | const use_llvm = comp.config.use_llvm; | |
| 440 | const output_mode = comp.config.output_mode; | |
| 441 | const shared_memory = comp.config.shared_memory; | |
| 442 | const wasi_exec_model = comp.config.wasi_exec_model; | |
| 757 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj { | |
| 758 | return &wasm.navs_obj.values()[@intFromEnum(i)]; | |
| 759 | } | |
| 443 | 760 | |
| 444 | // If using LLD to link, this code should produce an object file so that it | |
| 445 | // can be passed to LLD. | |
| 446 | // If using LLVM to generate the object file for the zig compilation unit, | |
| 447 | // we need a place to put the object file so that it can be subsequently | |
| 448 | // handled. | |
| 449 | const zcu_object_sub_path = if (!use_lld and !use_llvm) | |
| 450 | null | |
| 451 | else | |
| 452 | try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); | |
| 761 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { | |
| 762 | const zcu = wasm.base.comp.zcu.?; | |
| 763 | const ip = &zcu.intern_pool; | |
| 764 | const nav = ip.getNav(i.key(wasm).*); | |
| 765 | return nav.fqn.toSlice(ip); | |
| 766 | } | |
| 767 | }; | |
| 453 | 768 | |
| 454 | const wasm = try arena.create(Wasm); | |
| 455 | wasm.* = .{ | |
| 456 | .base = .{ | |
| 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, | |
| 769 | /// Index into `Wasm.navs_exe`. | |
| 770 | pub const NavsExeIndex = enum(u32) { | |
| 771 | _, | |
| 482 | 772 | |
| 483 | .entry_name = undefined, | |
| 484 | .zig_object = null, | |
| 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); | |
| 773 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index { | |
| 774 | return &wasm.navs_exe.keys()[@intFromEnum(i)]; | |
| 492 | 775 | } |
| 493 | errdefer wasm.base.destroy(); | |
| 494 | 776 | |
| 495 | wasm.host_name = try wasm.internString("env"); | |
| 496 | ||
| 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 | }; | |
| 777 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe { | |
| 778 | return &wasm.navs_exe.values()[@intFromEnum(i)]; | |
| 502 | 779 | } |
| 503 | 780 | |
| 504 | inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| { | |
| 505 | @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name); | |
| 781 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { | |
| 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) { | |
| 509 | .disabled => .none, | |
| 510 | .default => if (output_mode != .Exe) .none else defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), | |
| 511 | .enabled => defaultEntrySymbolName(&wasm.preloaded_strings, wasi_exec_model).toOptional(), | |
| 512 | .named => |name| (try wasm.internString(name)).toOptional(), | |
| 513 | }; | |
| 789 | /// Index into `Wasm.uavs_obj`. | |
| 790 | pub const UavsObjIndex = enum(u32) { | |
| 791 | _, | |
| 514 | 792 | |
| 515 | if (use_lld and (use_llvm or !comp.config.have_zcu)) { | |
| 516 | // LLVM emits the object file (if any); LLD links it into the final product. | |
| 517 | return wasm; | |
| 793 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { | |
| 794 | return &wasm.uavs_obj.keys()[@intFromEnum(i)]; | |
| 518 | 795 | } |
| 519 | 796 | |
| 520 | // What path should this Wasm linker code output to? | |
| 521 | // If using LLD to link, this code should produce an object file so that it | |
| 522 | // can be passed to LLD. | |
| 523 | const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; | |
| 797 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj { | |
| 798 | return &wasm.uavs_obj.values()[@intFromEnum(i)]; | |
| 799 | } | |
| 800 | }; | |
| 524 | 801 | |
| 525 | wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{ | |
| 526 | .truncate = true, | |
| 527 | .read = true, | |
| 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; | |
| 802 | /// Index into `Wasm.uavs_exe`. | |
| 803 | pub const UavsExeIndex = enum(u32) { | |
| 804 | _, | |
| 537 | 805 | |
| 538 | // create stack pointer symbol | |
| 539 | { | |
| 540 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__stack_pointer, .global); | |
| 541 | const symbol = wasm.symbolLocSymbol(loc); | |
| 542 | // For object files we will import the stack pointer symbol | |
| 543 | if (output_mode == .Obj) { | |
| 544 | symbol.setUndefined(true); | |
| 545 | symbol.index = @intCast(wasm.imported_globals_count); | |
| 546 | wasm.imported_globals_count += 1; | |
| 547 | try wasm.imports.putNoClobber(gpa, loc, .{ | |
| 548 | .module_name = wasm.host_name, | |
| 549 | .name = symbol.name, | |
| 550 | .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, | |
| 551 | }); | |
| 552 | } else { | |
| 553 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | |
| 554 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 555 | const global = try wasm.wasm_globals.addOne(gpa); | |
| 556 | global.* = .{ | |
| 557 | .global_type = .{ | |
| 558 | .valtype = .i32, | |
| 559 | .mutable = true, | |
| 560 | }, | |
| 561 | .init = .{ .i32_const = 0 }, | |
| 562 | }; | |
| 563 | } | |
| 806 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { | |
| 807 | return &wasm.uavs_exe.keys()[@intFromEnum(i)]; | |
| 808 | } | |
| 809 | ||
| 810 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe { | |
| 811 | return &wasm.uavs_exe.values()[@intFromEnum(i)]; | |
| 812 | } | |
| 813 | }; | |
| 814 | ||
| 815 | /// Used when emitting a relocatable object. | |
| 816 | pub const ZcuDataObj = extern struct { | |
| 817 | code: DataPayload, | |
| 818 | relocs: OutReloc.Slice, | |
| 819 | }; | |
| 820 | ||
| 821 | /// Used when not emitting a relocatable object. | |
| 822 | pub const ZcuDataExe = extern struct { | |
| 823 | code: DataPayload, | |
| 824 | /// Tracks how many references there are for the purposes of sorting data segments. | |
| 825 | count: u32, | |
| 826 | }; | |
| 827 | ||
| 828 | /// An abstraction for calling `lowerZcuData` repeatedly until all data entries | |
| 829 | /// are populated. | |
| 830 | const ZcuDataStarts = struct { | |
| 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 | |
| 567 | { | |
| 568 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__indirect_function_table, .table); | |
| 569 | const symbol = wasm.symbolLocSymbol(loc); | |
| 570 | const table: std.wasm.Table = .{ | |
| 571 | .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable` | |
| 572 | .reftype = .funcref, | |
| 839 | fn initObj(wasm: *const Wasm) ZcuDataStarts { | |
| 840 | return .{ | |
| 841 | .uavs_i = @intCast(wasm.uavs_obj.entries.len), | |
| 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 | |
| 595 | { | |
| 596 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_call_ctors, .function); | |
| 597 | const symbol = wasm.symbolLocSymbol(loc); | |
| 598 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 599 | // we do not know the function index until after we merged all sections. | |
| 600 | // Therefore we set `symbol.index` and create its corresponding references | |
| 601 | // at the end during `initializeCallCtorsFunction`. | |
| 845 | fn initExe(wasm: *const Wasm) ZcuDataStarts { | |
| 846 | return .{ | |
| 847 | .uavs_i = @intCast(wasm.uavs_exe.entries.len), | |
| 848 | }; | |
| 602 | 849 | } |
| 603 | 850 | |
| 604 | // shared-memory symbols for TLS support | |
| 605 | if (shared_memory) { | |
| 606 | { | |
| 607 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_base, .global); | |
| 608 | const symbol = wasm.symbolLocSymbol(loc); | |
| 609 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 610 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | |
| 611 | symbol.mark(); | |
| 612 | try wasm.wasm_globals.append(gpa, .{ | |
| 613 | .global_type = .{ .valtype = .i32, .mutable = true }, | |
| 614 | .init = .{ .i32_const = undefined }, | |
| 615 | }); | |
| 616 | } | |
| 617 | { | |
| 618 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_size, .global); | |
| 619 | const symbol = wasm.symbolLocSymbol(loc); | |
| 620 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 621 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | |
| 622 | symbol.mark(); | |
| 623 | try wasm.wasm_globals.append(gpa, .{ | |
| 624 | .global_type = .{ .valtype = .i32, .mutable = false }, | |
| 625 | .init = .{ .i32_const = undefined }, | |
| 626 | }); | |
| 627 | } | |
| 628 | { | |
| 629 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__tls_align, .global); | |
| 630 | const symbol = wasm.symbolLocSymbol(loc); | |
| 631 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 632 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | |
| 633 | symbol.mark(); | |
| 634 | try wasm.wasm_globals.append(gpa, .{ | |
| 635 | .global_type = .{ .valtype = .i32, .mutable = false }, | |
| 636 | .init = .{ .i32_const = undefined }, | |
| 637 | }); | |
| 638 | } | |
| 639 | { | |
| 640 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_tls, .function); | |
| 641 | const symbol = wasm.symbolLocSymbol(loc); | |
| 642 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 851 | fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { | |
| 852 | const comp = wasm.base.comp; | |
| 853 | const is_obj = comp.config.output_mode == .Obj; | |
| 854 | return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt); | |
| 855 | } | |
| 856 | ||
| 857 | fn finishObj(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { | |
| 858 | var uavs_i = zds.uavs_i; | |
| 859 | while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) { | |
| 860 | // Call to `lowerZcuData` here possibly creates more entries in these tables. | |
| 861 | wasm.uavs_obj.values()[uavs_i] = try lowerZcuData(wasm, pt, wasm.uavs_obj.keys()[uavs_i]); | |
| 643 | 862 | } |
| 644 | 863 | } |
| 645 | 864 | |
| 646 | if (comp.zcu) |zcu| { | |
| 647 | if (!use_llvm) { | |
| 648 | const zig_object = try arena.create(ZigObject); | |
| 649 | wasm.zig_object = zig_object; | |
| 650 | zig_object.* = .{ | |
| 651 | .path = .{ | |
| 652 | .root_dir = std.Build.Cache.Directory.cwd(), | |
| 653 | .sub_path = try std.fmt.allocPrint(gpa, "{s}.o", .{fs.path.stem(zcu.main_mod.root_src_path)}), | |
| 654 | }, | |
| 655 | .stack_pointer_sym = .null, | |
| 656 | }; | |
| 657 | try zig_object.init(wasm); | |
| 865 | fn finishExe(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { | |
| 866 | var uavs_i = zds.uavs_i; | |
| 867 | while (uavs_i < wasm.uavs_exe.entries.len) : (uavs_i += 1) { | |
| 868 | // Call to `lowerZcuData` here possibly creates more entries in these tables. | |
| 869 | const zcu_data = try lowerZcuData(wasm, pt, wasm.uavs_exe.keys()[uavs_i]); | |
| 870 | wasm.uavs_exe.values()[uavs_i].code = zcu_data.code; | |
| 658 | 871 | } |
| 659 | 872 | } |
| 873 | }; | |
| 660 | 874 | |
| 661 | return wasm; | |
| 662 | } | |
| 875 | pub const ZcuFunc = union { | |
| 876 | function: CodeGen.Function, | |
| 877 | tag_name: TagName, | |
| 663 | 878 | |
| 664 | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { | |
| 665 | var index: u32 = 0; | |
| 666 | while (index < wasm.func_types.items.len) : (index += 1) { | |
| 667 | if (wasm.func_types.items[index].eql(func_type)) return index; | |
| 668 | } | |
| 669 | return null; | |
| 670 | } | |
| 879 | pub const TagName = extern struct { | |
| 880 | symbol_name: String, | |
| 881 | type_index: FunctionType.Index, | |
| 882 | /// Index into `Wasm.tag_name_offs`. | |
| 883 | table_index: u32, | |
| 884 | }; | |
| 671 | 885 | |
| 672 | /// Either creates a new import, or updates one if existing. | |
| 673 | /// When `type_index` is non-null, we assume an external function. | |
| 674 | /// In all other cases, a data-symbol will be created instead. | |
| 675 | pub fn addOrUpdateImport( | |
| 676 | wasm: *Wasm, | |
| 677 | /// Name of the import | |
| 678 | name: []const u8, | |
| 679 | /// Symbol index that is external | |
| 680 | symbol_index: Symbol.Index, | |
| 681 | /// Optional library name (i.e. `extern "c" fn foo() void` | |
| 682 | lib_name: ?[:0]const u8, | |
| 683 | /// The index of the type that represents the function signature | |
| 684 | /// when the extern is a function. When this is null, a data-symbol | |
| 685 | /// is asserted instead. | |
| 686 | type_index: ?u32, | |
| 687 | ) !void { | |
| 688 | return wasm.zig_object.?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index); | |
| 689 | } | |
| 886 | /// Index into `Wasm.zcu_funcs`. | |
| 887 | /// Note that swapRemove is sometimes performed on `zcu_funcs`. | |
| 888 | pub const Index = enum(u32) { | |
| 889 | _, | |
| 690 | 890 | |
| 691 | /// For a given name, creates a new global synthetic symbol. | |
| 692 | /// Leaves index undefined and the default flags (0). | |
| 693 | fn createSyntheticSymbol(wasm: *Wasm, name: String, tag: Symbol.Tag) !SymbolLoc { | |
| 694 | return wasm.createSyntheticSymbolOffset(name, tag); | |
| 695 | } | |
| 891 | pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index { | |
| 892 | return &wasm.zcu_funcs.keys()[@intFromEnum(i)]; | |
| 893 | } | |
| 696 | 894 | |
| 697 | fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: String, tag: Symbol.Tag) !SymbolLoc { | |
| 698 | const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len); | |
| 699 | const loc: SymbolLoc = .{ .index = sym_index, .file = .none }; | |
| 700 | const gpa = wasm.base.comp.gpa; | |
| 701 | try wasm.synthetic_symbols.append(gpa, .{ | |
| 702 | .name = name_offset, | |
| 703 | .flags = 0, | |
| 704 | .tag = tag, | |
| 705 | .index = undefined, | |
| 706 | .virtual_address = undefined, | |
| 707 | }); | |
| 708 | try wasm.resolved_symbols.putNoClobber(gpa, loc, {}); | |
| 709 | try wasm.globals.put(gpa, name_offset, loc); | |
| 710 | return loc; | |
| 711 | } | |
| 895 | pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc { | |
| 896 | return &wasm.zcu_funcs.values()[@intFromEnum(i)]; | |
| 897 | } | |
| 712 | 898 | |
| 713 | fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void { | |
| 714 | const diags = &wasm.base.comp.link_diags; | |
| 715 | const obj = link.openObject(path, false, false) catch |err| { | |
| 716 | switch (diags.failParse(path, "failed to open object: {s}", .{@errorName(err)})) { | |
| 717 | error.LinkFailure => return, | |
| 899 | pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { | |
| 900 | const zcu = wasm.base.comp.zcu.?; | |
| 901 | const ip = &zcu.intern_pool; | |
| 902 | const ip_index = i.key(wasm).*; | |
| 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 | }; | |
| 720 | wasm.parseObject(obj) catch |err| { | |
| 721 | switch (diags.failParse(path, "failed to parse object: {s}", .{@errorName(err)})) { | |
| 722 | error.LinkFailure => return, | |
| 914 | ||
| 915 | pub fn typeIndex(i: @This(), wasm: *Wasm) FunctionType.Index { | |
| 916 | const comp = wasm.base.comp; | |
| 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 | } | |
| 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; | |
| 933 | }; | |
| 733 | 934 | |
| 734 | const file_contents = try gpa.alloc(u8, size); | |
| 735 | defer gpa.free(file_contents); | |
| 935 | pub const NavExport = extern struct { | |
| 936 | name: String, | |
| 937 | nav_index: InternPool.Nav.Index, | |
| 938 | }; | |
| 736 | 939 | |
| 737 | const n = try obj.file.preadAll(file_contents, 0); | |
| 738 | if (n != file_contents.len) return error.UnexpectedEndOfFile; | |
| 940 | pub const UavExport = extern struct { | |
| 941 | name: String, | |
| 942 | uav_index: InternPool.Index, | |
| 943 | }; | |
| 739 | 944 | |
| 740 | wasm.objects.appendAssumeCapacity(try Object.create(wasm, file_contents, obj.path, null)); | |
| 741 | } | |
| 945 | pub const FunctionImport = extern struct { | |
| 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` | |
| 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); | |
| 966 | const first_object_function = @intFromEnum(Resolution.__wasm_init_tls) + 1; | |
| 753 | 967 | |
| 754 | return index; | |
| 755 | } | |
| 968 | pub const Unpacked = union(enum) { | |
| 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 { | |
| 758 | return wasm.managed_atoms.items[@intFromEnum(index)]; | |
| 759 | } | |
| 978 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { | |
| 979 | return switch (r) { | |
| 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 { | |
| 762 | return &wasm.managed_atoms.items[@intFromEnum(index)]; | |
| 763 | } | |
| 988 | const zcu_func_index = if (object_function_index < wasm.object_functions.items.len) | |
| 989 | return .{ .object_function = @enumFromInt(object_function_index) } | |
| 990 | else | |
| 991 | object_function_index - wasm.object_functions.items.len; | |
| 764 | 992 | |
| 765 | fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void { | |
| 766 | const gpa = wasm.base.comp.gpa; | |
| 993 | return .{ .zcu_func = @enumFromInt(zcu_func_index) }; | |
| 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(); | |
| 771 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; | |
| 1010 | pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution { | |
| 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); | |
| 774 | var keep_file_contents = false; | |
| 775 | defer if (!keep_file_contents) gpa.free(file_contents); | |
| 1016 | pub fn fromZcuFunc(wasm: *const Wasm, i: ZcuFunc.Index) Resolution { | |
| 1017 | return pack(wasm, .{ .zcu_func = i }); | |
| 1018 | } | |
| 776 | 1019 | |
| 777 | const n = try obj.file.preadAll(file_contents, 0); | |
| 778 | if (n != file_contents.len) return error.UnexpectedEndOfFile; | |
| 1020 | pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { | |
| 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) { | |
| 783 | errdefer archive.deinit(gpa); | |
| 784 | try wasm.lazy_archives.append(gpa, .{ | |
| 785 | .path = .{ | |
| 786 | .root_dir = obj.path.root_dir, | |
| 787 | .sub_path = try gpa.dupe(u8, obj.path.sub_path), | |
| 788 | }, | |
| 789 | .file_contents = file_contents, | |
| 790 | .archive = archive, | |
| 791 | }); | |
| 792 | keep_file_contents = true; | |
| 793 | return; | |
| 794 | } | |
| 1028 | pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool { | |
| 1029 | return switch (r.unpack(wasm)) { | |
| 1030 | .unresolved, .zcu_func => true, | |
| 1031 | else => false, | |
| 1032 | }; | |
| 1033 | } | |
| 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 | |
| 799 | // We loop over all symbols, and then group them by offset as the offset | |
| 800 | // notates where the object file starts. | |
| 801 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); | |
| 802 | defer offsets.deinit(); | |
| 803 | for (archive.toc.values()) |symbol_offsets| { | |
| 804 | for (symbol_offsets.items) |sym_offset| { | |
| 805 | try offsets.put(sym_offset, {}); | |
| 1048 | pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { | |
| 1049 | return switch (unpack(r, wasm)) { | |
| 1050 | .unresolved => unreachable, | |
| 1051 | .__wasm_apply_global_tls_relocs => @tagName(Unpacked.__wasm_apply_global_tls_relocs), | |
| 1052 | .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors), | |
| 1053 | .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory), | |
| 1054 | .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls), | |
| 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| { | |
| 810 | const object = try archive.parseObject(wasm, file_contents[file_offset..], obj.path); | |
| 811 | try wasm.objects.append(gpa, object); | |
| 812 | } | |
| 813 | } | |
| 1061 | /// Index into `object_function_imports`. | |
| 1062 | pub const Index = enum(u32) { | |
| 1063 | _, | |
| 814 | 1064 | |
| 815 | fn requiresTLSReloc(wasm: *const Wasm) bool { | |
| 816 | for (wasm.got_symbols.items) |loc| { | |
| 817 | if (wasm.symbolLocSymbol(loc).isTLS()) { | |
| 818 | return true; | |
| 1065 | pub fn key(index: Index, wasm: *const Wasm) *String { | |
| 1066 | return &wasm.object_function_imports.keys()[@intFromEnum(index)]; | |
| 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 { | |
| 830 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items; | |
| 831 | return obj.symtable; | |
| 832 | } | |
| 1069 | pub fn value(index: Index, wasm: *const Wasm) *FunctionImport { | |
| 1070 | return &wasm.object_function_imports.values()[@intFromEnum(index)]; | |
| 1071 | } | |
| 833 | 1072 | |
| 834 | fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol { | |
| 835 | const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)]; | |
| 836 | return &obj.symtable[@intFromEnum(index)]; | |
| 837 | } | |
| 1073 | pub fn symbolName(index: Index, wasm: *const Wasm) String { | |
| 1074 | return index.key(wasm).*; | |
| 1075 | } | |
| 838 | 1076 | |
| 839 | fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func { | |
| 840 | const obj = wasm.objectById(object_id) orelse { | |
| 841 | const zo = wasm.zig_object.?; | |
| 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 | } | |
| 1077 | pub fn importName(index: Index, wasm: *const Wasm) String { | |
| 1078 | return index.value(wasm).name; | |
| 1079 | } | |
| 848 | 1080 | |
| 849 | fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 { | |
| 850 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count; | |
| 851 | return obj.imported_functions_count; | |
| 852 | } | |
| 1081 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { | |
| 1082 | return index.value(wasm).module_name; | |
| 1083 | } | |
| 853 | 1084 | |
| 854 | fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global { | |
| 855 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items; | |
| 856 | return obj.globals; | |
| 857 | } | |
| 1085 | pub fn functionType(index: Index, wasm: *const Wasm) FunctionType.Index { | |
| 1086 | return value(index, wasm).type; | |
| 1087 | } | |
| 1088 | }; | |
| 1089 | }; | |
| 858 | 1090 | |
| 859 | fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type { | |
| 860 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items; | |
| 861 | return obj.func_types; | |
| 862 | } | |
| 1091 | pub const ObjectFunction = extern struct { | |
| 1092 | flags: SymbolFlags, | |
| 1093 | /// `none` if this function has no symbol describing it. | |
| 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 { | |
| 865 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items; | |
| 866 | return obj.segment_info; | |
| 867 | } | |
| 1102 | pub const Code = DataPayload; | |
| 868 | 1103 | |
| 869 | /// For a given symbol index, find its corresponding import. | |
| 870 | /// Asserts import exists. | |
| 871 | fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import { | |
| 872 | const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?; | |
| 873 | return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]); | |
| 874 | } | |
| 1104 | pub fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice { | |
| 1105 | const code_section_index = of.object_index.ptr(wasm).code_section_index.?; | |
| 1106 | const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty; | |
| 1107 | return .init(relocs, of.offset, of.code.len, wasm); | |
| 1108 | } | |
| 1109 | }; | |
| 875 | 1110 | |
| 876 | /// Returns the object element pointer, or null if it is the ZigObject. | |
| 877 | fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object { | |
| 878 | if (object_id == .zig_object) return null; | |
| 879 | return &wasm.objects.items[@intFromEnum(object_id)]; | |
| 880 | } | |
| 1111 | pub const GlobalImport = extern struct { | |
| 1112 | flags: SymbolFlags, | |
| 1113 | module_name: OptionalString, | |
| 1114 | /// May be different than the key which is a symbol name. | |
| 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 { | |
| 883 | const gpa = wasm.base.comp.gpa; | |
| 884 | const diags = &wasm.base.comp.link_diags; | |
| 885 | const obj_path = objectPath(wasm, object_id); | |
| 886 | log.debug("Resolving symbols in object: '{'}'", .{obj_path}); | |
| 887 | const symbols = objectSymbols(wasm, object_id); | |
| 888 | ||
| 889 | for (symbols, 0..) |symbol, i| { | |
| 890 | const sym_index: Symbol.Index = @enumFromInt(i); | |
| 891 | const location: SymbolLoc = .{ | |
| 892 | .file = object_id.toOptional(), | |
| 893 | .index = sym_index, | |
| 1133 | const first_object_global = @intFromEnum(Resolution.__tls_size) + 1; | |
| 1134 | ||
| 1135 | pub const Unpacked = union(enum) { | |
| 1136 | unresolved, | |
| 1137 | __heap_base, | |
| 1138 | __heap_end, | |
| 1139 | __stack_pointer, | |
| 1140 | __tls_align, | |
| 1141 | __tls_base, | |
| 1142 | __tls_size, | |
| 1143 | object_global: ObjectGlobalIndex, | |
| 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()) { | |
| 898 | if (symbol.isUndefined()) { | |
| 899 | diags.addParseError(obj_path, "local symbol '{s}' references import", .{ | |
| 900 | wasm.stringSlice(symbol.name), | |
| 901 | }); | |
| 902 | } | |
| 903 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); | |
| 904 | continue; | |
| 1148 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { | |
| 1149 | return switch (r) { | |
| 1150 | .unresolved => .unresolved, | |
| 1151 | .__heap_base => .__heap_base, | |
| 1152 | .__heap_end => .__heap_end, | |
| 1153 | .__stack_pointer => .__stack_pointer, | |
| 1154 | .__tls_align => .__tls_align, | |
| 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); | |
| 908 | if (!maybe_existing.found_existing) { | |
| 909 | maybe_existing.value_ptr.* = location; | |
| 910 | try wasm.resolved_symbols.putNoClobber(gpa, location, {}); | |
| 911 | ||
| 912 | if (symbol.isUndefined()) { | |
| 913 | try wasm.undefs.putNoClobber(gpa, symbol.name, location); | |
| 914 | } | |
| 915 | continue; | |
| 1174 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { | |
| 1175 | return switch (unpacked) { | |
| 1176 | .unresolved => .unresolved, | |
| 1177 | .__heap_base => .__heap_base, | |
| 1178 | .__heap_end => .__heap_end, | |
| 1179 | .__stack_pointer => .__stack_pointer, | |
| 1180 | .__tls_align => .__tls_align, | |
| 1181 | .__tls_base => .__tls_base, | |
| 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.*; | |
| 919 | const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc); | |
| 920 | const existing_file_path: Path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else .{ | |
| 921 | .root_dir = std.Build.Cache.Directory.cwd(), | |
| 922 | .sub_path = wasm.name, | |
| 923 | }; | |
| 924 | ||
| 925 | if (!existing_sym.isUndefined()) outer: { | |
| 926 | if (!symbol.isUndefined()) inner: { | |
| 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 | } | |
| 1189 | pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution { | |
| 1190 | const comp = wasm.base.comp; | |
| 1191 | const is_obj = comp.config.output_mode == .Obj; | |
| 1192 | return pack(wasm, if (is_obj) .{ | |
| 1193 | .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(ip_nav).?), | |
| 1194 | } else .{ | |
| 1195 | .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(ip_nav).?), | |
| 1196 | }); | |
| 1197 | } | |
| 939 | 1198 | |
| 940 | try wasm.discarded.put(gpa, location, existing_loc); | |
| 941 | continue; // Do not overwrite defined symbols with undefined symbols | |
| 1199 | pub fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution { | |
| 1200 | return pack(wasm, .{ .object_global = object_global }); | |
| 942 | 1201 | } |
| 943 | 1202 | |
| 944 | if (symbol.tag != existing_sym.tag) { | |
| 945 | var err = try diags.addErrorWithNotes(2); | |
| 946 | try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ | |
| 947 | wasm.stringSlice(symbol.name), @tagName(symbol.tag), @tagName(existing_sym.tag), | |
| 948 | }); | |
| 949 | try err.addNote("first definition in '{'}'", .{existing_file_path}); | |
| 950 | try err.addNote("next definition in '{'}'", .{obj_path}); | |
| 1203 | pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { | |
| 1204 | return switch (unpack(r, wasm)) { | |
| 1205 | .unresolved => unreachable, | |
| 1206 | .__heap_base => @tagName(Unpacked.__heap_base), | |
| 1207 | .__heap_end => @tagName(Unpacked.__heap_end), | |
| 1208 | .__stack_pointer => @tagName(Unpacked.__stack_pointer), | |
| 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()) { | |
| 954 | // only verify module/import name for function symbols | |
| 955 | if (symbol.tag == .function) { | |
| 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 | } | |
| 1219 | /// Index into `Wasm.object_global_imports`. | |
| 1220 | pub const Index = enum(u32) { | |
| 1221 | _, | |
| 973 | 1222 | |
| 974 | // both undefined so skip overwriting existing symbol and discard the new symbol | |
| 975 | try wasm.discarded.put(gpa, location, existing_loc); | |
| 976 | continue; | |
| 1223 | pub fn key(index: Index, wasm: *const Wasm) *String { | |
| 1224 | return &wasm.object_global_imports.keys()[@intFromEnum(index)]; | |
| 977 | 1225 | } |
| 978 | 1226 | |
| 979 | if (existing_sym.tag == .global) { | |
| 980 | const existing_ty = wasm.getGlobalType(existing_loc); | |
| 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 | } | |
| 1227 | pub fn value(index: Index, wasm: *const Wasm) *GlobalImport { | |
| 1228 | return &wasm.object_global_imports.values()[@intFromEnum(index)]; | |
| 988 | 1229 | } |
| 989 | 1230 | |
| 990 | if (existing_sym.tag == .function) { | |
| 991 | const existing_ty = wasm.getFunctionSignature(existing_loc); | |
| 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 | } | |
| 1231 | pub fn symbolName(index: Index, wasm: *const Wasm) String { | |
| 1232 | return index.key(wasm).*; | |
| 1000 | 1233 | } |
| 1001 | 1234 | |
| 1002 | // when both symbols are weak, we skip overwriting unless the existing | |
| 1003 | // symbol is weak and the new one isn't, in which case we *do* overwrite it. | |
| 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; | |
| 1235 | pub fn importName(index: Index, wasm: *const Wasm) String { | |
| 1236 | return index.value(wasm).name; | |
| 1008 | 1237 | } |
| 1009 | 1238 | |
| 1010 | // simply overwrite with the new symbol | |
| 1011 | log.debug("Overwriting symbol '{s}'", .{wasm.stringSlice(symbol.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); | |
| 1239 | pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString { | |
| 1240 | return index.value(wasm).module_name; | |
| 1021 | 1241 | } |
| 1022 | } | |
| 1023 | } | |
| 1024 | 1242 | |
| 1025 | fn resolveSymbolsInArchives(wasm: *Wasm) !void { | |
| 1026 | if (wasm.lazy_archives.items.len == 0) return; | |
| 1027 | const gpa = wasm.base.comp.gpa; | |
| 1028 | const diags = &wasm.base.comp.link_diags; | |
| 1243 | pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type { | |
| 1244 | return value(index, wasm).type(); | |
| 1245 | } | |
| 1246 | }; | |
| 1029 | 1247 | |
| 1030 | log.debug("Resolving symbols in lazy_archives", .{}); | |
| 1031 | var index: u32 = 0; | |
| 1032 | undef_loop: while (index < wasm.undefs.count()) { | |
| 1033 | const sym_name_index = wasm.undefs.keys()[index]; | |
| 1248 | pub fn @"type"(gi: *const GlobalImport) ObjectGlobal.Type { | |
| 1249 | return gi.flags.global_type.to(); | |
| 1250 | } | |
| 1251 | }; | |
| 1034 | 1252 | |
| 1035 | for (wasm.lazy_archives.items) |lazy_archive| { | |
| 1036 | const sym_name = wasm.stringSlice(sym_name_index); | |
| 1037 | log.debug("Detected symbol '{s}' in archive '{'}', parsing objects..", .{ | |
| 1038 | sym_name, lazy_archive.path, | |
| 1039 | }); | |
| 1040 | const offset = lazy_archive.archive.toc.get(sym_name) orelse continue; // symbol does not exist in this archive | |
| 1041 | ||
| 1042 | // Symbol is found in unparsed object file within current archive. | |
| 1043 | // Parse object and and resolve symbols again before we check remaining | |
| 1044 | // undefined symbols. | |
| 1045 | const file_contents = lazy_archive.file_contents[offset.items[0]..]; | |
| 1046 | const object = lazy_archive.archive.parseObject(wasm, file_contents, lazy_archive.path) catch |err| { | |
| 1047 | // TODO this fails to include information to identify which object failed | |
| 1048 | return diags.failParse(lazy_archive.path, "failed to parse object in archive: {s}", .{@errorName(err)}); | |
| 1049 | }; | |
| 1050 | try wasm.objects.append(gpa, object); | |
| 1051 | try wasm.resolveSymbolsInObject(@enumFromInt(wasm.objects.items.len - 1)); | |
| 1253 | pub const ObjectGlobal = extern struct { | |
| 1254 | /// `none` if this function has no symbol describing it. | |
| 1255 | name: OptionalString, | |
| 1256 | flags: SymbolFlags, | |
| 1257 | expr: Expr, | |
| 1258 | /// The object file whose global section contains this global. | |
| 1259 | object_index: ObjectIndex, | |
| 1260 | offset: u32, | |
| 1261 | size: u32, | |
| 1052 | 1262 | |
| 1053 | // continue loop for any remaining undefined symbols that still exist | |
| 1054 | // after resolving last object file | |
| 1055 | continue :undef_loop; | |
| 1056 | } | |
| 1057 | index += 1; | |
| 1263 | pub fn @"type"(og: *const ObjectGlobal) Type { | |
| 1264 | return og.flags.global_type.to(); | |
| 1058 | 1265 | } |
| 1059 | } | |
| 1060 | 1266 | |
| 1061 | /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. | |
| 1062 | fn writeI32Const(writer: anytype, val: u32) !void { | |
| 1063 | try writer.writeByte(std.wasm.opcode(.i32_const)); | |
| 1064 | try leb.writeIleb128(writer, @as(i32, @bitCast(val))); | |
| 1065 | } | |
| 1066 | ||
| 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; | |
| 1267 | pub const Type = struct { | |
| 1268 | valtype: std.wasm.Valtype, | |
| 1269 | mutable: bool, | |
| 1270 | }; | |
| 1072 | 1271 | |
| 1073 | // Passive segments are used to avoid memory being reinitialized on each | |
| 1074 | // thread's instantiation. These passive segments are initialized and | |
| 1075 | // dropped in __wasm_init_memory, which is registered as the start function | |
| 1076 | // We also initialize bss segments (using memory.fill) as part of this | |
| 1077 | // function. | |
| 1078 | if (!wasm.hasPassiveInitializationSegments()) { | |
| 1079 | return; | |
| 1272 | pub fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice { | |
| 1273 | const global_section_index = og.object_index.ptr(wasm).global_section_index.?; | |
| 1274 | const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty; | |
| 1275 | return .init(relocs, og.offset, og.size, wasm); | |
| 1080 | 1276 | } |
| 1081 | const sym_loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory, .function); | |
| 1082 | wasm.symbolLocSymbol(sym_loc).mark(); | |
| 1083 | ||
| 1084 | const flag_address: u32 = if (shared_memory) address: { | |
| 1085 | // when we have passive initialization segments and shared memory | |
| 1086 | // `setupMemory` will create this symbol and set its virtual address. | |
| 1087 | const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory_flag).?; | |
| 1088 | break :address wasm.symbolLocSymbol(loc).virtual_address; | |
| 1089 | } else 0; | |
| 1090 | ||
| 1091 | var function_body = std.ArrayList(u8).init(gpa); | |
| 1092 | defer function_body.deinit(); | |
| 1093 | const writer = function_body.writer(); | |
| 1094 | ||
| 1095 | // we have 0 locals | |
| 1096 | try leb.writeUleb128(writer, @as(u32, 0)); | |
| 1097 | ||
| 1098 | if (shared_memory) { | |
| 1099 | // destination blocks | |
| 1100 | // based on values we jump to corresponding label | |
| 1101 | try writer.writeByte(std.wasm.opcode(.block)); // $drop | |
| 1102 | try writer.writeByte(std.wasm.block_empty); // block type | |
| 1103 | ||
| 1104 | try writer.writeByte(std.wasm.opcode(.block)); // $wait | |
| 1105 | try writer.writeByte(std.wasm.block_empty); // block type | |
| 1106 | ||
| 1107 | try writer.writeByte(std.wasm.opcode(.block)); // $init | |
| 1108 | try writer.writeByte(std.wasm.block_empty); // block type | |
| 1109 | ||
| 1110 | // atomically check | |
| 1111 | try writeI32Const(writer, flag_address); | |
| 1112 | try writeI32Const(writer, 0); | |
| 1113 | try writeI32Const(writer, 1); | |
| 1114 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); | |
| 1115 | try leb.writeUleb128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg)); | |
| 1116 | try leb.writeUleb128(writer, @as(u32, 2)); // alignment | |
| 1117 | try leb.writeUleb128(writer, @as(u32, 0)); // offset | |
| 1118 | ||
| 1119 | // based on the value from the atomic check, jump to the label. | |
| 1120 | try writer.writeByte(std.wasm.opcode(.br_table)); | |
| 1121 | try leb.writeUleb128(writer, @as(u32, 2)); // length of the table (we have 3 blocks but because of the mandatory default the length is 2). | |
| 1122 | try leb.writeUleb128(writer, @as(u32, 0)); // $init | |
| 1123 | try leb.writeUleb128(writer, @as(u32, 1)); // $wait | |
| 1124 | try leb.writeUleb128(writer, @as(u32, 2)); // $drop | |
| 1125 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 1126 | } | |
| 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 | } | |
| 1277 | }; | |
| 1147 | 1278 | |
| 1148 | try writeI32Const(writer, 0); | |
| 1149 | try writeI32Const(writer, segment.size); | |
| 1150 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | |
| 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 | } | |
| 1279 | pub const RefType1 = enum(u1) { | |
| 1280 | funcref, | |
| 1281 | externref, | |
| 1212 | 1282 | |
| 1213 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | |
| 1214 | try leb.writeUleb128(writer, std.wasm.miscOpcode(.data_drop)); | |
| 1215 | try leb.writeUleb128(writer, segment_index); | |
| 1216 | } | |
| 1283 | pub fn from(rt: std.wasm.RefType) RefType1 { | |
| 1284 | return switch (rt) { | |
| 1285 | .funcref => .funcref, | |
| 1286 | .externref => .externref, | |
| 1287 | }; | |
| 1217 | 1288 | } |
| 1218 | 1289 | |
| 1219 | // End of the function body | |
| 1220 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 1290 | pub fn to(rt: RefType1) std.wasm.RefType { | |
| 1291 | return switch (rt) { | |
| 1292 | .funcref => .funcref, | |
| 1293 | .externref => .externref, | |
| 1294 | }; | |
| 1295 | } | |
| 1296 | }; | |
| 1221 | 1297 | |
| 1222 | try wasm.createSyntheticFunction( | |
| 1223 | wasm.preloaded_strings.__wasm_init_memory, | |
| 1224 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, | |
| 1225 | &function_body, | |
| 1226 | ); | |
| 1227 | } | |
| 1298 | pub const TableImport = extern struct { | |
| 1299 | flags: SymbolFlags, | |
| 1300 | module_name: String, | |
| 1301 | /// May be different than the key which is a symbol name. | |
| 1302 | name: String, | |
| 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 | |
| 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; | |
| 1315 | const first_object_table = @intFromEnum(Resolution.__indirect_function_table) + 1; | |
| 1235 | 1316 | |
| 1236 | // When we have TLS GOT entries and shared memory is enabled, | |
| 1237 | // we must perform runtime relocations or else we don't create the function. | |
| 1238 | if (!shared_memory or !wasm.requiresTLSReloc()) { | |
| 1239 | return; | |
| 1240 | } | |
| 1317 | pub const Unpacked = union(enum) { | |
| 1318 | unresolved, | |
| 1319 | __indirect_function_table, | |
| 1320 | object_table: ObjectTableIndex, | |
| 1321 | }; | |
| 1241 | 1322 | |
| 1242 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_apply_global_tls_relocs, .function); | |
| 1243 | wasm.symbolLocSymbol(loc).mark(); | |
| 1244 | var function_body = std.ArrayList(u8).init(gpa); | |
| 1245 | defer function_body.deinit(); | |
| 1246 | const writer = function_body.writer(); | |
| 1247 | ||
| 1248 | // locals (we have none) | |
| 1249 | try writer.writeByte(0); | |
| 1250 | for (wasm.got_symbols.items, 0..) |got_loc, got_index| { | |
| 1251 | const sym: *Symbol = wasm.symbolLocSymbol(got_loc); | |
| 1252 | if (!sym.isTLS()) continue; // only relocate TLS symbols | |
| 1253 | if (sym.tag == .data and sym.isDefined()) { | |
| 1254 | // get __tls_base | |
| 1255 | try writer.writeByte(std.wasm.opcode(.global_get)); | |
| 1256 | try leb.writeUleb128(writer, wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__tls_base).?).index); | |
| 1257 | ||
| 1258 | // add the virtual address of the symbol | |
| 1259 | try writer.writeByte(std.wasm.opcode(.i32_const)); | |
| 1260 | try leb.writeUleb128(writer, sym.virtual_address); | |
| 1261 | } else if (sym.tag == .function) { | |
| 1262 | @panic("TODO: relocate GOT entry of function"); | |
| 1263 | } else continue; | |
| 1264 | ||
| 1265 | try writer.writeByte(std.wasm.opcode(.i32_add)); | |
| 1266 | try writer.writeByte(std.wasm.opcode(.global_set)); | |
| 1267 | try leb.writeUleb128(writer, wasm.imported_globals_count + @as(u32, @intCast(wasm.wasm_globals.items.len + got_index))); | |
| 1268 | } | |
| 1269 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 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 | } | |
| 1323 | pub fn unpack(r: Resolution) Unpacked { | |
| 1324 | return switch (r) { | |
| 1325 | .unresolved => .unresolved, | |
| 1326 | .__indirect_function_table => .__indirect_function_table, | |
| 1327 | _ => .{ .object_table = @enumFromInt(@intFromEnum(r) - first_object_table) }, | |
| 1328 | }; | |
| 1329 | } | |
| 1277 | 1330 | |
| 1278 | fn validateFeatures( | |
| 1279 | wasm: *const Wasm, | |
| 1280 | to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool, | |
| 1281 | emit_features_count: *u32, | |
| 1282 | ) !void { | |
| 1283 | const comp = wasm.base.comp; | |
| 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 | } | |
| 1331 | fn pack(unpacked: Unpacked) Resolution { | |
| 1332 | return switch (unpacked) { | |
| 1333 | .unresolved => .unresolved, | |
| 1334 | .__indirect_function_table => .__indirect_function_table, | |
| 1335 | .object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)), | |
| 1336 | }; | |
| 1309 | 1337 | } |
| 1310 | } | |
| 1311 | 1338 | |
| 1312 | // extract all the used, disallowed and required features from each | |
| 1313 | // linked object file so we can test them. | |
| 1314 | for (wasm.objects.items, 0..) |*object, file_index| { | |
| 1315 | for (object.features) |feature| { | |
| 1316 | const value = (@as(u16, @intCast(file_index)) << 1) | 1; | |
| 1317 | switch (feature.prefix) { | |
| 1318 | .used => { | |
| 1319 | used[@intFromEnum(feature.tag)] = value; | |
| 1320 | }, | |
| 1321 | .disallowed => { | |
| 1322 | disallowed[@intFromEnum(feature.tag)] = value; | |
| 1323 | }, | |
| 1324 | .required => { | |
| 1325 | required[@intFromEnum(feature.tag)] = value; | |
| 1326 | used[@intFromEnum(feature.tag)] = value; | |
| 1327 | }, | |
| 1328 | } | |
| 1339 | fn fromObjectTable(object_table: ObjectTableIndex) Resolution { | |
| 1340 | return pack(.{ .object_table = object_table }); | |
| 1329 | 1341 | } |
| 1330 | 1342 | |
| 1331 | for (object.segment_info) |segment| { | |
| 1332 | if (segment.isTLS()) { | |
| 1333 | has_tls = true; | |
| 1334 | } | |
| 1343 | pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType { | |
| 1344 | return switch (unpack(r)) { | |
| 1345 | .unresolved => unreachable, | |
| 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 | |
| 1339 | // and insert it into the 'allowed' set. When features are not inferred, | |
| 1340 | // we validate that a used feature is allowed. | |
| 1341 | for (used, 0..) |used_set, used_index| { | |
| 1342 | const is_enabled = @as(u1, @truncate(used_set)) != 0; | |
| 1343 | if (infer) { | |
| 1344 | allowed[used_index] = is_enabled; | |
| 1345 | emit_features_count.* += @intFromBool(is_enabled); | |
| 1346 | } else if (is_enabled and !allowed[used_index]) { | |
| 1347 | diags.addParseError( | |
| 1348 | wasm.objects.items[used_set >> 1].path, | |
| 1349 | "feature '{}' not allowed, but used by linked object", | |
| 1350 | .{@as(Feature.Tag, @enumFromInt(used_index))}, | |
| 1351 | ); | |
| 1352 | valid_feature_set = false; | |
| 1351 | pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits { | |
| 1352 | return switch (unpack(r)) { | |
| 1353 | .unresolved => unreachable, | |
| 1354 | .__indirect_function_table => .{ | |
| 1355 | .flags = .{ .has_max = true, .is_shared = false }, | |
| 1356 | .min = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1), | |
| 1357 | .max = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1), | |
| 1358 | }, | |
| 1359 | .object_table => |i| i.ptr(wasm).limits(), | |
| 1360 | }; | |
| 1353 | 1361 | } |
| 1354 | } | |
| 1362 | }; | |
| 1355 | 1363 | |
| 1356 | if (!valid_feature_set) { | |
| 1357 | return error.FlushFailure; | |
| 1358 | } | |
| 1364 | /// Index into `object_table_imports`. | |
| 1365 | pub const Index = enum(u32) { | |
| 1366 | _, | |
| 1359 | 1367 | |
| 1360 | if (shared_memory) { | |
| 1361 | const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)]; | |
| 1362 | if (@as(u1, @truncate(disallowed_feature)) != 0) { | |
| 1363 | diags.addParseError( | |
| 1364 | wasm.objects.items[disallowed_feature >> 1].path, | |
| 1365 | "shared-memory is disallowed because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled", | |
| 1366 | .{}, | |
| 1367 | ); | |
| 1368 | valid_feature_set = false; | |
| 1368 | pub fn key(index: Index, wasm: *const Wasm) *String { | |
| 1369 | return &wasm.object_table_imports.keys()[@intFromEnum(index)]; | |
| 1369 | 1370 | } |
| 1370 | 1371 | |
| 1371 | for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| { | |
| 1372 | if (!allowed[@intFromEnum(feature)]) { | |
| 1373 | var err = try diags.addErrorWithNotes(0); | |
| 1374 | try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature}); | |
| 1375 | } | |
| 1372 | pub fn value(index: Index, wasm: *const Wasm) *TableImport { | |
| 1373 | return &wasm.object_table_imports.values()[@intFromEnum(index)]; | |
| 1376 | 1374 | } |
| 1377 | } | |
| 1378 | 1375 | |
| 1379 | if (has_tls) { | |
| 1380 | for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| { | |
| 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 | } | |
| 1376 | pub fn name(index: Index, wasm: *const Wasm) String { | |
| 1377 | return index.key(wasm).*; | |
| 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 | |
| 1406 | for (required, 0..) |required_feature, feature_index| { | |
| 1407 | const is_required = @as(u1, @truncate(required_feature)) != 0; | |
| 1408 | if (is_required and !object_used_features[feature_index]) { | |
| 1409 | var err = try diags.addErrorWithNotes(2); | |
| 1410 | try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))}); | |
| 1411 | try err.addNote("required by '{'}'", .{wasm.objects.items[required_feature >> 1].path}); | |
| 1412 | try err.addNote("missing in '{'}'", .{object.path}); | |
| 1413 | valid_feature_set = false; | |
| 1414 | } | |
| 1415 | } | |
| 1385 | pub fn limits(ti: *const TableImport) std.wasm.Limits { | |
| 1386 | return .{ | |
| 1387 | .flags = .{ | |
| 1388 | .has_max = ti.flags.limits_has_max, | |
| 1389 | .is_shared = ti.flags.limits_is_shared, | |
| 1390 | }, | |
| 1391 | .min = ti.limits_min, | |
| 1392 | .max = ti.limits_max, | |
| 1393 | }; | |
| 1416 | 1394 | } |
| 1395 | }; | |
| 1417 | 1396 | |
| 1418 | if (!valid_feature_set) { | |
| 1419 | return error.FlushFailure; | |
| 1420 | } | |
| 1397 | pub const Table = extern struct { | |
| 1398 | module_name: OptionalString, | |
| 1399 | name: OptionalString, | |
| 1400 | flags: SymbolFlags, | |
| 1401 | limits_min: u32, | |
| 1402 | limits_max: u32, | |
| 1421 | 1403 | |
| 1422 | to_emit.* = allowed; | |
| 1423 | } | |
| 1404 | pub fn limits(t: *const Table) std.wasm.Limits { | |
| 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 | |
| 1426 | /// any object file. For instance, the `__heap_base` symbol will only be created, | |
| 1427 | /// if one or multiple undefined references exist. When none exist, the symbol will | |
| 1428 | /// not be created, ensuring we don't unnecessarily emit unreferenced symbols. | |
| 1429 | fn resolveLazySymbols(wasm: *Wasm) !void { | |
| 1430 | const comp = wasm.base.comp; | |
| 1431 | const gpa = comp.gpa; | |
| 1432 | const shared_memory = comp.config.shared_memory; | |
| 1416 | /// Uniquely identifies a section across all objects. By subtracting | |
| 1417 | /// `Object.local_section_index_base` from this one, the Object section index | |
| 1418 | /// is obtained. | |
| 1419 | pub const ObjectSectionIndex = enum(u32) { | |
| 1420 | _, | |
| 1421 | }; | |
| 1433 | 1422 | |
| 1434 | if (wasm.getExistingString("__heap_base")) |name_offset| { | |
| 1435 | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { | |
| 1436 | const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); | |
| 1437 | try wasm.discarded.putNoClobber(gpa, kv.value, loc); | |
| 1438 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. | |
| 1439 | } | |
| 1440 | } | |
| 1423 | /// Index into `object_tables`. | |
| 1424 | pub const ObjectTableIndex = enum(u32) { | |
| 1425 | _, | |
| 1441 | 1426 | |
| 1442 | if (wasm.getExistingString("__heap_end")) |name_offset| { | |
| 1443 | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { | |
| 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 | } | |
| 1427 | pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table { | |
| 1428 | return &wasm.object_tables.items[@intFromEnum(index)]; | |
| 1448 | 1429 | } |
| 1449 | 1430 | |
| 1450 | if (!shared_memory) { | |
| 1451 | if (wasm.getExistingString("__tls_base")) |name_offset| { | |
| 1452 | if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { | |
| 1453 | const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global); | |
| 1454 | try wasm.discarded.putNoClobber(gpa, kv.value, loc); | |
| 1455 | _ = wasm.resolved_symbols.swapRemove(kv.value); | |
| 1456 | const symbol = wasm.symbolLocSymbol(loc); | |
| 1457 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | |
| 1458 | symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); | |
| 1459 | try wasm.wasm_globals.append(gpa, .{ | |
| 1460 | .global_type = .{ .valtype = .i32, .mutable = true }, | |
| 1461 | .init = .{ .i32_const = undefined }, | |
| 1462 | }); | |
| 1463 | } | |
| 1464 | } | |
| 1431 | pub fn chaseWeak(i: ObjectTableIndex, wasm: *const Wasm) ObjectTableIndex { | |
| 1432 | const table = ptr(i, wasm); | |
| 1433 | if (table.flags.binding != .weak) return i; | |
| 1434 | const name = table.name.unwrap().?; | |
| 1435 | const import = wasm.object_table_imports.getPtr(name).?; | |
| 1436 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. | |
| 1437 | return import.resolution.unpack().object_table; | |
| 1465 | 1438 | } |
| 1466 | } | |
| 1439 | }; | |
| 1467 | 1440 | |
| 1468 | pub fn findGlobalSymbol(wasm: *const Wasm, name: []const u8) ?SymbolLoc { | |
| 1469 | const name_index = wasm.getExistingString(name) orelse return null; | |
| 1470 | return wasm.globals.get(name_index); | |
| 1471 | } | |
| 1441 | /// Index into `Wasm.object_globals`. | |
| 1442 | pub const ObjectGlobalIndex = enum(u32) { | |
| 1443 | _, | |
| 1472 | 1444 | |
| 1473 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { | |
| 1474 | const comp = wasm.base.comp; | |
| 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; | |
| 1445 | pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal { | |
| 1446 | return &wasm.object_globals.items[@intFromEnum(index)]; | |
| 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| { | |
| 1511 | func_type.deinit(gpa); | |
| 1512 | } | |
| 1513 | for (wasm.segment_info.values()) |segment_info| { | |
| 1514 | gpa.free(segment_info.name); | |
| 1515 | } | |
| 1516 | if (wasm.zig_object) |zig_obj| { | |
| 1517 | zig_obj.deinit(wasm); | |
| 1518 | } | |
| 1519 | for (wasm.objects.items) |*object| { | |
| 1520 | object.deinit(gpa); | |
| 1449 | pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString { | |
| 1450 | return index.ptr(wasm).name; | |
| 1521 | 1451 | } |
| 1522 | 1452 | |
| 1523 | for (wasm.lazy_archives.items) |*lazy_archive| lazy_archive.deinit(gpa); | |
| 1524 | wasm.lazy_archives.deinit(gpa); | |
| 1525 | ||
| 1526 | if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls)) |loc| { | |
| 1527 | const atom = wasm.symbol_atom.get(loc).?; | |
| 1528 | wasm.getAtomPtr(atom).deinit(gpa); | |
| 1453 | pub fn chaseWeak(i: ObjectGlobalIndex, wasm: *const Wasm) ObjectGlobalIndex { | |
| 1454 | const global = ptr(i, wasm); | |
| 1455 | if (global.flags.binding != .weak) return i; | |
| 1456 | const import_name = global.name.unwrap().?; | |
| 1457 | const import = wasm.object_global_imports.getPtr(import_name).?; | |
| 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); | |
| 1532 | wasm.globals.deinit(gpa); | |
| 1533 | wasm.resolved_symbols.deinit(gpa); | |
| 1534 | wasm.undefs.deinit(gpa); | |
| 1535 | wasm.discarded.deinit(gpa); | |
| 1536 | wasm.symbol_atom.deinit(gpa); | |
| 1537 | wasm.atoms.deinit(gpa); | |
| 1538 | wasm.managed_atoms.deinit(gpa); | |
| 1539 | wasm.segments.deinit(gpa); | |
| 1540 | wasm.data_segments.deinit(gpa); | |
| 1541 | wasm.segment_info.deinit(gpa); | |
| 1542 | wasm.objects.deinit(gpa); | |
| 1463 | pub const ObjectMemory = extern struct { | |
| 1464 | flags: SymbolFlags, | |
| 1465 | name: OptionalString, | |
| 1466 | limits_min: u32, | |
| 1467 | limits_max: u32, | |
| 1543 | 1468 | |
| 1544 | // free output sections | |
| 1545 | wasm.imports.deinit(gpa); | |
| 1546 | wasm.func_types.deinit(gpa); | |
| 1547 | wasm.functions.deinit(gpa); | |
| 1548 | wasm.wasm_globals.deinit(gpa); | |
| 1549 | wasm.function_table.deinit(gpa); | |
| 1550 | wasm.tables.deinit(gpa); | |
| 1551 | wasm.init_funcs.deinit(gpa); | |
| 1552 | wasm.exports.deinit(gpa); | |
| 1469 | /// Index into `Wasm.object_memories`. | |
| 1470 | pub const Index = enum(u32) { | |
| 1471 | _, | |
| 1553 | 1472 | |
| 1554 | wasm.string_bytes.deinit(gpa); | |
| 1555 | wasm.string_table.deinit(gpa); | |
| 1556 | wasm.dump_argv_list.deinit(gpa); | |
| 1557 | } | |
| 1473 | pub fn ptr(index: Index, wasm: *const Wasm) *ObjectMemory { | |
| 1474 | return &wasm.object_memories.items[@intFromEnum(index)]; | |
| 1475 | } | |
| 1476 | }; | |
| 1558 | 1477 | |
| 1559 | pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | |
| 1560 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | |
| 1561 | @panic("Attempted to compile for object format that was disabled by build configuration"); | |
| 1478 | pub fn limits(om: *const ObjectMemory) std.wasm.Limits { | |
| 1479 | return .{ | |
| 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); | |
| 1564 | try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness); | |
| 1565 | } | |
| 1488 | }; | |
| 1566 | 1489 | |
| 1567 | // Generate code for the "Nav", storing it in memory to be later written to | |
| 1568 | // the file on flush(). | |
| 1569 | pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { | |
| 1570 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | |
| 1571 | @panic("Attempted to compile for object format that was disabled by build configuration"); | |
| 1572 | } | |
| 1573 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav); | |
| 1574 | try wasm.zig_object.?.updateNav(wasm, pt, nav); | |
| 1575 | } | |
| 1490 | /// Index into `Wasm.object_functions`. | |
| 1491 | pub const ObjectFunctionIndex = enum(u32) { | |
| 1492 | _, | |
| 1576 | 1493 | |
| 1577 | pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { | |
| 1578 | if (wasm.llvm_object) |_| return; | |
| 1579 | try wasm.zig_object.?.updateLineNumber(pt, ti_id); | |
| 1580 | } | |
| 1494 | pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction { | |
| 1495 | return &wasm.object_functions.items[@intFromEnum(index)]; | |
| 1496 | } | |
| 1581 | 1497 | |
| 1582 | /// From a given symbol location, returns its `wasm.GlobalType`. | |
| 1583 | /// Asserts the Symbol represents a global. | |
| 1584 | fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType { | |
| 1585 | const symbol = wasm.symbolLocSymbol(loc); | |
| 1586 | assert(symbol.tag == .global); | |
| 1587 | const is_undefined = symbol.isUndefined(); | |
| 1588 | switch (loc.file) { | |
| 1589 | .zig_object => { | |
| 1590 | const zo = wasm.zig_object.?; | |
| 1591 | return if (is_undefined) | |
| 1592 | zo.imports.get(loc.index).?.kind.global | |
| 1593 | else | |
| 1594 | zo.globals.items[symbol.index - zo.imported_globals_count].global_type; | |
| 1595 | }, | |
| 1596 | .none => { | |
| 1597 | return if (is_undefined) | |
| 1598 | wasm.imports.get(loc).?.kind.global | |
| 1599 | else | |
| 1600 | wasm.wasm_globals.items[symbol.index].global_type; | |
| 1601 | }, | |
| 1602 | _ => { | |
| 1603 | const obj = &wasm.objects.items[@intFromEnum(loc.file)]; | |
| 1604 | return if (is_undefined) | |
| 1605 | obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.global | |
| 1606 | else | |
| 1607 | obj.globals[symbol.index - obj.imported_globals_count].global_type; | |
| 1608 | }, | |
| 1498 | pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex { | |
| 1499 | const result: OptionalObjectFunctionIndex = @enumFromInt(@intFromEnum(i)); | |
| 1500 | assert(result != .none); | |
| 1501 | return result; | |
| 1609 | 1502 | } |
| 1610 | } | |
| 1611 | 1503 | |
| 1612 | /// From a given symbol location, returns its `wasm.Type`. | |
| 1613 | /// Asserts the Symbol represents a function. | |
| 1614 | fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { | |
| 1615 | const symbol = wasm.symbolLocSymbol(loc); | |
| 1616 | assert(symbol.tag == .function); | |
| 1617 | const is_undefined = symbol.isUndefined(); | |
| 1618 | switch (loc.file) { | |
| 1619 | .zig_object => { | |
| 1620 | const zo = wasm.zig_object.?; | |
| 1621 | if (is_undefined) { | |
| 1622 | const type_index = zo.imports.get(loc.index).?.kind.function; | |
| 1623 | return zo.func_types.items[type_index]; | |
| 1624 | } | |
| 1625 | const sym = zo.symbols.items[@intFromEnum(loc.index)]; | |
| 1626 | const type_index = zo.functions.items[sym.index].type_index; | |
| 1627 | return zo.func_types.items[type_index]; | |
| 1628 | }, | |
| 1629 | .none => { | |
| 1630 | if (is_undefined) { | |
| 1631 | const type_index = wasm.imports.get(loc).?.kind.function; | |
| 1632 | return wasm.func_types.items[type_index]; | |
| 1633 | } | |
| 1634 | return wasm.func_types.items[ | |
| 1635 | wasm.functions.get(.{ | |
| 1636 | .file = .none, | |
| 1637 | .index = symbol.index, | |
| 1638 | }).?.func.type_index | |
| 1639 | ]; | |
| 1640 | }, | |
| 1641 | _ => { | |
| 1642 | const obj = &wasm.objects.items[@intFromEnum(loc.file)]; | |
| 1643 | if (is_undefined) { | |
| 1644 | const type_index = obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.function; | |
| 1645 | return obj.func_types[type_index]; | |
| 1646 | } | |
| 1647 | const sym = obj.symtable[@intFromEnum(loc.index)]; | |
| 1648 | const type_index = obj.functions[sym.index - obj.imported_functions_count].type_index; | |
| 1649 | return obj.func_types[type_index]; | |
| 1650 | }, | |
| 1504 | pub fn chaseWeak(i: ObjectFunctionIndex, wasm: *const Wasm) ObjectFunctionIndex { | |
| 1505 | const func = ptr(i, wasm); | |
| 1506 | if (func.flags.binding != .weak) return i; | |
| 1507 | const name = func.name.unwrap().?; | |
| 1508 | const import = wasm.object_function_imports.getPtr(name).?; | |
| 1509 | assert(import.resolution != .unresolved); // otherwise it should resolve to this one. | |
| 1510 | return import.resolution.unpack(wasm).object_function; | |
| 1651 | 1511 | } |
| 1652 | } | |
| 1512 | }; | |
| 1653 | 1513 | |
| 1654 | /// Returns the symbol index from a symbol of which its flag is set global, | |
| 1655 | /// such as an exported or imported symbol. | |
| 1656 | /// If the symbol does not yet exist, creates a new one symbol instead | |
| 1657 | /// and then returns the index to it. | |
| 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 | } | |
| 1514 | /// Index into `object_functions`, or null. | |
| 1515 | pub const OptionalObjectFunctionIndex = enum(u32) { | |
| 1516 | none = std.math.maxInt(u32), | |
| 1517 | _, | |
| 1663 | 1518 | |
| 1664 | /// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type. | |
| 1665 | /// Returns the given pointer address | |
| 1666 | pub fn getNavVAddr( | |
| 1667 | wasm: *Wasm, | |
| 1668 | pt: Zcu.PerThread, | |
| 1669 | nav: InternPool.Nav.Index, | |
| 1670 | reloc_info: link.File.RelocInfo, | |
| 1671 | ) !u64 { | |
| 1672 | return wasm.zig_object.?.getNavVAddr(wasm, pt, nav, reloc_info); | |
| 1673 | } | |
| 1519 | pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex { | |
| 1520 | if (i == .none) return null; | |
| 1521 | return @enumFromInt(@intFromEnum(i)); | |
| 1522 | } | |
| 1523 | }; | |
| 1674 | 1524 | |
| 1675 | pub fn lowerUav( | |
| 1676 | wasm: *Wasm, | |
| 1677 | pt: Zcu.PerThread, | |
| 1678 | uav: InternPool.Index, | |
| 1679 | explicit_alignment: Alignment, | |
| 1680 | src_loc: Zcu.LazySrcLoc, | |
| 1681 | ) !codegen.GenResult { | |
| 1682 | return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc); | |
| 1683 | } | |
| 1525 | pub const ObjectDataSegment = extern struct { | |
| 1526 | /// `none` if segment info custom subsection is missing. | |
| 1527 | name: OptionalString, | |
| 1528 | flags: Flags, | |
| 1529 | payload: DataPayload, | |
| 1530 | offset: u32, | |
| 1531 | object_index: ObjectIndex, | |
| 1532 | ||
| 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 { | |
| 1686 | return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info); | |
| 1687 | } | |
| 1551 | /// Index into `Wasm.object_data_segments`. | |
| 1552 | pub const Index = enum(u32) { | |
| 1553 | _, | |
| 1688 | 1554 | |
| 1689 | pub fn deleteExport( | |
| 1690 | wasm: *Wasm, | |
| 1691 | exported: Zcu.Exported, | |
| 1692 | name: InternPool.NullTerminatedString, | |
| 1693 | ) void { | |
| 1694 | if (wasm.llvm_object) |_| return; | |
| 1695 | return wasm.zig_object.?.deleteExport(wasm, exported, name); | |
| 1696 | } | |
| 1555 | pub fn ptr(i: Index, wasm: *const Wasm) *ObjectDataSegment { | |
| 1556 | return &wasm.object_data_segments.items[@intFromEnum(i)]; | |
| 1557 | } | |
| 1558 | }; | |
| 1697 | 1559 | |
| 1698 | pub fn updateExports( | |
| 1699 | wasm: *Wasm, | |
| 1700 | pt: Zcu.PerThread, | |
| 1701 | exported: Zcu.Exported, | |
| 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"); | |
| 1560 | pub fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice { | |
| 1561 | const data_section_index = ods.object_index.ptr(wasm).data_section_index.?; | |
| 1562 | const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty; | |
| 1563 | return .init(relocs, ods.offset, ods.payload.len, wasm); | |
| 1706 | 1564 | } |
| 1707 | if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); | |
| 1708 | return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices); | |
| 1709 | } | |
| 1565 | }; | |
| 1710 | 1566 | |
| 1711 | pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { | |
| 1712 | if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); | |
| 1713 | return wasm.zig_object.?.freeDecl(wasm, decl_index); | |
| 1714 | } | |
| 1567 | /// A local or exported global const from an object file. | |
| 1568 | pub const ObjectData = extern struct { | |
| 1569 | segment: ObjectDataSegment.Index, | |
| 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. | |
| 1717 | /// Starts at offset 1, where the value `0` represents an unresolved function pointer | |
| 1718 | /// or null-pointer | |
| 1719 | fn mapFunctionTable(wasm: *Wasm) void { | |
| 1720 | var it = wasm.function_table.iterator(); | |
| 1721 | var index: u32 = 1; | |
| 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); | |
| 1577 | /// Index into `Wasm.object_datas`. | |
| 1578 | pub const Index = enum(u32) { | |
| 1579 | _, | |
| 1580 | ||
| 1581 | pub fn ptr(i: Index, wasm: *const Wasm) *ObjectData { | |
| 1582 | return &wasm.object_datas.items[@intFromEnum(i)]; | |
| 1729 | 1583 | } |
| 1730 | } | |
| 1584 | }; | |
| 1585 | }; | |
| 1731 | 1586 | |
| 1732 | if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) { | |
| 1733 | const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?; | |
| 1734 | const import = wasm.imports.getPtr(sym_loc).?; | |
| 1735 | import.kind.table.limits.min = index - 1; // we start at index 1. | |
| 1736 | } else if (index > 1) { | |
| 1737 | log.debug("Appending indirect function table", .{}); | |
| 1738 | const sym_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?; | |
| 1739 | const symbol = wasm.symbolLocSymbol(sym_loc); | |
| 1740 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; | |
| 1741 | table.limits = .{ .min = index, .max = index, .flags = 0x1 }; | |
| 1742 | } | |
| 1743 | } | |
| 1587 | pub const ObjectDataImport = extern struct { | |
| 1588 | resolution: Resolution, | |
| 1589 | flags: SymbolFlags, | |
| 1590 | source_location: SourceLocation, | |
| 1591 | ||
| 1592 | pub const Resolution = enum(u32) { | |
| 1593 | unresolved, | |
| 1594 | __zig_error_names, | |
| 1595 | __zig_error_name_table, | |
| 1596 | __heap_base, | |
| 1597 | __heap_end, | |
| 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. | |
| 1746 | /// Simply inserts it into the map of atoms when it doesn't exist yet. | |
| 1747 | pub fn appendAtomAtIndex(wasm: *Wasm, index: Segment.Index, atom_index: Atom.Index) !void { | |
| 1748 | const gpa = wasm.base.comp.gpa; | |
| 1749 | const atom = wasm.getAtomPtr(atom_index); | |
| 1750 | if (wasm.atoms.getPtr(index)) |last_index_ptr| { | |
| 1751 | atom.prev = last_index_ptr.*; | |
| 1752 | last_index_ptr.* = atom_index; | |
| 1753 | } else { | |
| 1754 | try wasm.atoms.putNoClobber(gpa, index, atom_index); | |
| 1755 | } | |
| 1756 | } | |
| 1603 | const first_object = @intFromEnum(Resolution.__heap_end) + 1; | |
| 1604 | ||
| 1605 | pub const Unpacked = union(enum) { | |
| 1606 | unresolved, | |
| 1607 | __zig_error_names, | |
| 1608 | __zig_error_name_table, | |
| 1609 | __heap_base, | |
| 1610 | __heap_end, | |
| 1611 | object: ObjectData.Index, | |
| 1612 | uav_exe: UavsExeIndex, | |
| 1613 | uav_obj: UavsObjIndex, | |
| 1614 | nav_exe: NavsExeIndex, | |
| 1615 | nav_obj: NavsObjIndex, | |
| 1616 | }; | |
| 1757 | 1617 | |
| 1758 | fn allocateAtoms(wasm: *Wasm) !void { | |
| 1759 | // first sort the data segments | |
| 1760 | try sortDataSegments(wasm); | |
| 1761 | ||
| 1762 | var it = wasm.atoms.iterator(); | |
| 1763 | while (it.next()) |entry| { | |
| 1764 | const segment = wasm.segmentPtr(entry.key_ptr.*); | |
| 1765 | var atom_index = entry.value_ptr.*; | |
| 1766 | if (entry.key_ptr.toOptional() == wasm.code_section_index) { | |
| 1767 | // Code section is allocated upon writing as they are required to be ordered | |
| 1768 | // to synchronise with the function section. | |
| 1769 | continue; | |
| 1770 | } | |
| 1771 | var offset: u32 = 0; | |
| 1772 | while (true) { | |
| 1773 | const atom = wasm.getAtomPtr(atom_index); | |
| 1774 | const symbol_loc = atom.symbolLoc(); | |
| 1775 | // Ensure we get the original symbol, so we verify the correct symbol on whether | |
| 1776 | // it is dead or not and ensure an atom is removed when dead. | |
| 1777 | // This is required as we may have parsed aliases into atoms. | |
| 1778 | const sym = switch (symbol_loc.file) { | |
| 1779 | .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)], | |
| 1780 | .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)], | |
| 1781 | _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)], | |
| 1618 | pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked { | |
| 1619 | return switch (r) { | |
| 1620 | .unresolved => .unresolved, | |
| 1621 | .__zig_error_names => .__zig_error_names, | |
| 1622 | .__zig_error_name_table => .__zig_error_name_table, | |
| 1623 | .__heap_base => .__heap_base, | |
| 1624 | .__heap_end => .__heap_end, | |
| 1625 | _ => { | |
| 1626 | const object_index = @intFromEnum(r) - first_object; | |
| 1627 | ||
| 1628 | const uav_index = if (object_index < wasm.object_datas.items.len) | |
| 1629 | return .{ .object = @enumFromInt(object_index) } | |
| 1630 | else | |
| 1631 | object_index - wasm.object_datas.items.len; | |
| 1632 | ||
| 1633 | const comp = wasm.base.comp; | |
| 1634 | const is_obj = comp.config.output_mode == .Obj; | |
| 1635 | if (is_obj) { | |
| 1636 | const nav_index = if (uav_index < wasm.uavs_obj.entries.len) | |
| 1637 | return .{ .uav_obj = @enumFromInt(uav_index) } | |
| 1638 | else | |
| 1639 | uav_index - wasm.uavs_obj.entries.len; | |
| 1640 | ||
| 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 | |
| 1785 | // from being emit into the binary. | |
| 1786 | if (sym.isDead()) { | |
| 1787 | if (entry.value_ptr.* == atom_index and atom.prev != .null) { | |
| 1788 | // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update | |
| 1789 | // the entry to point it to the previous atom to ensure we do not start with a dead symbol that | |
| 1790 | // was removed and therefore do not emit any code at all. | |
| 1791 | entry.value_ptr.* = atom.prev; | |
| 1792 | } | |
| 1793 | if (atom.prev == .null) break; | |
| 1794 | atom_index = atom.prev; | |
| 1795 | atom.prev = .null; | |
| 1796 | continue; | |
| 1797 | } | |
| 1798 | offset = @intCast(atom.alignment.forward(offset)); | |
| 1799 | atom.offset = offset; | |
| 1800 | log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{ | |
| 1801 | wasm.symbolLocName(symbol_loc), | |
| 1802 | offset, | |
| 1803 | offset + atom.size, | |
| 1804 | atom.size, | |
| 1805 | }); | |
| 1806 | offset += atom.size; | |
| 1807 | if (atom.prev == .null) break; | |
| 1808 | atom_index = atom.prev; | |
| 1654 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Resolution { | |
| 1655 | return switch (unpacked) { | |
| 1656 | .unresolved => .unresolved, | |
| 1657 | .__zig_error_names => .__zig_error_names, | |
| 1658 | .__zig_error_name_table => .__zig_error_name_table, | |
| 1659 | .__heap_base => .__heap_base, | |
| 1660 | .__heap_end => .__heap_end, | |
| 1661 | .object => |i| @enumFromInt(first_object + @intFromEnum(i)), | |
| 1662 | inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + @intFromEnum(i)), | |
| 1663 | .nav_exe => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)), | |
| 1664 | .nav_obj => |i| @enumFromInt(first_object + wasm.object_datas.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)), | |
| 1665 | }; | |
| 1809 | 1666 | } |
| 1810 | segment.size = @intCast(segment.alignment.forward(offset)); | |
| 1811 | } | |
| 1812 | } | |
| 1813 | 1667 | |
| 1814 | /// For each data symbol, sets the virtual address. | |
| 1815 | fn allocateVirtualAddresses(wasm: *Wasm) void { | |
| 1816 | for (wasm.resolved_symbols.keys()) |loc| { | |
| 1817 | const symbol = wasm.symbolLocSymbol(loc); | |
| 1818 | if (symbol.tag != .data or symbol.isDead()) { | |
| 1819 | // Only data symbols have virtual addresses. | |
| 1820 | // Dead symbols do not get allocated, so we don't need to set their virtual address either. | |
| 1821 | continue; | |
| 1822 | } | |
| 1823 | const atom_index = wasm.symbol_atom.get(loc) orelse { | |
| 1824 | // synthetic symbol that does not contain an atom | |
| 1825 | continue; | |
| 1826 | }; | |
| 1668 | pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution { | |
| 1669 | return pack(wasm, .{ .object = object_data_index }); | |
| 1670 | } | |
| 1827 | 1671 | |
| 1828 | const atom = wasm.getAtom(atom_index); | |
| 1829 | const merge_segment = wasm.base.comp.config.output_mode != .Obj; | |
| 1830 | const segment_info = switch (atom.file) { | |
| 1831 | .zig_object => wasm.zig_object.?.segment_info.items, | |
| 1832 | .none => wasm.segment_info.values(), | |
| 1833 | _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info, | |
| 1834 | }; | |
| 1835 | const segment_name = segment_info[symbol.index].outputName(merge_segment); | |
| 1836 | const segment_index = wasm.data_segments.get(segment_name).?; | |
| 1837 | const segment = wasm.segmentPtr(segment_index); | |
| 1838 | ||
| 1839 | // TLS symbols have their virtual address set relative to their own TLS segment, | |
| 1840 | // rather than the entire Data section. | |
| 1841 | if (symbol.hasFlag(.WASM_SYM_TLS)) { | |
| 1842 | symbol.virtual_address = atom.offset; | |
| 1843 | } else { | |
| 1844 | symbol.virtual_address = atom.offset + segment.offset; | |
| 1672 | pub fn objectDataSegment(r: Resolution, wasm: *const Wasm) ?ObjectDataSegment.Index { | |
| 1673 | return switch (unpack(r, wasm)) { | |
| 1674 | .unresolved => unreachable, | |
| 1675 | .object => |i| i.ptr(wasm).segment, | |
| 1676 | .__zig_error_names, | |
| 1677 | .__zig_error_name_table, | |
| 1678 | .__heap_base, | |
| 1679 | .__heap_end, | |
| 1680 | .uav_exe, | |
| 1681 | .uav_obj, | |
| 1682 | .nav_exe, | |
| 1683 | .nav_obj, | |
| 1684 | => null, | |
| 1685 | }; | |
| 1845 | 1686 | } |
| 1846 | } | |
| 1847 | } | |
| 1848 | 1687 | |
| 1849 | fn sortDataSegments(wasm: *Wasm) !void { | |
| 1850 | const gpa = wasm.base.comp.gpa; | |
| 1851 | var new_mapping: std.StringArrayHashMapUnmanaged(Segment.Index) = .empty; | |
| 1852 | try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count()); | |
| 1853 | errdefer new_mapping.deinit(gpa); | |
| 1688 | pub fn dataLoc(r: Resolution, wasm: *const Wasm) DataLoc { | |
| 1689 | return switch (unpack(r, wasm)) { | |
| 1690 | .unresolved => unreachable, | |
| 1691 | .object => |i| { | |
| 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()); | |
| 1856 | defer gpa.free(keys); | |
| 1710 | /// Points into `Wasm.object_data_imports`. | |
| 1711 | pub const Index = enum(u32) { | |
| 1712 | _, | |
| 1857 | 1713 | |
| 1858 | const SortContext = struct { | |
| 1859 | fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { | |
| 1860 | return order(lhs) < order(rhs); | |
| 1714 | pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport { | |
| 1715 | return &wasm.object_data_imports.values()[@intFromEnum(i)]; | |
| 1861 | 1716 | } |
| 1862 | 1717 | |
| 1863 | fn order(name: []const u8) u8 { | |
| 1864 | if (mem.startsWith(u8, name, ".rodata")) return 0; | |
| 1865 | if (mem.startsWith(u8, name, ".data")) return 1; | |
| 1866 | if (mem.startsWith(u8, name, ".text")) return 2; | |
| 1867 | return 3; | |
| 1718 | pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index { | |
| 1719 | return @enumFromInt(wasm.object_data_imports.getIndex(name) orelse return null); | |
| 1868 | 1720 | } |
| 1869 | 1721 | }; |
| 1722 | }; | |
| 1870 | 1723 | |
| 1871 | mem.sort([]const u8, keys, {}, SortContext.sort); | |
| 1872 | for (keys) |key| { | |
| 1873 | const segment_index = wasm.data_segments.get(key).?; | |
| 1874 | new_mapping.putAssumeCapacity(key, segment_index); | |
| 1875 | } | |
| 1876 | wasm.data_segments.deinit(gpa); | |
| 1877 | wasm.data_segments = new_mapping; | |
| 1878 | } | |
| 1724 | pub const DataPayload = extern struct { | |
| 1725 | off: Off, | |
| 1726 | /// The size in bytes of the data representing the segment within the section. | |
| 1727 | len: u32, | |
| 1879 | 1728 | |
| 1880 | /// Obtains all initfuncs from each object file, verifies its function signature, | |
| 1881 | /// and then appends it to our final `init_funcs` list. | |
| 1882 | /// After all functions have been inserted, the functions will be ordered based | |
| 1883 | /// on their priority. | |
| 1884 | /// NOTE: This function must be called before we merged any other section. | |
| 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 | } | |
| 1729 | pub const Off = enum(u32) { | |
| 1730 | /// The payload is all zeroes (bss section). | |
| 1731 | none = std.math.maxInt(u32), | |
| 1732 | /// Points into string_bytes. No corresponding string_table entry. | |
| 1733 | _, | |
| 1920 | 1734 | |
| 1921 | // sort the initfunctions based on their priority | |
| 1922 | mem.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); | |
| 1735 | pub fn unwrap(off: Off) ?u32 { | |
| 1736 | return if (off == .none) null else @intFromEnum(off); | |
| 1737 | } | |
| 1738 | }; | |
| 1923 | 1739 | |
| 1924 | if (wasm.init_funcs.items.len > 0) { | |
| 1925 | const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?; | |
| 1926 | try wasm.mark(loc); | |
| 1740 | pub fn slice(p: DataPayload, wasm: *const Wasm) []const u8 { | |
| 1741 | return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len]; | |
| 1927 | 1742 | } |
| 1928 | } | |
| 1743 | }; | |
| 1929 | 1744 | |
| 1930 | /// Creates a function body for the `__wasm_call_ctors` symbol. | |
| 1931 | /// Loops over all constructors found in `init_funcs` and calls them | |
| 1932 | /// respectively based on their priority which was sorted by `setupInitFunctions`. | |
| 1933 | /// NOTE: This function must be called after we merged all sections to ensure the | |
| 1934 | /// references to the function stored in the symbol have been finalized so we end | |
| 1935 | /// up calling the resolved function. | |
| 1936 | fn initializeCallCtorsFunction(wasm: *Wasm) !void { | |
| 1937 | const gpa = wasm.base.comp.gpa; | |
| 1938 | // No code to emit, so also no ctors to call | |
| 1939 | if (wasm.code_section_index == .none) { | |
| 1940 | // Make sure to remove it from the resolved symbols so we do not emit | |
| 1941 | // it within any section. TODO: Remove this once we implement garbage collection. | |
| 1942 | const loc = wasm.globals.get(wasm.preloaded_strings.__wasm_call_ctors).?; | |
| 1943 | assert(wasm.resolved_symbols.swapRemove(loc)); | |
| 1944 | return; | |
| 1945 | } | |
| 1745 | /// A reference to a local or exported global const. | |
| 1746 | pub const DataSegmentId = enum(u32) { | |
| 1747 | __zig_error_names, | |
| 1748 | __zig_error_name_table, | |
| 1749 | /// All name string bytes for all `@tagName` implementations, concatenated together. | |
| 1750 | __zig_tag_names, | |
| 1751 | /// All tag name slices for all `@tagName` implementations, concatenated together. | |
| 1752 | __zig_tag_name_table, | |
| 1753 | /// This and `__heap_end` are better retrieved via a global, but there is | |
| 1754 | /// some suboptimal code out there (wasi libc) that additionally needs them | |
| 1755 | /// as data symbols. | |
| 1756 | __heap_base, | |
| 1757 | __heap_end, | |
| 1758 | /// First, an `ObjectDataSegment.Index`. | |
| 1759 | /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. | |
| 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); | |
| 1948 | defer function_body.deinit(); | |
| 1949 | const writer = function_body.writer(); | |
| 1763 | const first_object = @intFromEnum(DataSegmentId.__heap_end) + 1; | |
| 1950 | 1764 | |
| 1951 | // Create the function body | |
| 1952 | { | |
| 1953 | // Write locals count (we have none) | |
| 1954 | try leb.writeUleb128(writer, @as(u32, 0)); | |
| 1765 | pub const Category = enum { | |
| 1766 | /// Thread-local variables. | |
| 1767 | tls, | |
| 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 | |
| 1957 | for (wasm.init_funcs.items) |init_func_loc| { | |
| 1958 | const symbol = init_func_loc.getSymbol(wasm); | |
| 1959 | const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count].func; | |
| 1960 | const ty = wasm.func_types.items[func.type_index]; | |
| 1775 | pub const Unpacked = union(enum) { | |
| 1776 | __zig_error_names, | |
| 1777 | __zig_error_name_table, | |
| 1778 | __zig_tag_names, | |
| 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 | |
| 1963 | try writer.writeByte(std.wasm.opcode(.call)); | |
| 1964 | try leb.writeUleb128(writer, symbol.index); | |
| 1789 | pub fn pack(wasm: *const Wasm, unpacked: Unpacked) DataSegmentId { | |
| 1790 | return switch (unpacked) { | |
| 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 | |
| 1967 | for (ty.returns) |_| { | |
| 1968 | try writer.writeByte(std.wasm.opcode(.drop)); | |
| 1969 | } | |
| 1970 | } | |
| 1804 | pub fn unpack(id: DataSegmentId, wasm: *const Wasm) Unpacked { | |
| 1805 | return switch (id) { | |
| 1806 | .__zig_error_names => .__zig_error_names, | |
| 1807 | .__zig_error_name_table => .__zig_error_name_table, | |
| 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 | |
| 1973 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 1974 | } | |
| 1815 | const uav_index = if (object_index < wasm.object_data_segments.items.len) | |
| 1816 | return .{ .object = @enumFromInt(object_index) } | |
| 1817 | else | |
| 1818 | object_index - wasm.object_data_segments.items.len; | |
| 1975 | 1819 | |
| 1976 | try wasm.createSyntheticFunction( | |
| 1977 | wasm.preloaded_strings.__wasm_call_ctors, | |
| 1978 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, | |
| 1979 | &function_body, | |
| 1980 | ); | |
| 1981 | } | |
| 1820 | const comp = wasm.base.comp; | |
| 1821 | const is_obj = comp.config.output_mode == .Obj; | |
| 1822 | if (is_obj) { | |
| 1823 | const nav_index = if (uav_index < wasm.uavs_obj.entries.len) | |
| 1824 | return .{ .uav_obj = @enumFromInt(uav_index) } | |
| 1825 | else | |
| 1826 | uav_index - wasm.uavs_obj.entries.len; | |
| 1982 | 1827 | |
| 1983 | fn createSyntheticFunction( | |
| 1984 | wasm: *Wasm, | |
| 1985 | symbol_name: String, | |
| 1986 | func_ty: std.wasm.Type, | |
| 1987 | function_body: *std.ArrayList(u8), | |
| 1988 | ) !void { | |
| 1989 | const gpa = wasm.base.comp.gpa; | |
| 1990 | const loc = wasm.globals.get(symbol_name).?; | |
| 1991 | const symbol = wasm.symbolLocSymbol(loc); | |
| 1992 | if (symbol.isDead()) { | |
| 1993 | return; | |
| 1828 | return .{ .nav_obj = @enumFromInt(nav_index) }; | |
| 1829 | } else { | |
| 1830 | const nav_index = if (uav_index < wasm.uavs_exe.entries.len) | |
| 1831 | return .{ .uav_exe = @enumFromInt(uav_index) } | |
| 1832 | else | |
| 1833 | uav_index - wasm.uavs_exe.entries.len; | |
| 1834 | ||
| 1835 | return .{ .nav_exe = @enumFromInt(nav_index) }; | |
| 1836 | } | |
| 1837 | }, | |
| 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 | |
| 2014 | /// the codegeneration backend. This will not allocate the created Atom yet. | |
| 2015 | /// Returns the index of the symbol. | |
| 2016 | pub fn createFunction( | |
| 2017 | wasm: *Wasm, | |
| 2018 | symbol_name: []const u8, | |
| 2019 | func_ty: std.wasm.Type, | |
| 2020 | function_body: *std.ArrayList(u8), | |
| 2021 | relocations: *std.ArrayList(Relocation), | |
| 2022 | ) !Symbol.Index { | |
| 2023 | return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations); | |
| 2024 | } | |
| 1841 | pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId { | |
| 1842 | const comp = wasm.base.comp; | |
| 1843 | const is_obj = comp.config.output_mode == .Obj; | |
| 1844 | return pack(wasm, if (is_obj) .{ | |
| 1845 | .nav_obj = @enumFromInt(wasm.navs_obj.getIndex(nav_index).?), | |
| 1846 | } else .{ | |
| 1847 | .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?), | |
| 1848 | }); | |
| 1849 | } | |
| 2025 | 1850 | |
| 2026 | /// If required, sets the function index in the `start` section. | |
| 2027 | fn setupStartSection(wasm: *Wasm) !void { | |
| 2028 | if (wasm.globals.get(wasm.preloaded_strings.__wasm_init_memory)) |loc| { | |
| 2029 | wasm.entry = wasm.symbolLocSymbol(loc).index; | |
| 1851 | pub fn fromObjectDataSegment(wasm: *const Wasm, object_data_segment: ObjectDataSegment.Index) DataSegmentId { | |
| 1852 | return pack(wasm, .{ .object = object_data_segment }); | |
| 2030 | 1853 | } |
| 2031 | } | |
| 2032 | 1854 | |
| 2033 | fn initializeTLSFunction(wasm: *Wasm) !void { | |
| 2034 | const comp = wasm.base.comp; | |
| 2035 | const gpa = comp.gpa; | |
| 2036 | const shared_memory = comp.config.shared_memory; | |
| 1855 | pub fn category(id: DataSegmentId, wasm: *const Wasm) Category { | |
| 1856 | return switch (unpack(id, wasm)) { | |
| 1857 | .__zig_error_names, | |
| 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 | |
| 2041 | wasm.symbolLocSymbol(wasm.globals.get(wasm.preloaded_strings.__wasm_init_tls).?).mark(); | |
| 1883 | pub fn isTls(id: DataSegmentId, wasm: *const Wasm) bool { | |
| 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); | |
| 2044 | defer function_body.deinit(); | |
| 2045 | const writer = function_body.writer(); | |
| 1904 | pub fn isBss(id: DataSegmentId, wasm: *const Wasm) bool { | |
| 1905 | return id.category(wasm) == .zero; | |
| 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 | |
| 2048 | try writer.writeByte(0); | |
| 1934 | pub fn alignment(id: DataSegmentId, wasm: *const Wasm) Alignment { | |
| 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 | |
| 2051 | if (wasm.data_segments.getIndex(".tdata")) |data_index| { | |
| 2052 | const segment_index = wasm.data_segments.entries.items(.value)[data_index]; | |
| 2053 | const segment = wasm.segmentPtr(segment_index); | |
| 1963 | pub fn refCount(id: DataSegmentId, wasm: *const Wasm) u32 { | |
| 1964 | return switch (unpack(id, wasm)) { | |
| 1965 | .__zig_error_names => @intCast(wasm.error_name_offs.items.len), | |
| 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)); | |
| 2058 | try leb.writeUleb128(writer, param_local); | |
| 1991 | pub fn isEmpty(id: DataSegmentId, wasm: *const Wasm) bool { | |
| 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).?; | |
| 2061 | try writer.writeByte(std.wasm.opcode(.global_set)); | |
| 2062 | try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index); | |
| 2001 | .object => |i| i.ptr(wasm).payload.off == .none, | |
| 2002 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none, | |
| 2003 | }; | |
| 2004 | } | |
| 2063 | 2005 | |
| 2064 | // load stack values for the bulk-memory operation | |
| 2065 | { | |
| 2066 | try writer.writeByte(std.wasm.opcode(.local_get)); | |
| 2067 | try leb.writeUleb128(writer, param_local); | |
| 2006 | pub fn size(id: DataSegmentId, wasm: *const Wasm) u32 { | |
| 2007 | return switch (unpack(id, wasm)) { | |
| 2008 | .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), | |
| 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)); | |
| 2070 | try leb.writeUleb128(writer, @as(u32, 0)); //segment offset | |
| 2031 | pub const DataLoc = struct { | |
| 2032 | segment: Wasm.DataSegmentId, | |
| 2033 | offset: u32, | |
| 2071 | 2034 | |
| 2072 | try writer.writeByte(std.wasm.opcode(.i32_const)); | |
| 2073 | try leb.writeUleb128(writer, @as(u32, segment.size)); //segment offset | |
| 2074 | } | |
| 2035 | pub fn fromObjectDataIndex(wasm: *const Wasm, i: Wasm.ObjectData.Index) DataLoc { | |
| 2036 | const ptr = i.ptr(wasm); | |
| 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 | |
| 2077 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); | |
| 2078 | try leb.writeUleb128(writer, std.wasm.miscOpcode(.memory_init)); | |
| 2079 | // segment immediate | |
| 2080 | try leb.writeUleb128(writer, @as(u32, @intCast(data_index))); | |
| 2081 | // memory index immediate (always 0) | |
| 2082 | try leb.writeUleb128(writer, @as(u32, 0)); | |
| 2043 | pub fn fromDataImportId(wasm: *const Wasm, id: Wasm.DataImportId) DataLoc { | |
| 2044 | return switch (id.unpack(wasm)) { | |
| 2045 | .object_data_import => |i| .fromObjectDataImportIndex(wasm, i), | |
| 2046 | .zcu_import => |i| .fromZcuImport(wasm, i), | |
| 2047 | }; | |
| 2083 | 2048 | } |
| 2084 | 2049 | |
| 2085 | // If we have to perform any TLS relocations, call the corresponding function | |
| 2086 | // which performs all runtime TLS relocations. This is a synthetic function, | |
| 2087 | // generated by the linker. | |
| 2088 | if (wasm.globals.get(wasm.preloaded_strings.__wasm_apply_global_tls_relocs)) |loc| { | |
| 2089 | try writer.writeByte(std.wasm.opcode(.call)); | |
| 2090 | try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index); | |
| 2091 | wasm.symbolLocSymbol(loc).mark(); | |
| 2050 | pub fn fromObjectDataImportIndex(wasm: *const Wasm, i: Wasm.ObjectDataImport.Index) DataLoc { | |
| 2051 | return i.value(wasm).resolution.dataLoc(wasm); | |
| 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( | |
| 2097 | wasm.preloaded_strings.__wasm_init_tls, | |
| 2098 | std.wasm.Type{ .params = &.{.i32}, .returns = &.{} }, | |
| 2099 | &function_body, | |
| 2100 | ); | |
| 2101 | } | |
| 2063 | /// Index into `Wasm.uavs`. | |
| 2064 | pub const UavIndex = enum(u32) { | |
| 2065 | _, | |
| 2066 | }; | |
| 2102 | 2067 | |
| 2103 | fn setupImports(wasm: *Wasm) !void { | |
| 2104 | const gpa = wasm.base.comp.gpa; | |
| 2105 | log.debug("Merging imports", .{}); | |
| 2106 | for (wasm.resolved_symbols.keys()) |symbol_loc| { | |
| 2107 | const object_id = symbol_loc.file.unwrap() orelse { | |
| 2108 | // Synthetic symbols will already exist in the `import` section | |
| 2109 | continue; | |
| 2110 | }; | |
| 2068 | pub const CustomSegment = extern struct { | |
| 2069 | payload: Payload, | |
| 2070 | flags: SymbolFlags, | |
| 2071 | section_name: String, | |
| 2111 | 2072 | |
| 2112 | const symbol = wasm.symbolLocSymbol(symbol_loc); | |
| 2113 | if (symbol.isDead()) continue; | |
| 2114 | if (!symbol.requiresImport()) continue; | |
| 2115 | if (symbol.name == wasm.preloaded_strings.__indirect_function_table) continue; | |
| 2073 | pub const Payload = DataPayload; | |
| 2074 | }; | |
| 2075 | ||
| 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)}); | |
| 2118 | const import = objectImport(wasm, object_id, symbol_loc.index); | |
| 2080 | pub const end = @intFromEnum(std.wasm.Opcode.end); | |
| 2119 | 2081 | |
| 2120 | // We copy the import to a new import to ensure the names contain references | |
| 2121 | // to the internal string table, rather than of the object file. | |
| 2122 | const new_imp: Import = .{ | |
| 2123 | .module_name = import.module_name, | |
| 2124 | .name = import.name, | |
| 2125 | .kind = import.kind, | |
| 2082 | pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 { | |
| 2083 | const start_slice = wasm.string_bytes.items[@intFromEnum(index)..]; | |
| 2084 | const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) { | |
| 2085 | error.InvalidInitOpcode => unreachable, | |
| 2126 | 2086 | }; |
| 2127 | // TODO: De-duplicate imports when they contain the same names and type | |
| 2128 | try wasm.imports.putNoClobber(gpa, symbol_loc, new_imp); | |
| 2129 | } | |
| 2130 | ||
| 2131 | // Assign all indexes of the imports to their representing symbols | |
| 2132 | var function_index: u32 = 0; | |
| 2133 | var global_index: u32 = 0; | |
| 2134 | var table_index: u32 = 0; | |
| 2135 | var it = wasm.imports.iterator(); | |
| 2136 | while (it.next()) |entry| { | |
| 2137 | const symbol = wasm.symbolLocSymbol(entry.key_ptr.*); | |
| 2138 | const import: Import = entry.value_ptr.*; | |
| 2139 | switch (import.kind) { | |
| 2140 | .function => { | |
| 2141 | symbol.index = function_index; | |
| 2142 | function_index += 1; | |
| 2143 | }, | |
| 2144 | .global => { | |
| 2145 | symbol.index = global_index; | |
| 2146 | global_index += 1; | |
| 2147 | }, | |
| 2148 | .table => { | |
| 2149 | symbol.index = table_index; | |
| 2150 | table_index += 1; | |
| 2151 | }, | |
| 2152 | else => unreachable, | |
| 2153 | } | |
| 2087 | return start_slice[0..end_pos :end]; | |
| 2154 | 2088 | } |
| 2155 | wasm.imported_functions_count = function_index; | |
| 2156 | wasm.imported_globals_count = global_index; | |
| 2157 | wasm.imported_tables_count = table_index; | |
| 2089 | }; | |
| 2158 | 2090 | |
| 2159 | log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{ | |
| 2160 | function_index, | |
| 2161 | global_index, | |
| 2162 | table_index, | |
| 2163 | }); | |
| 2164 | } | |
| 2091 | pub const FunctionType = extern struct { | |
| 2092 | params: ValtypeList, | |
| 2093 | returns: ValtypeList, | |
| 2165 | 2094 | |
| 2166 | /// Takes the global, function and table section from each linked object file | |
| 2167 | /// and merges it into a single section for each. | |
| 2168 | fn mergeSections(wasm: *Wasm) !void { | |
| 2169 | const gpa = wasm.base.comp.gpa; | |
| 2095 | /// Index into func_types | |
| 2096 | pub const Index = enum(u32) { | |
| 2097 | _, | |
| 2170 | 2098 | |
| 2171 | var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa); | |
| 2172 | defer removed_duplicates.deinit(); | |
| 2099 | pub fn ptr(i: Index, wasm: *const Wasm) *FunctionType { | |
| 2100 | return &wasm.func_types.keys()[@intFromEnum(i)]; | |
| 2101 | } | |
| 2173 | 2102 | |
| 2174 | for (wasm.resolved_symbols.keys()) |sym_loc| { | |
| 2175 | const object_id = sym_loc.file.unwrap() orelse { | |
| 2176 | // Synthetic symbols already live in the corresponding sections. | |
| 2177 | continue; | |
| 2178 | }; | |
| 2103 | pub fn fmt(i: Index, wasm: *const Wasm) Formatter { | |
| 2104 | return i.ptr(wasm).fmt(wasm); | |
| 2105 | } | |
| 2106 | }; | |
| 2179 | 2107 | |
| 2180 | const symbol = objectSymbol(wasm, object_id, sym_loc.index); | |
| 2181 | if (symbol.isDead() or symbol.isUndefined()) { | |
| 2182 | // Skip undefined symbols as they go in the `import` section | |
| 2183 | continue; | |
| 2184 | } | |
| 2185 | ||
| 2186 | switch (symbol.tag) { | |
| 2187 | .function => { | |
| 2188 | const gop = try wasm.functions.getOrPut( | |
| 2189 | gpa, | |
| 2190 | .{ .file = sym_loc.file, .index = symbol.index }, | |
| 2191 | ); | |
| 2192 | if (gop.found_existing) { | |
| 2193 | // We found an alias to the same function, discard this symbol in favor of | |
| 2194 | // the original symbol and point the discard function to it. This ensures | |
| 2195 | // we only emit a single function, instead of duplicates. | |
| 2196 | // we favor keeping the global over a local. | |
| 2197 | const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index }; | |
| 2198 | const original_sym = wasm.symbolLocSymbol(original_loc); | |
| 2199 | if (original_sym.isLocal() and symbol.isGlobal()) { | |
| 2200 | original_sym.unmark(); | |
| 2201 | try wasm.discarded.put(gpa, original_loc, sym_loc); | |
| 2202 | try removed_duplicates.append(original_loc); | |
| 2203 | } else { | |
| 2204 | symbol.unmark(); | |
| 2205 | try wasm.discarded.putNoClobber(gpa, sym_loc, original_loc); | |
| 2206 | try removed_duplicates.append(sym_loc); | |
| 2207 | continue; | |
| 2108 | pub const format = @compileError("can't format without *Wasm reference"); | |
| 2109 | ||
| 2110 | pub fn eql(a: FunctionType, b: FunctionType) bool { | |
| 2111 | return a.params == b.params and a.returns == b.returns; | |
| 2112 | } | |
| 2113 | ||
| 2114 | pub fn fmt(ft: FunctionType, wasm: *const Wasm) Formatter { | |
| 2115 | return .{ .wasm = wasm, .ft = ft }; | |
| 2116 | } | |
| 2117 | ||
| 2118 | const Formatter = struct { | |
| 2119 | wasm: *const Wasm, | |
| 2120 | ft: FunctionType, | |
| 2121 | ||
| 2122 | pub fn format( | |
| 2123 | self: Formatter, | |
| 2124 | comptime format_string: []const u8, | |
| 2125 | options: std.fmt.FormatOptions, | |
| 2126 | writer: anytype, | |
| 2127 | ) !void { | |
| 2128 | if (format_string.len != 0) std.fmt.invalidFmtError(format_string, self); | |
| 2129 | _ = options; | |
| 2130 | const params = self.ft.params.slice(self.wasm); | |
| 2131 | const returns = self.ft.returns.slice(self.wasm); | |
| 2132 | ||
| 2133 | try writer.writeByte('('); | |
| 2134 | for (params, 0..) |param, i| { | |
| 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.* = .{ | |
| 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 => {}, | |
| 2150 | } | |
| 2232 | 2151 | } |
| 2233 | } | |
| 2152 | }; | |
| 2153 | }; | |
| 2234 | 2154 | |
| 2235 | // For any removed duplicates, remove them from the resolved symbols list | |
| 2236 | for (removed_duplicates.items) |sym_loc| { | |
| 2237 | assert(wasm.resolved_symbols.swapRemove(sym_loc)); | |
| 2238 | gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)}); | |
| 2239 | } | |
| 2155 | /// Represents a function entry, holding the index to its type | |
| 2156 | pub const Func = extern struct { | |
| 2157 | type_index: FunctionType.Index, | |
| 2158 | }; | |
| 2240 | 2159 | |
| 2241 | log.debug("Merged ({d}) functions", .{wasm.functions.count()}); | |
| 2242 | log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len}); | |
| 2243 | log.debug("Merged ({d}) tables", .{wasm.tables.items.len}); | |
| 2244 | } | |
| 2160 | /// Type reflection is used on the field names to autopopulate each field | |
| 2161 | /// during initialization. | |
| 2162 | const PreloadedStrings = struct { | |
| 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 | |
| 2247 | /// 'types' section, while assigning the type index to the representing | |
| 2248 | /// section (import, export, function). | |
| 2249 | fn mergeTypes(wasm: *Wasm) !void { | |
| 2250 | const gpa = wasm.base.comp.gpa; | |
| 2251 | // A map to track which functions have already had their | |
| 2252 | // type inserted. If we do this for the same function multiple times, | |
| 2253 | // it will be overwritten with the incorrect type. | |
| 2254 | var dirty = std.AutoHashMap(u32, void).init(gpa); | |
| 2255 | try dirty.ensureUnusedCapacity(@as(u32, @intCast(wasm.functions.count()))); | |
| 2256 | defer dirty.deinit(); | |
| 2257 | ||
| 2258 | for (wasm.resolved_symbols.keys()) |sym_loc| { | |
| 2259 | const object_id = sym_loc.file.unwrap() orelse { | |
| 2260 | // zig code-generated symbols are already present in final type section | |
| 2261 | continue; | |
| 2262 | }; | |
| 2184 | /// Index into string_bytes | |
| 2185 | pub const String = enum(u32) { | |
| 2186 | _, | |
| 2187 | ||
| 2188 | const Table = std.HashMapUnmanaged(String, void, TableContext, std.hash_map.default_max_load_percentage); | |
| 2189 | ||
| 2190 | const TableContext = struct { | |
| 2191 | bytes: []const u8, | |
| 2263 | 2192 | |
| 2264 | const symbol = objectSymbol(wasm, object_id, sym_loc.index); | |
| 2265 | if (symbol.tag != .function or symbol.isDead()) { | |
| 2266 | // Only functions have types. Only retrieve the type of referenced functions. | |
| 2267 | continue; | |
| 2193 | pub fn eql(_: @This(), a: String, b: String) bool { | |
| 2194 | return a == b; | |
| 2268 | 2195 | } |
| 2269 | 2196 | |
| 2270 | if (symbol.isUndefined()) { | |
| 2271 | log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)}); | |
| 2272 | const import: *Import = wasm.imports.getPtr(sym_loc) orelse continue; | |
| 2273 | const original_type = objectFuncTypes(wasm, object_id)[import.kind.function]; | |
| 2274 | import.kind.function = try wasm.putOrGetFuncType(original_type); | |
| 2275 | } else if (!dirty.contains(symbol.index)) { | |
| 2276 | log.debug("Adding type from function '{s}'", .{wasm.symbolLocName(sym_loc)}); | |
| 2277 | const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func; | |
| 2278 | func.type_index = try wasm.putOrGetFuncType(objectFuncTypes(wasm, object_id)[func.type_index]); | |
| 2279 | dirty.putAssumeCapacityNoClobber(symbol.index, {}); | |
| 2197 | pub fn hash(ctx: @This(), key: String) u64 { | |
| 2198 | return std.hash_map.hashString(mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0)); | |
| 2280 | 2199 | } |
| 2281 | } | |
| 2282 | log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len}); | |
| 2283 | } | |
| 2200 | }; | |
| 2284 | 2201 | |
| 2285 | fn checkExportNames(wasm: *Wasm) !void { | |
| 2286 | const force_exp_names = wasm.export_symbol_names; | |
| 2287 | const diags = &wasm.base.comp.link_diags; | |
| 2288 | if (force_exp_names.len > 0) { | |
| 2289 | var failed_exports = false; | |
| 2290 | ||
| 2291 | for (force_exp_names) |exp_name| { | |
| 2292 | const exp_name_interned = try wasm.internString(exp_name); | |
| 2293 | const loc = wasm.globals.get(exp_name_interned) orelse { | |
| 2294 | var err = try diags.addErrorWithNotes(0); | |
| 2295 | try err.addMsg("could not export '{s}', symbol not found", .{exp_name}); | |
| 2296 | failed_exports = true; | |
| 2297 | continue; | |
| 2298 | }; | |
| 2202 | const TableIndexAdapter = struct { | |
| 2203 | bytes: []const u8, | |
| 2299 | 2204 | |
| 2300 | const symbol = wasm.symbolLocSymbol(loc); | |
| 2301 | symbol.setFlag(.WASM_SYM_EXPORTED); | |
| 2205 | pub fn eql(ctx: @This(), a: []const u8, b: String) bool { | |
| 2206 | return mem.eql(u8, a, mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0)); | |
| 2302 | 2207 | } |
| 2303 | 2208 | |
| 2304 | if (failed_exports) { | |
| 2305 | return error.FlushFailure; | |
| 2209 | pub fn hash(_: @This(), adapted_key: []const u8) u64 { | |
| 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 { | |
| 2311 | const comp = wasm.base.comp; | |
| 2312 | const gpa = comp.gpa; | |
| 2313 | if (comp.config.output_mode == .Obj) return; | |
| 2314 | log.debug("Building exports from symbols", .{}); | |
| 2315 | ||
| 2316 | for (wasm.resolved_symbols.keys()) |sym_loc| { | |
| 2317 | const symbol = wasm.symbolLocSymbol(sym_loc); | |
| 2318 | if (!symbol.isExported(comp.config.rdynamic)) continue; | |
| 2319 | ||
| 2320 | const exp: Export = if (symbol.tag == .data) exp: { | |
| 2321 | const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len)); | |
| 2322 | try wasm.wasm_globals.append(gpa, .{ | |
| 2323 | .global_type = .{ .valtype = .i32, .mutable = false }, | |
| 2324 | .init = .{ .i32_const = @as(i32, @intCast(symbol.virtual_address)) }, | |
| 2325 | }); | |
| 2326 | break :exp .{ | |
| 2327 | .name = symbol.name, | |
| 2328 | .kind = .global, | |
| 2329 | .index = global_index, | |
| 2330 | }; | |
| 2331 | } else .{ | |
| 2332 | .name = symbol.name, | |
| 2333 | .kind = symbol.tag.externalType(), | |
| 2334 | .index = symbol.index, | |
| 2335 | }; | |
| 2336 | log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ | |
| 2337 | wasm.stringSlice(symbol.name), | |
| 2338 | wasm.stringSlice(exp.name), | |
| 2339 | exp.index, | |
| 2340 | }); | |
| 2341 | try wasm.exports.append(gpa, exp); | |
| 2220 | pub fn toOptional(i: String) OptionalString { | |
| 2221 | const result: OptionalString = @enumFromInt(@intFromEnum(i)); | |
| 2222 | assert(result != .none); | |
| 2223 | return result; | |
| 2342 | 2224 | } |
| 2225 | }; | |
| 2343 | 2226 | |
| 2344 | log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len}); | |
| 2345 | } | |
| 2227 | pub const OptionalString = enum(u32) { | |
| 2228 | none = std.math.maxInt(u32), | |
| 2229 | _, | |
| 2346 | 2230 | |
| 2347 | fn setupStart(wasm: *Wasm) !void { | |
| 2348 | const comp = wasm.base.comp; | |
| 2349 | const diags = &wasm.base.comp.link_diags; | |
| 2350 | // do not export entry point if user set none or no default was set. | |
| 2351 | const entry_name = wasm.entry_name.unwrap() orelse return; | |
| 2352 | ||
| 2353 | const symbol_loc = wasm.globals.get(entry_name) orelse { | |
| 2354 | var err = try diags.addErrorWithNotes(1); | |
| 2355 | try err.addMsg("entry symbol '{s}' missing", .{wasm.stringSlice(entry_name)}); | |
| 2356 | try err.addNote("'-fno-entry' suppresses this error", .{}); | |
| 2357 | return error.LinkFailure; | |
| 2358 | }; | |
| 2231 | pub fn unwrap(i: OptionalString) ?String { | |
| 2232 | if (i == .none) return null; | |
| 2233 | return @enumFromInt(@intFromEnum(i)); | |
| 2234 | } | |
| 2235 | ||
| 2236 | pub fn slice(index: OptionalString, wasm: *const Wasm) ?[:0]const u8 { | |
| 2237 | return (index.unwrap() orelse return null).slice(wasm); | |
| 2238 | } | |
| 2239 | }; | |
| 2359 | 2240 | |
| 2360 | const symbol = wasm.symbolLocSymbol(symbol_loc); | |
| 2361 | if (symbol.tag != .function) | |
| 2362 | return diags.fail("entry symbol '{s}' is not a function", .{wasm.stringSlice(entry_name)}); | |
| 2241 | /// Stored identically to `String`. The bytes are reinterpreted as | |
| 2242 | /// `std.wasm.Valtype` elements. | |
| 2243 | pub const ValtypeList = enum(u32) { | |
| 2244 | _, | |
| 2363 | 2245 | |
| 2364 | // Ensure the symbol is exported so host environment can access it | |
| 2365 | if (comp.config.output_mode != .Obj) { | |
| 2366 | symbol.setFlag(.WASM_SYM_EXPORTED); | |
| 2246 | pub fn fromString(s: String) ValtypeList { | |
| 2247 | return @enumFromInt(@intFromEnum(s)); | |
| 2367 | 2248 | } |
| 2368 | } | |
| 2369 | 2249 | |
| 2370 | /// Sets up the memory section of the wasm module, as well as the stack. | |
| 2371 | fn setupMemory(wasm: *Wasm) !void { | |
| 2372 | const comp = wasm.base.comp; | |
| 2373 | const diags = &wasm.base.comp.link_diags; | |
| 2374 | const shared_memory = comp.config.shared_memory; | |
| 2375 | log.debug("Setting up memory layout", .{}); | |
| 2376 | const page_size = std.wasm.page_size; // 64kb | |
| 2377 | const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention | |
| 2378 | const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention | |
| 2379 | ||
| 2380 | // Always place the stack at the start by default | |
| 2381 | // unless the user specified the global-base flag | |
| 2382 | var place_stack_first = true; | |
| 2383 | var memory_ptr: u64 = if (wasm.global_base) |base| blk: { | |
| 2384 | place_stack_first = false; | |
| 2385 | break :blk base; | |
| 2386 | } else 0; | |
| 2250 | pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype { | |
| 2251 | return @ptrCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm)); | |
| 2252 | } | |
| 2253 | }; | |
| 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: { | |
| 2391 | const sym = wasm.symbolLocSymbol(loc); | |
| 2392 | break :index sym.index - wasm.imported_globals_count; | |
| 2393 | } else null; | |
| 2259 | pub fn ptr(index: ZcuImportIndex, wasm: *const Wasm) *InternPool.Nav.Index { | |
| 2260 | return &wasm.imports.keys()[@intFromEnum(index)]; | |
| 2261 | } | |
| 2394 | 2262 | |
| 2395 | if (place_stack_first and !is_obj) { | |
| 2396 | memory_ptr = stack_alignment.forward(memory_ptr); | |
| 2397 | memory_ptr += wasm.base.stack_size; | |
| 2398 | // We always put the stack pointer global at index 0 | |
| 2399 | if (stack_ptr) |index| { | |
| 2400 | wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); | |
| 2401 | } | |
| 2263 | pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String { | |
| 2264 | const zcu = wasm.base.comp.zcu.?; | |
| 2265 | const ip = &zcu.intern_pool; | |
| 2266 | const nav_index = index.ptr(wasm).*; | |
| 2267 | const ext = ip.getNav(nav_index).getResolvedExtern(ip).?; | |
| 2268 | const name_slice = ext.name.toSlice(ip); | |
| 2269 | return wasm.getExistingString(name_slice).?; | |
| 2402 | 2270 | } |
| 2403 | 2271 | |
| 2404 | var offset: u32 = @as(u32, @intCast(memory_ptr)); | |
| 2405 | var data_seg_it = wasm.data_segments.iterator(); | |
| 2406 | while (data_seg_it.next()) |entry| { | |
| 2407 | const segment = wasm.segmentPtr(entry.value_ptr.*); | |
| 2408 | memory_ptr = segment.alignment.forward(memory_ptr); | |
| 2272 | pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString { | |
| 2273 | const zcu = wasm.base.comp.zcu.?; | |
| 2274 | const ip = &zcu.intern_pool; | |
| 2275 | const nav_index = index.ptr(wasm).*; | |
| 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 | |
| 2411 | if (mem.eql(u8, entry.key_ptr.*, ".tdata")) { | |
| 2412 | if (wasm.globals.get(wasm.preloaded_strings.__tls_size)) |loc| { | |
| 2413 | const sym = wasm.symbolLocSymbol(loc); | |
| 2414 | wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size); | |
| 2415 | } | |
| 2416 | if (wasm.globals.get(wasm.preloaded_strings.__tls_align)) |loc| { | |
| 2417 | const sym = wasm.symbolLocSymbol(loc); | |
| 2418 | wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?); | |
| 2419 | } | |
| 2420 | if (wasm.globals.get(wasm.preloaded_strings.__tls_base)) |loc| { | |
| 2421 | const sym = wasm.symbolLocSymbol(loc); | |
| 2422 | wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory) | |
| 2423 | @as(i32, 0) | |
| 2424 | else | |
| 2425 | @as(i32, @intCast(memory_ptr)); | |
| 2426 | } | |
| 2427 | } | |
| 2281 | pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index { | |
| 2282 | const comp = wasm.base.comp; | |
| 2283 | const target = &comp.root_mod.resolved_target.result; | |
| 2284 | const zcu = comp.zcu.?; | |
| 2285 | const ip = &zcu.intern_pool; | |
| 2286 | const nav_index = index.ptr(wasm).*; | |
| 2287 | const ext = ip.getNav(nav_index).getResolvedExtern(ip).?; | |
| 2288 | const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?; | |
| 2289 | return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?; | |
| 2290 | } | |
| 2428 | 2291 | |
| 2429 | memory_ptr += segment.size; | |
| 2430 | segment.offset = offset; | |
| 2431 | offset += segment.size; | |
| 2292 | pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type { | |
| 2293 | _ = index; | |
| 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 | |
| 2435 | if (shared_memory and wasm.hasPassiveInitializationSegments()) { | |
| 2436 | // align to pointer size | |
| 2437 | memory_ptr = mem.alignForward(u64, memory_ptr, 4); | |
| 2438 | const loc = try wasm.createSyntheticSymbol(wasm.preloaded_strings.__wasm_init_memory_flag, .data); | |
| 2439 | const sym = wasm.symbolLocSymbol(loc); | |
| 2440 | sym.mark(); | |
| 2441 | sym.virtual_address = @as(u32, @intCast(memory_ptr)); | |
| 2442 | memory_ptr += 4; | |
| 2299 | /// 0. Index into `Wasm.object_function_imports`. | |
| 2300 | /// 1. Index into `Wasm.imports`. | |
| 2301 | pub const FunctionImportId = enum(u32) { | |
| 2302 | _, | |
| 2303 | ||
| 2304 | pub const Unpacked = union(enum) { | |
| 2305 | object_function_import: FunctionImport.Index, | |
| 2306 | zcu_import: ZcuImportIndex, | |
| 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) { | |
| 2446 | memory_ptr = stack_alignment.forward(memory_ptr); | |
| 2447 | memory_ptr += wasm.base.stack_size; | |
| 2448 | if (stack_ptr) |index| { | |
| 2449 | wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); | |
| 2450 | } | |
| 2316 | pub fn unpack(id: FunctionImportId, wasm: *const Wasm) Unpacked { | |
| 2317 | const i = @intFromEnum(id); | |
| 2318 | if (i < wasm.object_function_imports.entries.len) return .{ .object_function_import = @enumFromInt(i) }; | |
| 2319 | const zcu_import_i = i - wasm.object_function_imports.entries.len; | |
| 2320 | return .{ .zcu_import = @enumFromInt(zcu_import_i) }; | |
| 2451 | 2321 | } |
| 2452 | 2322 | |
| 2453 | // One of the linked object files has a reference to the __heap_base symbol. | |
| 2454 | // We must set its virtual address so it can be used in relocations. | |
| 2455 | if (wasm.globals.get(wasm.preloaded_strings.__heap_base)) |loc| { | |
| 2456 | const symbol = wasm.symbolLocSymbol(loc); | |
| 2457 | symbol.virtual_address = @intCast(heap_alignment.forward(memory_ptr)); | |
| 2323 | pub fn fromObject(function_import_index: FunctionImport.Index, wasm: *const Wasm) FunctionImportId { | |
| 2324 | return pack(.{ .object_function_import = function_import_index }, wasm); | |
| 2458 | 2325 | } |
| 2459 | 2326 | |
| 2460 | // Setup the max amount of pages | |
| 2461 | // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 | |
| 2462 | const max_memory_allowed: u64 = (1 << 32) - 1; | |
| 2327 | pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) FunctionImportId { | |
| 2328 | return pack(.{ .zcu_import = zcu_import }, wasm); | |
| 2329 | } | |
| 2463 | 2330 | |
| 2464 | if (wasm.initial_memory) |initial_memory| { | |
| 2465 | if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { | |
| 2466 | var err = try diags.addErrorWithNotes(0); | |
| 2467 | try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size}); | |
| 2468 | } | |
| 2469 | if (memory_ptr > initial_memory) { | |
| 2470 | var err = try diags.addErrorWithNotes(0); | |
| 2471 | try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr}); | |
| 2472 | } | |
| 2473 | if (initial_memory > max_memory_allowed) { | |
| 2474 | var err = try diags.addErrorWithNotes(0); | |
| 2475 | try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed}); | |
| 2331 | /// This function is allowed O(N) lookup because it is only called during | |
| 2332 | /// diagnostic generation. | |
| 2333 | pub fn sourceLocation(id: FunctionImportId, wasm: *const Wasm) SourceLocation { | |
| 2334 | switch (id.unpack(wasm)) { | |
| 2335 | .object_function_import => |obj_func_index| { | |
| 2336 | // TODO binary search | |
| 2337 | for (wasm.objects.items, 0..) |o, i| { | |
| 2338 | if (o.function_imports.off <= @intFromEnum(obj_func_index) and | |
| 2339 | o.function_imports.off + o.function_imports.len > @intFromEnum(obj_func_index)) | |
| 2340 | { | |
| 2341 | return .pack(.{ .object_index = @enumFromInt(i) }, wasm); | |
| 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| { | |
| 2486 | const symbol = wasm.symbolLocSymbol(loc); | |
| 2487 | symbol.virtual_address = @as(u32, @intCast(memory_ptr)); | |
| 2349 | pub fn importName(id: FunctionImportId, wasm: *const Wasm) String { | |
| 2350 | return switch (unpack(id, wasm)) { | |
| 2351 | inline .object_function_import, .zcu_import => |i| i.importName(wasm), | |
| 2352 | }; | |
| 2488 | 2353 | } |
| 2489 | 2354 | |
| 2490 | if (wasm.max_memory) |max_memory| { | |
| 2491 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { | |
| 2492 | var err = try diags.addErrorWithNotes(0); | |
| 2493 | try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size}); | |
| 2494 | } | |
| 2495 | if (memory_ptr > max_memory) { | |
| 2496 | var err = try diags.addErrorWithNotes(0); | |
| 2497 | try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr}); | |
| 2498 | } | |
| 2499 | if (max_memory > max_memory_allowed) { | |
| 2500 | var err = try diags.addErrorWithNotes(0); | |
| 2501 | try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed}); | |
| 2502 | } | |
| 2503 | wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); | |
| 2504 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); | |
| 2505 | if (shared_memory) { | |
| 2506 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED); | |
| 2355 | pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString { | |
| 2356 | return switch (unpack(id, wasm)) { | |
| 2357 | inline .object_function_import, .zcu_import => |i| i.moduleName(wasm), | |
| 2358 | }; | |
| 2359 | } | |
| 2360 | ||
| 2361 | pub fn functionType(id: FunctionImportId, wasm: *Wasm) FunctionType.Index { | |
| 2362 | return switch (unpack(id, wasm)) { | |
| 2363 | inline .object_function_import, .zcu_import => |i| i.functionType(wasm), | |
| 2364 | }; | |
| 2365 | } | |
| 2366 | ||
| 2367 | /// Asserts not emitting an object, and `Wasm.import_symbols` is false. | |
| 2368 | pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool { | |
| 2369 | assert(!wasm.import_symbols); | |
| 2370 | assert(wasm.base.comp.config.output_mode != .Obj); | |
| 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 | |
| 2513 | /// index of the segment within the final data section. When the segment does not yet | |
| 2514 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. | |
| 2515 | pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Segment.Index { | |
| 2516 | const comp = wasm.base.comp; | |
| 2517 | const gpa = comp.gpa; | |
| 2518 | const diags = &wasm.base.comp.link_diags; | |
| 2519 | const symbol = objectSymbols(wasm, object_id)[@intFromEnum(symbol_index)]; | |
| 2520 | const index: Segment.Index = @enumFromInt(wasm.segments.items.len); | |
| 2521 | const shared_memory = comp.config.shared_memory; | |
| 2432 | pub fn importName(id: GlobalImportId, wasm: *const Wasm) String { | |
| 2433 | return switch (unpack(id, wasm)) { | |
| 2434 | inline .object_global_import, .zcu_import => |i| i.importName(wasm), | |
| 2435 | }; | |
| 2436 | } | |
| 2522 | 2437 | |
| 2523 | switch (symbol.tag) { | |
| 2524 | .data => { | |
| 2525 | const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index]; | |
| 2526 | const merge_segment = comp.config.output_mode != .Obj; | |
| 2527 | const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); | |
| 2528 | if (!result.found_existing) { | |
| 2529 | result.value_ptr.* = index; | |
| 2530 | var flags: u32 = 0; | |
| 2531 | if (shared_memory) { | |
| 2532 | flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); | |
| 2533 | } | |
| 2534 | try wasm.segments.append(gpa, .{ | |
| 2535 | .alignment = .@"1", | |
| 2536 | .size = 0, | |
| 2537 | .offset = 0, | |
| 2538 | .flags = flags, | |
| 2539 | }); | |
| 2540 | try wasm.segment_info.putNoClobber(gpa, index, .{ | |
| 2541 | .name = try gpa.dupe(u8, segment_info.name), | |
| 2542 | .alignment = segment_info.alignment, | |
| 2543 | .flags = segment_info.flags, | |
| 2544 | }); | |
| 2545 | return index; | |
| 2546 | } else return result.value_ptr.*; | |
| 2547 | }, | |
| 2548 | .function => return wasm.code_section_index.unwrap() orelse blk: { | |
| 2549 | wasm.code_section_index = index.toOptional(); | |
| 2550 | try wasm.appendDummySegment(); | |
| 2551 | break :blk index; | |
| 2552 | }, | |
| 2553 | .section => { | |
| 2554 | const section_name = wasm.objectSymbol(object_id, symbol_index).name; | |
| 2555 | ||
| 2556 | inline for (@typeInfo(CustomSections).@"struct".fields) |field| { | |
| 2557 | if (@field(wasm.custom_sections, field.name).name == section_name) { | |
| 2558 | const field_ptr = &@field(wasm.custom_sections, field.name).index; | |
| 2559 | return field_ptr.unwrap() orelse { | |
| 2560 | field_ptr.* = index.toOptional(); | |
| 2561 | try wasm.appendDummySegment(); | |
| 2562 | return index; | |
| 2563 | }; | |
| 2564 | } | |
| 2565 | } else { | |
| 2566 | return diags.failParse(objectPath(wasm, object_id), "unknown section: {s}", .{ | |
| 2567 | wasm.stringSlice(section_name), | |
| 2568 | }); | |
| 2569 | } | |
| 2438 | pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString { | |
| 2439 | return switch (unpack(id, wasm)) { | |
| 2440 | inline .object_global_import, .zcu_import => |i| i.moduleName(wasm), | |
| 2441 | }; | |
| 2442 | } | |
| 2443 | ||
| 2444 | pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type { | |
| 2445 | return switch (unpack(id, wasm)) { | |
| 2446 | inline .object_global_import, .zcu_import => |i| i.globalType(wasm), | |
| 2447 | }; | |
| 2448 | } | |
| 2449 | }; | |
| 2450 | ||
| 2451 | /// 0. Index into `Wasm.object_data_imports`. | |
| 2452 | /// 1. Index into `Wasm.imports`. | |
| 2453 | pub const DataImportId = enum(u32) { | |
| 2454 | _, | |
| 2455 | ||
| 2456 | pub const Unpacked = union(enum) { | |
| 2457 | object_data_import: ObjectDataImport.Index, | |
| 2458 | zcu_import: ZcuImportIndex, | |
| 2459 | }; | |
| 2460 | ||
| 2461 | pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId { | |
| 2462 | return switch (unpacked) { | |
| 2463 | .object_data_import => |i| @enumFromInt(@intFromEnum(i)), | |
| 2464 | .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_data_imports.entries.len), | |
| 2465 | }; | |
| 2466 | } | |
| 2467 | ||
| 2468 | pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked { | |
| 2469 | const i = @intFromEnum(id); | |
| 2470 | if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @enumFromInt(i) }; | |
| 2471 | const zcu_import_i = i - wasm.object_data_imports.entries.len; | |
| 2472 | return .{ .zcu_import = @enumFromInt(zcu_import_i) }; | |
| 2473 | } | |
| 2474 | ||
| 2475 | pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId { | |
| 2476 | return pack(.{ .zcu_import = zcu_import }, wasm); | |
| 2477 | } | |
| 2478 | ||
| 2479 | pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId { | |
| 2480 | return pack(.{ .object_data_import = object_data_import }, wasm); | |
| 2481 | } | |
| 2482 | ||
| 2483 | pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation { | |
| 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 | |
| 2576 | fn appendDummySegment(wasm: *Wasm) !void { | |
| 2577 | const gpa = wasm.base.comp.gpa; | |
| 2578 | try wasm.segments.append(gpa, .{ | |
| 2579 | .alignment = .@"1", | |
| 2580 | .size = 0, | |
| 2581 | .offset = 0, | |
| 2582 | .flags = 0, | |
| 2583 | }); | |
| 3285 | pub fn updateExports( | |
| 3286 | wasm: *Wasm, | |
| 3287 | pt: Zcu.PerThread, | |
| 3288 | exported: Zcu.Exported, | |
| 3289 | export_indices: []const Zcu.Export.Index, | |
| 3290 | ) !void { | |
| 3291 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | |
| 3292 | @panic("Attempted to compile for object format that was disabled by build configuration"); | |
| 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 | 3313 | pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| ... | ... | @@ -2596,7 +3323,9 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { |
| 2596 | 3323 | .res => unreachable, |
| 2597 | 3324 | .dso_exact => unreachable, |
| 2598 | 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 | 3341 | pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 2613 | 3342 | const comp = wasm.base.comp; |
| 2614 | 3343 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 3344 | const diags = &comp.link_diags; | |
| 2615 | 3345 | |
| 2616 | 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 | 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 | 3357 | const tracy = trace(@src()); |
| 2624 | 3358 | defer tracy.end(); |
| 2625 | 3359 | |
| 2626 | const comp = wasm.base.comp; | |
| 2627 | const diags = &comp.link_diags; | |
| 2628 | if (wasm.llvm_object) |llvm_object| { | |
| 2629 | try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); | |
| 2630 | const use_lld = build_options.have_llvm and comp.config.use_lld; | |
| 2631 | if (use_lld) return; | |
| 2632 | } | |
| 2633 | ||
| 2634 | if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items); | |
| 2635 | ||
| 2636 | const sub_prog_node = prog_node.start("Wasm Flush", 0); | |
| 3360 | const sub_prog_node = prog_node.start("Wasm Prelink", 0); | |
| 2637 | 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 | 3363 | const comp = wasm.base.comp; |
| 2700 | const diags = &comp.link_diags; | |
| 2701 | 3364 | const gpa = comp.gpa; |
| 2702 | const use_llvm = comp.config.use_llvm; | |
| 2703 | const use_lld = build_options.have_llvm and comp.config.use_lld; | |
| 2704 | const shared_memory = comp.config.shared_memory; | |
| 2705 | const import_memory = comp.config.import_memory; | |
| 2706 | const export_memory = comp.config.export_memory; | |
| 3365 | const rdynamic = comp.config.rdynamic; | |
| 3366 | const is_obj = comp.config.output_mode == .Obj; | |
| 2707 | 3367 | |
| 2708 | // Size of each section header | |
| 2709 | const header_size = 5 + 1; | |
| 2710 | // The amount of sections that will be written | |
| 2711 | var section_count: u32 = 0; | |
| 2712 | // Index of the code section. Used to tell relocation table where the section lives. | |
| 2713 | var code_section_index: ?u32 = null; | |
| 2714 | // Index of the data section. Used to tell relocation table where the section lives. | |
| 2715 | var data_section_index: ?u32 = null; | |
| 2716 | const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld); | |
| 2717 | ||
| 2718 | var binary_bytes = std.ArrayList(u8).init(gpa); | |
| 2719 | defer binary_bytes.deinit(); | |
| 2720 | const binary_writer = binary_bytes.writer(); | |
| 2721 | ||
| 2722 | // We write the magic bytes at the end so they will only be written | |
| 2723 | // if everything succeeded as expected. So populate with 0's for now. | |
| 2724 | try binary_writer.writeAll(&[_]u8{0} ** 8); | |
| 2725 | // (Re)set file pointer to 0 | |
| 2726 | try wasm.base.file.?.setEndPos(0); | |
| 2727 | try wasm.base.file.?.seekTo(0); | |
| 2728 | ||
| 2729 | // Type section | |
| 2730 | if (wasm.func_types.items.len != 0) { | |
| 2731 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2732 | log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len}); | |
| 2733 | for (wasm.func_types.items) |func_type| { | |
| 2734 | try leb.writeUleb128(binary_writer, std.wasm.function_type); | |
| 2735 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.params.len))); | |
| 2736 | for (func_type.params) |param_ty| { | |
| 2737 | try leb.writeUleb128(binary_writer, std.wasm.valtype(param_ty)); | |
| 3368 | assert(wasm.missing_exports.entries.len == 0); | |
| 3369 | for (wasm.export_symbol_names) |exp_name| { | |
| 3370 | const exp_name_interned = try wasm.internString(exp_name); | |
| 3371 | if (wasm.object_function_imports.getPtr(exp_name_interned)) |import| { | |
| 3372 | if (import.resolution != .unresolved) { | |
| 3373 | import.flags.exported = true; | |
| 3374 | continue; | |
| 2738 | 3375 | } |
| 2739 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(func_type.returns.len))); | |
| 2740 | for (func_type.returns) |ret_ty| { | |
| 2741 | try leb.writeUleb128(binary_writer, std.wasm.valtype(ret_ty)); | |
| 3376 | } | |
| 3377 | if (wasm.object_global_imports.getPtr(exp_name_interned)) |import| { | |
| 3378 | if (import.resolution != .unresolved) { | |
| 3379 | import.flags.exported = true; | |
| 3380 | continue; | |
| 2742 | 3381 | } |
| 2743 | 3382 | } |
| 2744 | ||
| 2745 | try writeVecSectionHeader( | |
| 2746 | binary_bytes.items, | |
| 2747 | header_offset, | |
| 2748 | .type, | |
| 2749 | @intCast(binary_bytes.items.len - header_offset - header_size), | |
| 2750 | @intCast(wasm.func_types.items.len), | |
| 2751 | ); | |
| 2752 | section_count += 1; | |
| 3383 | if (wasm.object_table_imports.getPtr(exp_name_interned)) |import| { | |
| 3384 | if (import.resolution != .unresolved) { | |
| 3385 | import.flags.exported = true; | |
| 3386 | continue; | |
| 3387 | } | |
| 3388 | } | |
| 3389 | try wasm.missing_exports.put(gpa, exp_name_interned, {}); | |
| 2753 | 3390 | } |
| 2754 | 3391 | |
| 2755 | // Import section | |
| 2756 | if (wasm.imports.count() != 0 or import_memory) { | |
| 2757 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2758 | ||
| 2759 | var it = wasm.imports.iterator(); | |
| 2760 | while (it.next()) |entry| { | |
| 2761 | assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined()); | |
| 2762 | const import = entry.value_ptr.*; | |
| 2763 | try wasm.emitImport(binary_writer, import); | |
| 3392 | if (wasm.entry_name.unwrap()) |entry_name| { | |
| 3393 | if (wasm.object_function_imports.getPtr(entry_name)) |import| { | |
| 3394 | if (import.resolution != .unresolved) { | |
| 3395 | import.flags.exported = true; | |
| 3396 | wasm.entry_resolution = import.resolution; | |
| 3397 | } | |
| 2764 | 3398 | } |
| 3399 | } | |
| 2765 | 3400 | |
| 2766 | if (import_memory) { | |
| 2767 | const mem_imp: Import = .{ | |
| 2768 | .module_name = wasm.host_name, | |
| 2769 | .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory, | |
| 2770 | .kind = .{ .memory = wasm.memories.limits }, | |
| 2771 | }; | |
| 2772 | try wasm.emitImport(binary_writer, mem_imp); | |
| 3401 | if (comp.zcu != null) { | |
| 3402 | // Zig always depends on a stack pointer global. | |
| 3403 | // If emitting an object, it's an import. Otherwise, the linker synthesizes it. | |
| 3404 | if (is_obj) { | |
| 3405 | @panic("TODO"); | |
| 3406 | } else { | |
| 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 | |
| 2786 | if (wasm.functions.count() != 0) { | |
| 2787 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2788 | for (wasm.functions.values()) |function| { | |
| 2789 | try leb.writeUleb128(binary_writer, function.func.type_index); | |
| 3412 | // These loops do both recursive marking of alive symbols well as checking for undefined symbols. | |
| 3413 | // At the end, output functions and globals will be populated. | |
| 3414 | for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| { | |
| 3415 | if (import.flags.isIncluded(rdynamic)) { | |
| 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 | ||
| 2802 | // Table section | |
| 2803 | if (wasm.tables.items.len > 0) { | |
| 2804 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2805 | ||
| 2806 | for (wasm.tables.items) |table| { | |
| 2807 | try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype)); | |
| 2808 | try emitLimits(binary_writer, table.limits); | |
| 3419 | // Also treat init functions as roots. | |
| 3420 | for (wasm.object_init_funcs.items) |init_func| { | |
| 3421 | const func = init_func.function_index.ptr(wasm); | |
| 3422 | if (func.object_index.ptr(wasm).is_included) { | |
| 3423 | try markFunction(wasm, init_func.function_index, false); | |
| 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 | |
| 2822 | if (!import_memory) { | |
| 2823 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2824 | ||
| 2825 | try emitLimits(binary_writer, wasm.memories.limits); | |
| 2826 | try writeVecSectionHeader( | |
| 2827 | binary_bytes.items, | |
| 2828 | header_offset, | |
| 2829 | .memory, | |
| 2830 | @intCast(binary_bytes.items.len - header_offset - header_size), | |
| 2831 | 1, // wasm currently only supports 1 linear memory segment | |
| 2832 | ); | |
| 2833 | section_count += 1; | |
| 3428 | for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| { | |
| 3429 | if (import.flags.isIncluded(rdynamic)) { | |
| 3430 | try markGlobalImport(wasm, name, import, @enumFromInt(i)); | |
| 3431 | } | |
| 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) | |
| 2837 | if (wasm.wasm_globals.items.len > 0) { | |
| 2838 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2839 | ||
| 2840 | for (wasm.wasm_globals.items) |global| { | |
| 2841 | try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype)); | |
| 2842 | try binary_writer.writeByte(@intFromBool(global.global_type.mutable)); | |
| 2843 | try emitInit(binary_writer, global.init); | |
| 3436 | for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| { | |
| 3437 | if (import.flags.isIncluded(rdynamic)) { | |
| 3438 | try markTableImport(wasm, name, import, @enumFromInt(i)); | |
| 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 | |
| 2857 | if (wasm.exports.items.len != 0 or export_memory) { | |
| 2858 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2859 | ||
| 2860 | for (wasm.exports.items) |exp| { | |
| 2861 | const name = wasm.stringSlice(exp.name); | |
| 2862 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | |
| 2863 | try binary_writer.writeAll(name); | |
| 2864 | try leb.writeUleb128(binary_writer, @intFromEnum(exp.kind)); | |
| 2865 | try leb.writeUleb128(binary_writer, exp.index); | |
| 3442 | for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| { | |
| 3443 | if (import.flags.isIncluded(rdynamic)) { | |
| 3444 | try markDataImport(wasm, name, import, @enumFromInt(i)); | |
| 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| { | |
| 2886 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2887 | try writeVecSectionHeader( | |
| 2888 | binary_bytes.items, | |
| 2889 | header_offset, | |
| 2890 | .start, | |
| 2891 | @intCast(binary_bytes.items.len - header_offset - header_size), | |
| 2892 | entry_index, | |
| 2893 | ); | |
| 3448 | // This is a wild ass guess at how to merge memories, haven't checked yet | |
| 3449 | // what the proper way to do this is. | |
| 3450 | for (wasm.object_memory_imports.values()) |*memory_import| { | |
| 3451 | wasm.memories.limits.min = @min(wasm.memories.limits.min, memory_import.limits_min); | |
| 3452 | wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max); | |
| 3453 | wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max; | |
| 2894 | 3454 | } |
| 2895 | 3455 | |
| 2896 | // element section (function table) | |
| 2897 | if (wasm.function_table.count() > 0) { | |
| 2898 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2899 | ||
| 2900 | const table_loc = wasm.globals.get(wasm.preloaded_strings.__indirect_function_table).?; | |
| 2901 | const table_sym = wasm.symbolLocSymbol(table_loc); | |
| 2902 | ||
| 2903 | const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually | |
| 2904 | try leb.writeUleb128(binary_writer, flags); | |
| 2905 | if (flags == 0x02) { | |
| 2906 | try leb.writeUleb128(binary_writer, table_sym.index); | |
| 2907 | } | |
| 2908 | try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid | |
| 2909 | if (flags == 0x02) { | |
| 2910 | try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref | |
| 2911 | } | |
| 2912 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count()))); | |
| 2913 | var symbol_it = wasm.function_table.keyIterator(); | |
| 2914 | while (symbol_it.next()) |symbol_loc_ptr| { | |
| 2915 | const sym = wasm.symbolLocSymbol(symbol_loc_ptr.*); | |
| 2916 | std.debug.assert(sym.isAlive()); | |
| 2917 | std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count); | |
| 2918 | try leb.writeUleb128(binary_writer, sym.index); | |
| 2919 | } | |
| 2920 | ||
| 2921 | try writeVecSectionHeader( | |
| 2922 | binary_bytes.items, | |
| 2923 | header_offset, | |
| 2924 | .element, | |
| 2925 | @intCast(binary_bytes.items.len - header_offset - header_size), | |
| 2926 | 1, | |
| 2927 | ); | |
| 2928 | section_count += 1; | |
| 2929 | } | |
| 2930 | ||
| 2931 | // When the shared-memory option is enabled, we *must* emit the 'data count' section. | |
| 2932 | const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory); | |
| 2933 | if (data_segments_count != 0 and shared_memory) { | |
| 2934 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2935 | try writeVecSectionHeader( | |
| 2936 | binary_bytes.items, | |
| 2937 | header_offset, | |
| 2938 | .data_count, | |
| 2939 | @intCast(binary_bytes.items.len - header_offset - header_size), | |
| 2940 | @intCast(data_segments_count), | |
| 2941 | ); | |
| 2942 | } | |
| 3456 | wasm.function_imports_len_prelink = @intCast(wasm.function_imports.entries.len); | |
| 3457 | wasm.data_imports_len_prelink = @intCast(wasm.data_imports.entries.len); | |
| 3458 | } | |
| 2943 | 3459 | |
| 2944 | // Code section | |
| 2945 | if (wasm.code_section_index != .none) { | |
| 2946 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2947 | const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count | |
| 3460 | pub fn markFunctionImport( | |
| 3461 | wasm: *Wasm, | |
| 3462 | name: String, | |
| 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(); | |
| 2950 | while (func_it.next()) |entry| { | |
| 2951 | const sym_loc: SymbolLoc = .{ .index = entry.value_ptr.sym_index, .file = entry.key_ptr.file }; | |
| 2952 | const atom_index = wasm.symbol_atom.get(sym_loc).?; | |
| 2953 | const atom = wasm.getAtomPtr(atom_index); | |
| 3469 | const comp = wasm.base.comp; | |
| 3470 | const gpa = comp.gpa; | |
| 2954 | 3471 | |
| 2955 | if (!is_obj) { | |
| 2956 | atom.resolveRelocs(wasm); | |
| 2957 | } | |
| 2958 | atom.offset = @intCast(binary_bytes.items.len - start_offset); | |
| 2959 | try leb.writeUleb128(binary_writer, atom.size); | |
| 2960 | try binary_writer.writeAll(atom.code.items); | |
| 3472 | try wasm.functions.ensureUnusedCapacity(gpa, 1); | |
| 3473 | ||
| 3474 | if (import.resolution == .unresolved) { | |
| 3475 | if (name == wasm.preloaded_strings.__wasm_init_memory) { | |
| 3476 | try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{}); | |
| 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( | |
| 2964 | binary_bytes.items, | |
| 2965 | header_offset, | |
| 2966 | .code, | |
| 2967 | @intCast(binary_bytes.items.len - header_offset - header_size), | |
| 2968 | @intCast(wasm.functions.count()), | |
| 2969 | ); | |
| 2970 | code_section_index = section_count; | |
| 2971 | section_count += 1; | |
| 2972 | } | |
| 2973 | ||
| 2974 | // Data section | |
| 2975 | if (data_segments_count != 0) { | |
| 2976 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | |
| 2977 | ||
| 2978 | var it = wasm.data_segments.iterator(); | |
| 2979 | var segment_count: u32 = 0; | |
| 2980 | while (it.next()) |entry| { | |
| 2981 | // do not output 'bss' section unless we import memory and therefore | |
| 2982 | // want to guarantee the data is zero initialized | |
| 2983 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; | |
| 2984 | const segment_index = entry.value_ptr.*; | |
| 2985 | const segment = wasm.segmentPtr(segment_index); | |
| 2986 | if (segment.size == 0) continue; // do not emit empty segments | |
| 2987 | segment_count += 1; | |
| 2988 | var atom_index = wasm.atoms.get(segment_index).?; | |
| 2989 | ||
| 2990 | try leb.writeUleb128(binary_writer, segment.flags); | |
| 2991 | if (segment.flags & @intFromEnum(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) { | |
| 2992 | try leb.writeUleb128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry | |
| 2993 | } | |
| 2994 | // when a segment is passive, it's initialized during runtime. | |
| 2995 | if (!segment.isPassive()) { | |
| 2996 | try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment.offset)) }); | |
| 2997 | } | |
| 2998 | // offset into data section | |
| 2999 | try leb.writeUleb128(binary_writer, segment.size); | |
| 3000 | ||
| 3001 | // fill in the offset table and the data segments | |
| 3002 | var current_offset: u32 = 0; | |
| 3003 | while (true) { | |
| 3004 | const atom = wasm.getAtomPtr(atom_index); | |
| 3005 | if (!is_obj) { | |
| 3006 | atom.resolveRelocs(wasm); | |
| 3007 | } | |
| 3491 | /// Recursively mark alive everything referenced by the function. | |
| 3492 | fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.File.FlushError!void { | |
| 3493 | const comp = wasm.base.comp; | |
| 3494 | const gpa = comp.gpa; | |
| 3495 | const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i)); | |
| 3496 | if (gop.found_existing) return; | |
| 3008 | 3497 | |
| 3009 | // Pad with zeroes to ensure all segments are aligned | |
| 3010 | if (current_offset != atom.offset) { | |
| 3011 | const diff = atom.offset - current_offset; | |
| 3012 | try binary_writer.writeByteNTimes(0, diff); | |
| 3013 | current_offset += diff; | |
| 3014 | } | |
| 3015 | assert(current_offset == atom.offset); | |
| 3016 | assert(atom.code.items.len == atom.size); | |
| 3017 | try binary_writer.writeAll(atom.code.items); | |
| 3498 | const rdynamic = comp.config.rdynamic; | |
| 3499 | const is_obj = comp.config.output_mode == .Obj; | |
| 3500 | const function = i.ptr(wasm); | |
| 3501 | markObject(wasm, function.object_index); | |
| 3018 | 3502 | |
| 3019 | current_offset += atom.size; | |
| 3020 | if (atom.prev != .null) { | |
| 3021 | atom_index = atom.prev; | |
| 3022 | } else { | |
| 3023 | // also pad with zeroes when last atom to ensure | |
| 3024 | // segments are aligned. | |
| 3025 | if (current_offset != segment.size) { | |
| 3026 | try binary_writer.writeByteNTimes(0, segment.size - current_offset); | |
| 3027 | current_offset += segment.size - current_offset; | |
| 3028 | } | |
| 3029 | break; | |
| 3030 | } | |
| 3031 | } | |
| 3032 | assert(current_offset == segment.size); | |
| 3503 | if (!is_obj and (override_export or function.flags.isExported(rdynamic))) { | |
| 3504 | const symbol_name = function.name.unwrap().?; | |
| 3505 | if (!override_export and function.flags.visibility_hidden) { | |
| 3506 | try wasm.hidden_function_exports.put(gpa, symbol_name, @enumFromInt(gop.index)); | |
| 3507 | } else { | |
| 3508 | try wasm.function_exports.put(gpa, symbol_name, @enumFromInt(gop.index)); | |
| 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) { | |
| 3047 | // relocations need to point to the index of a symbol in the final symbol table. To save memory, | |
| 3048 | // we never store all symbols in a single table, but store a location reference instead. | |
| 3049 | // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections. | |
| 3050 | var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena); | |
| 3051 | try wasm.emitLinkSection(&binary_bytes, &symbol_table); | |
| 3052 | if (code_section_index) |code_index| { | |
| 3053 | try wasm.emitCodeRelocations(&binary_bytes, code_index, symbol_table); | |
| 3054 | } | |
| 3055 | if (data_section_index) |data_index| { | |
| 3056 | try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table); | |
| 3057 | } | |
| 3058 | } else if (comp.config.debug_format != .strip) { | |
| 3059 | try wasm.emitNameSection(&binary_bytes, arena); | |
| 3060 | } | |
| 3061 | ||
| 3062 | if (comp.config.debug_format != .strip) { | |
| 3063 | // The build id must be computed on the main sections only, | |
| 3064 | // so we have to do it now, before the debug sections. | |
| 3065 | switch (wasm.base.build_id) { | |
| 3066 | .none => {}, | |
| 3067 | .fast => { | |
| 3068 | var id: [16]u8 = undefined; | |
| 3069 | std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{}); | |
| 3070 | var uuid: [36]u8 = undefined; | |
| 3071 | _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{ | |
| 3072 | std.fmt.fmtSliceHexLower(id[0..4]), | |
| 3073 | std.fmt.fmtSliceHexLower(id[4..6]), | |
| 3074 | std.fmt.fmtSliceHexLower(id[6..8]), | |
| 3075 | std.fmt.fmtSliceHexLower(id[8..10]), | |
| 3076 | std.fmt.fmtSliceHexLower(id[10..]), | |
| 3077 | }); | |
| 3078 | try emitBuildIdSection(&binary_bytes, &uuid); | |
| 3079 | }, | |
| 3080 | .hexstring => |hs| { | |
| 3081 | var buffer: [32 * 2]u8 = undefined; | |
| 3082 | const str = std.fmt.bufPrint(&buffer, "{s}", .{ | |
| 3083 | std.fmt.fmtSliceHexLower(hs.toSlice()), | |
| 3084 | }) catch unreachable; | |
| 3085 | try emitBuildIdSection(&binary_bytes, str); | |
| 3086 | }, | |
| 3087 | else => |mode| { | |
| 3088 | var err = try diags.addErrorWithNotes(0); | |
| 3089 | try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}); | |
| 3090 | }, | |
| 3091 | } | |
| 3512 | try wasm.markRelocations(function.relocations(wasm)); | |
| 3513 | } | |
| 3092 | 3514 | |
| 3093 | var debug_bytes = std.ArrayList(u8).init(gpa); | |
| 3094 | defer debug_bytes.deinit(); | |
| 3515 | fn markObject(wasm: *Wasm, i: ObjectIndex) void { | |
| 3516 | i.ptr(wasm).is_included = true; | |
| 3517 | } | |
| 3095 | 3518 | |
| 3096 | inline for (@typeInfo(CustomSections).@"struct".fields) |field| { | |
| 3097 | if (@field(wasm.custom_sections, field.name).index.unwrap()) |index| { | |
| 3098 | var atom = wasm.getAtomPtr(wasm.atoms.get(index).?); | |
| 3099 | while (true) { | |
| 3100 | atom.resolveRelocs(wasm); | |
| 3101 | try debug_bytes.appendSlice(atom.code.items); | |
| 3102 | if (atom.prev == .null) break; | |
| 3103 | atom = wasm.getAtomPtr(atom.prev); | |
| 3104 | } | |
| 3105 | try emitDebugSection(&binary_bytes, debug_bytes.items, field.name); | |
| 3106 | debug_bytes.clearRetainingCapacity(); | |
| 3107 | } | |
| 3108 | } | |
| 3519 | /// Recursively mark alive everything referenced by the global. | |
| 3520 | fn markGlobalImport( | |
| 3521 | wasm: *Wasm, | |
| 3522 | name: String, | |
| 3523 | import: *GlobalImport, | |
| 3524 | global_index: GlobalImport.Index, | |
| 3525 | ) link.File.FlushError!void { | |
| 3526 | if (import.flags.alive) return; | |
| 3527 | import.flags.alive = true; | |
| 3109 | 3528 | |
| 3110 | try emitProducerSection(&binary_bytes); | |
| 3111 | if (feature_count > 0) { | |
| 3112 | try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count); | |
| 3113 | } | |
| 3114 | } | |
| 3529 | const comp = wasm.base.comp; | |
| 3530 | const gpa = comp.gpa; | |
| 3115 | 3531 | |
| 3116 | // Only when writing all sections executed properly we write the magic | |
| 3117 | // bytes. This allows us to easily detect what went wrong while generating | |
| 3118 | // the final binary. | |
| 3119 | { | |
| 3120 | const src = std.wasm.magic ++ std.wasm.version; | |
| 3121 | binary_bytes.items[0..src.len].* = src; | |
| 3532 | try wasm.globals.ensureUnusedCapacity(gpa, 1); | |
| 3533 | ||
| 3534 | if (import.resolution == .unresolved) { | |
| 3535 | if (name == wasm.preloaded_strings.__heap_base) { | |
| 3536 | import.resolution = .__heap_base; | |
| 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 { | |
| 3151 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | |
| 3561 | fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.File.FlushError!void { | |
| 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(); | |
| 3154 | const producers = "producers"; | |
| 3155 | try leb.writeUleb128(writer, @as(u32, @intCast(producers.len))); | |
| 3156 | try writer.writeAll(producers); | |
| 3567 | const rdynamic = comp.config.rdynamic; | |
| 3568 | const is_obj = comp.config.output_mode == .Obj; | |
| 3569 | const global = i.ptr(wasm); | |
| 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 | |
| 3161 | var version_buf: [100]u8 = undefined; | |
| 3162 | const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver}); | |
| 3576 | try wasm.markRelocations(global.relocations(wasm)); | |
| 3577 | } | |
| 3163 | 3578 | |
| 3164 | // language field | |
| 3165 | { | |
| 3166 | const language = "language"; | |
| 3167 | try leb.writeUleb128(writer, @as(u32, @intCast(language.len))); | |
| 3168 | try writer.writeAll(language); | |
| 3579 | fn markTableImport( | |
| 3580 | wasm: *Wasm, | |
| 3581 | name: String, | |
| 3582 | import: *TableImport, | |
| 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) | |
| 3171 | try leb.writeUleb128(writer, @as(u32, 1)); | |
| 3588 | const comp = wasm.base.comp; | |
| 3589 | const gpa = comp.gpa; | |
| 3172 | 3590 | |
| 3173 | // versioned name | |
| 3174 | { | |
| 3175 | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" | |
| 3176 | try writer.writeAll("Zig"); | |
| 3591 | try wasm.tables.ensureUnusedCapacity(gpa, 1); | |
| 3177 | 3592 | |
| 3178 | try leb.writeUleb128(writer, @as(u32, @intCast(version.len))); | |
| 3179 | try writer.writeAll(version); | |
| 3593 | if (import.resolution == .unresolved) { | |
| 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 | |
| 3184 | { | |
| 3185 | const processed_by = "processed-by"; | |
| 3186 | try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len))); | |
| 3187 | try writer.writeAll(processed_by); | |
| 3188 | ||
| 3189 | // field_value_count (TODO: Parse object files for producer sections to detect other used tools) | |
| 3190 | try leb.writeUleb128(writer, @as(u32, 1)); | |
| 3191 | ||
| 3192 | // versioned name | |
| 3193 | { | |
| 3194 | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" | |
| 3195 | try writer.writeAll("Zig"); | |
| 3606 | fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void { | |
| 3607 | const comp = wasm.base.comp; | |
| 3608 | const segment = segment_index.ptr(wasm); | |
| 3609 | if (segment.flags.alive) return; | |
| 3610 | segment.flags.alive = true; | |
| 3196 | 3611 | |
| 3197 | try leb.writeUleb128(writer, @as(u32, @intCast(version.len))); | |
| 3198 | try writer.writeAll(version); | |
| 3199 | } | |
| 3200 | } | |
| 3612 | wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or | |
| 3613 | (comp.config.import_memory and !wasm.isBss(segment.name)); | |
| 3201 | 3614 | |
| 3202 | try writeCustomSectionHeader( | |
| 3203 | binary_bytes.items, | |
| 3204 | header_offset, | |
| 3205 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | |
| 3206 | ); | |
| 3615 | try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {}); | |
| 3616 | try wasm.markRelocations(segment.relocations(wasm)); | |
| 3207 | 3617 | } |
| 3208 | 3618 | |
| 3209 | fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8), build_id: []const u8) !void { | |
| 3210 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | |
| 3211 | ||
| 3212 | const writer = binary_bytes.writer(); | |
| 3213 | const hdr_build_id = "build_id"; | |
| 3214 | try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len))); | |
| 3215 | try writer.writeAll(hdr_build_id); | |
| 3619 | pub fn markDataImport( | |
| 3620 | wasm: *Wasm, | |
| 3621 | name: String, | |
| 3622 | import: *ObjectDataImport, | |
| 3623 | data_index: ObjectDataImport.Index, | |
| 3624 | ) link.File.FlushError!void { | |
| 3625 | if (import.flags.alive) return; | |
| 3626 | import.flags.alive = true; | |
| 3216 | 3627 | |
| 3217 | try leb.writeUleb128(writer, @as(u32, 1)); | |
| 3218 | try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len))); | |
| 3219 | try writer.writeAll(build_id); | |
| 3628 | const comp = wasm.base.comp; | |
| 3629 | const gpa = comp.gpa; | |
| 3220 | 3630 | |
| 3221 | try writeCustomSectionHeader( | |
| 3222 | binary_bytes.items, | |
| 3223 | header_offset, | |
| 3224 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | |
| 3225 | ); | |
| 3631 | if (import.resolution == .unresolved) { | |
| 3632 | if (name == wasm.preloaded_strings.__heap_base) { | |
| 3633 | import.resolution = .__heap_base; | |
| 3634 | wasm.data_segments.putAssumeCapacity(.__heap_base, {}); | |
| 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 { | |
| 3229 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | |
| 3646 | fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void { | |
| 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(); | |
| 3232 | const target_features = "target_features"; | |
| 3233 | try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len))); | |
| 3234 | try writer.writeAll(target_features); | |
| 3725 | .memory_addr_leb, | |
| 3726 | .memory_addr_sleb, | |
| 3727 | .memory_addr_i32, | |
| 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); | |
| 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); | |
| 3738 | .type_index_leb => continue, | |
| 3245 | 3739 | } |
| 3246 | 3740 | } |
| 3741 | } | |
| 3247 | 3742 | |
| 3248 | try writeCustomSectionHeader( | |
| 3249 | binary_bytes.items, | |
| 3250 | header_offset, | |
| 3251 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | |
| 3252 | ); | |
| 3743 | fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void { | |
| 3744 | try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {}); | |
| 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 | 3757 | const comp = wasm.base.comp; |
| 3257 | const import_memory = comp.config.import_memory; | |
| 3258 | const Name = struct { | |
| 3259 | index: u32, | |
| 3260 | name: []const u8, | |
| 3261 | ||
| 3262 | fn lessThan(context: void, lhs: @This(), rhs: @This()) bool { | |
| 3263 | _ = context; | |
| 3264 | return lhs.index < rhs.index; | |
| 3265 | } | |
| 3266 | }; | |
| 3758 | const use_lld = build_options.have_llvm and comp.config.use_lld; | |
| 3759 | const diags = &comp.link_diags; | |
| 3760 | const gpa = comp.gpa; | |
| 3267 | 3761 | |
| 3268 | // we must de-duplicate symbols that point to the same function | |
| 3269 | var funcs = std.AutoArrayHashMap(u32, Name).init(arena); | |
| 3270 | try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count); | |
| 3271 | var globals = try std.ArrayList(Name).initCapacity(arena, wasm.wasm_globals.items.len + wasm.imported_globals_count); | |
| 3272 | var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count()); | |
| 3273 | ||
| 3274 | for (wasm.resolved_symbols.keys()) |sym_loc| { | |
| 3275 | const symbol = wasm.symbolLocSymbol(sym_loc).*; | |
| 3276 | if (symbol.isDead()) { | |
| 3277 | continue; | |
| 3278 | } | |
| 3279 | const name = wasm.symbolLocName(sym_loc); | |
| 3280 | switch (symbol.tag) { | |
| 3281 | .function => { | |
| 3282 | const gop = funcs.getOrPutAssumeCapacity(symbol.index); | |
| 3283 | if (!gop.found_existing) { | |
| 3284 | gop.value_ptr.* = .{ .index = symbol.index, .name = name }; | |
| 3285 | } | |
| 3286 | }, | |
| 3287 | .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }), | |
| 3288 | else => {}, | |
| 3289 | } | |
| 3290 | } | |
| 3291 | // data segments are already 'ordered' | |
| 3292 | var data_segment_index: u32 = 0; | |
| 3293 | for (wasm.data_segments.keys()) |key| { | |
| 3294 | // bss section is not emitted when this condition holds true, so we also | |
| 3295 | // do not output a name for it. | |
| 3296 | if (!import_memory and std.mem.eql(u8, key, ".bss")) continue; | |
| 3297 | segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); | |
| 3298 | data_segment_index += 1; | |
| 3762 | if (wasm.llvm_object) |llvm_object| { | |
| 3763 | try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); | |
| 3764 | if (use_lld) return; | |
| 3299 | 3765 | } |
| 3300 | 3766 | |
| 3301 | mem.sort(Name, funcs.values(), {}, Name.lessThan); | |
| 3302 | mem.sort(Name, globals.items, {}, Name.lessThan); | |
| 3303 | ||
| 3304 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | |
| 3305 | const writer = binary_bytes.writer(); | |
| 3306 | try leb.writeUleb128(writer, @as(u32, @intCast("name".len))); | |
| 3307 | try writer.writeAll("name"); | |
| 3767 | if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items); | |
| 3308 | 3768 | |
| 3309 | try wasm.emitNameSubsection(.function, funcs.values(), writer); | |
| 3310 | try wasm.emitNameSubsection(.global, globals.items, writer); | |
| 3311 | try wasm.emitNameSubsection(.data_segment, segments.items, writer); | |
| 3769 | if (wasm.base.zcu_object_sub_path) |path| { | |
| 3770 | const module_obj_path: Path = .{ | |
| 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( | |
| 3314 | binary_bytes.items, | |
| 3315 | header_offset, | |
| 3316 | @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)), | |
| 3317 | ); | |
| 3318 | } | |
| 3781 | const tracy = trace(@src()); | |
| 3782 | defer tracy.end(); | |
| 3319 | 3783 | |
| 3320 | fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { | |
| 3321 | const gpa = wasm.base.comp.gpa; | |
| 3784 | const sub_prog_node = prog_node.start("Wasm Flush", 0); | |
| 3785 | defer sub_prog_node.end(); | |
| 3322 | 3786 | |
| 3323 | // We must emit subsection size, so first write to a temporary list | |
| 3324 | var section_list = std.ArrayList(u8).init(gpa); | |
| 3325 | defer section_list.deinit(); | |
| 3326 | const sub_writer = section_list.writer(); | |
| 3787 | const functions_end_zcu: u32 = @intCast(wasm.functions.entries.len); | |
| 3788 | defer wasm.functions.shrinkRetainingCapacity(functions_end_zcu); | |
| 3327 | 3789 | |
| 3328 | try leb.writeUleb128(sub_writer, @as(u32, @intCast(names.len))); | |
| 3329 | for (names) |name| { | |
| 3330 | log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) }); | |
| 3331 | try leb.writeUleb128(sub_writer, name.index); | |
| 3332 | try leb.writeUleb128(sub_writer, @as(u32, @intCast(name.name.len))); | |
| 3333 | try sub_writer.writeAll(name.name); | |
| 3334 | } | |
| 3790 | const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len); | |
| 3791 | defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu); | |
| 3335 | 3792 | |
| 3336 | // From now, write to the actual writer | |
| 3337 | try leb.writeUleb128(writer, @intFromEnum(section_id)); | |
| 3338 | try leb.writeUleb128(writer, @as(u32, @intCast(section_list.items.len))); | |
| 3339 | try writer.writeAll(section_list.items); | |
| 3340 | } | |
| 3793 | const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len); | |
| 3794 | defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu); | |
| 3341 | 3795 | |
| 3342 | fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void { | |
| 3343 | try writer.writeByte(limits.flags); | |
| 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 | } | |
| 3796 | const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len); | |
| 3797 | defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu); | |
| 3349 | 3798 | |
| 3350 | fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { | |
| 3351 | switch (init_expr) { | |
| 3352 | .i32_const => |val| { | |
| 3353 | try writer.writeByte(std.wasm.opcode(.i32_const)); | |
| 3354 | try leb.writeIleb128(writer, val); | |
| 3355 | }, | |
| 3356 | .i64_const => |val| { | |
| 3357 | try writer.writeByte(std.wasm.opcode(.i64_const)); | |
| 3358 | try leb.writeIleb128(writer, val); | |
| 3359 | }, | |
| 3360 | .f32_const => |val| { | |
| 3361 | try writer.writeByte(std.wasm.opcode(.f32_const)); | |
| 3362 | try writer.writeInt(u32, @bitCast(val), .little); | |
| 3363 | }, | |
| 3364 | .f64_const => |val| { | |
| 3365 | try writer.writeByte(std.wasm.opcode(.f64_const)); | |
| 3366 | try writer.writeInt(u64, @bitCast(val), .little); | |
| 3367 | }, | |
| 3368 | .global_get => |val| { | |
| 3369 | try writer.writeByte(std.wasm.opcode(.global_get)); | |
| 3370 | try leb.writeUleb128(writer, val); | |
| 3371 | }, | |
| 3372 | } | |
| 3373 | try writer.writeByte(std.wasm.opcode(.end)); | |
| 3374 | } | |
| 3799 | wasm.flush_buffer.clear(); | |
| 3800 | try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{}); | |
| 3801 | try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values()); | |
| 3802 | try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values()); | |
| 3803 | try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values()); | |
| 3375 | 3804 | |
| 3376 | fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void { | |
| 3377 | const module_name = wasm.stringSlice(import.module_name); | |
| 3378 | try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len))); | |
| 3379 | try writer.writeAll(module_name); | |
| 3380 | ||
| 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 | } | |
| 3805 | return wasm.flush_buffer.finish(wasm) catch |err| switch (err) { | |
| 3806 | error.OutOfMemory => return error.OutOfMemory, | |
| 3807 | error.LinkFailure => return error.LinkFailure, | |
| 3808 | else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}), | |
| 3809 | }; | |
| 3400 | 3810 | } |
| 3401 | 3811 | |
| 3402 | 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 | 3816 | defer tracy.end(); |
| 3407 | 3817 | |
| 3408 | 3818 | const comp = wasm.base.comp; |
| 3819 | const diags = &comp.link_diags; | |
| 3409 | 3820 | const shared_memory = comp.config.shared_memory; |
| 3410 | 3821 | const export_memory = comp.config.export_memory; |
| 3411 | 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 | 3870 | } |
| 3460 | 3871 | try man.addOptionalFile(module_obj_path); |
| 3461 | 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 | 3874 | man.hash.add(wasm.base.stack_size); |
| 3464 | 3875 | man.hash.add(wasm.base.build_id); |
| 3465 | 3876 | man.hash.add(import_memory); |
| ... | ... | @@ -3608,7 +4019,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3608 | 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 | 4023 | try argv.appendSlice(&.{ "--entry", entry_name }); |
| 3613 | 4024 | } else { |
| 3614 | 4025 | try argv.append("--no-entry"); |
| ... | ... | @@ -3750,14 +4161,12 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3750 | 4161 | switch (term) { |
| 3751 | 4162 | .Exited => |code| { |
| 3752 | 4163 | if (code != 0) { |
| 3753 | const diags = &comp.link_diags; | |
| 3754 | 4164 | diags.lockAndParseLldStderr(linker_command, stderr); |
| 3755 | return error.LLDReportedFailure; | |
| 4165 | return error.LinkFailure; | |
| 3756 | 4166 | } |
| 3757 | 4167 | }, |
| 3758 | 4168 | else => { |
| 3759 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | |
| 3760 | return error.LLDCrashed; | |
| 4169 | return diags.fail("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | |
| 3761 | 4170 | }, |
| 3762 | 4171 | } |
| 3763 | 4172 | |
| ... | ... | @@ -3771,7 +4180,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 3771 | 4180 | if (comp.clang_passthrough_mode) { |
| 3772 | 4181 | std.process.exit(exit_code); |
| 3773 | 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 | 4220 | } |
| 3812 | 4221 | } |
| 3813 | 4222 | |
| 3814 | fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 { | |
| 3815 | // section id + fixed leb contents size + fixed leb vector length | |
| 3816 | const header_size = 1 + 5 + 5; | |
| 3817 | const offset = @as(u32, @intCast(bytes.items.len)); | |
| 3818 | try bytes.appendSlice(&[_]u8{0} ** header_size); | |
| 3819 | return offset; | |
| 4223 | fn defaultEntrySymbolName( | |
| 4224 | preloaded_strings: *const PreloadedStrings, | |
| 4225 | wasi_exec_model: std.builtin.WasiExecModel, | |
| 4226 | ) String { | |
| 4227 | return switch (wasi_exec_model) { | |
| 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 { | |
| 3823 | // unlike regular section, we don't emit the count | |
| 3824 | const header_size = 1 + 5; | |
| 3825 | const offset = @as(u32, @intCast(bytes.items.len)); | |
| 3826 | try bytes.appendSlice(&[_]u8{0} ** header_size); | |
| 3827 | return offset; | |
| 4263 | // TODO implement instead by appending to string_bytes | |
| 4264 | pub fn internStringFmt(wasm: *Wasm, comptime format: []const u8, args: anytype) Allocator.Error!String { | |
| 4265 | var buffer: [32]u8 = undefined; | |
| 4266 | const slice = std.fmt.bufPrint(&buffer, format, args) catch unreachable; | |
| 4267 | return internString(wasm, slice); | |
| 3828 | 4268 | } |
| 3829 | 4269 | |
| 3830 | fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void { | |
| 3831 | var buf: [1 + 5 + 5]u8 = undefined; | |
| 3832 | buf[0] = @intFromEnum(section); | |
| 3833 | leb.writeUnsignedFixed(5, buf[1..6], size); | |
| 3834 | leb.writeUnsignedFixed(5, buf[6..], items); | |
| 3835 | buffer[offset..][0..buf.len].* = buf; | |
| 4270 | pub fn getExistingString(wasm: *const Wasm, bytes: []const u8) ?String { | |
| 4271 | assert(mem.indexOfScalar(u8, bytes, 0) == null); | |
| 4272 | return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{ | |
| 4273 | .bytes = wasm.string_bytes.items, | |
| 4274 | })); | |
| 3836 | 4275 | } |
| 3837 | 4276 | |
| 3838 | fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void { | |
| 3839 | var buf: [1 + 5]u8 = undefined; | |
| 3840 | buf[0] = 0; // 0 = 'custom' section | |
| 3841 | leb.writeUnsignedFixed(5, buf[1..6], size); | |
| 3842 | buffer[offset..][0..buf.len].* = buf; | |
| 4277 | pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) Allocator.Error!ValtypeList { | |
| 4278 | return .fromString(try internString(wasm, @ptrCast(valtype_list))); | |
| 3843 | 4279 | } |
| 3844 | 4280 | |
| 3845 | fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { | |
| 3846 | const offset = try reserveCustomSectionHeader(binary_bytes); | |
| 3847 | const writer = binary_bytes.writer(); | |
| 3848 | // emit "linking" custom section name | |
| 3849 | const section_name = "linking"; | |
| 3850 | try leb.writeUleb128(writer, section_name.len); | |
| 3851 | try writer.writeAll(section_name); | |
| 3852 | ||
| 3853 | // meta data version, which is currently '2' | |
| 3854 | try leb.writeUleb128(writer, @as(u32, 2)); | |
| 3855 | ||
| 3856 | // For each subsection type (found in Subsection) we can emit a section. | |
| 3857 | // Currently, we only support emitting segment info and the symbol table. | |
| 3858 | try wasm.emitSymbolTable(binary_bytes, symbol_table); | |
| 3859 | try wasm.emitSegmentInfo(binary_bytes); | |
| 3860 | ||
| 3861 | const size: u32 = @intCast(binary_bytes.items.len - offset - 6); | |
| 3862 | try writeCustomSectionHeader(binary_bytes.items, offset, size); | |
| 4281 | pub fn getExistingValtypeList(wasm: *const Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList { | |
| 4282 | return .fromString(getExistingString(wasm, @ptrCast(valtype_list)) orelse return null); | |
| 3863 | 4283 | } |
| 3864 | 4284 | |
| 3865 | fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { | |
| 3866 | const writer = binary_bytes.writer(); | |
| 3867 | ||
| 3868 | try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE)); | |
| 3869 | const table_offset = binary_bytes.items.len; | |
| 3870 | ||
| 3871 | var symbol_count: u32 = 0; | |
| 3872 | for (wasm.resolved_symbols.keys()) |sym_loc| { | |
| 3873 | const symbol = wasm.symbolLocSymbol(sym_loc).*; | |
| 3874 | if (symbol.tag == .dead) continue; // Do not emit dead symbols | |
| 3875 | try symbol_table.putNoClobber(sym_loc, symbol_count); | |
| 3876 | symbol_count += 1; | |
| 3877 | log.debug("Emit symbol: {}", .{symbol}); | |
| 3878 | try leb.writeUleb128(writer, @intFromEnum(symbol.tag)); | |
| 3879 | try leb.writeUleb128(writer, symbol.flags); | |
| 3880 | ||
| 3881 | const sym_name = wasm.symbolLocName(sym_loc); | |
| 3882 | switch (symbol.tag) { | |
| 3883 | .data => { | |
| 3884 | try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len))); | |
| 3885 | try writer.writeAll(sym_name); | |
| 3886 | ||
| 3887 | if (symbol.isDefined()) { | |
| 3888 | try leb.writeUleb128(writer, symbol.index); | |
| 3889 | const atom_index = wasm.symbol_atom.get(sym_loc).?; | |
| 3890 | const atom = wasm.getAtom(atom_index); | |
| 3891 | try leb.writeUleb128(writer, @as(u32, atom.offset)); | |
| 3892 | try leb.writeUleb128(writer, @as(u32, atom.size)); | |
| 3893 | } | |
| 3894 | }, | |
| 3895 | .section => { | |
| 3896 | try leb.writeUleb128(writer, symbol.index); | |
| 3897 | }, | |
| 3898 | else => { | |
| 3899 | try leb.writeUleb128(writer, symbol.index); | |
| 3900 | if (symbol.isDefined()) { | |
| 3901 | try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len))); | |
| 3902 | try writer.writeAll(sym_name); | |
| 3903 | } | |
| 3904 | }, | |
| 3905 | } | |
| 3906 | } | |
| 3907 | ||
| 3908 | var buf: [10]u8 = undefined; | |
| 3909 | leb.writeUnsignedFixed(5, buf[0..5], @intCast(binary_bytes.items.len - table_offset + 5)); | |
| 3910 | leb.writeUnsignedFixed(5, buf[5..], symbol_count); | |
| 3911 | try binary_bytes.insertSlice(table_offset, &buf); | |
| 4285 | pub fn addFuncType(wasm: *Wasm, ft: FunctionType) Allocator.Error!FunctionType.Index { | |
| 4286 | const gpa = wasm.base.comp.gpa; | |
| 4287 | const gop = try wasm.func_types.getOrPut(gpa, ft); | |
| 4288 | return @enumFromInt(gop.index); | |
| 3912 | 4289 | } |
| 3913 | 4290 | |
| 3914 | fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { | |
| 3915 | const writer = binary_bytes.writer(); | |
| 3916 | try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO)); | |
| 3917 | const segment_offset = binary_bytes.items.len; | |
| 3918 | ||
| 3919 | try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count()))); | |
| 3920 | for (wasm.segment_info.values()) |segment_info| { | |
| 3921 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ | |
| 3922 | segment_info.name, | |
| 3923 | segment_info.alignment, | |
| 3924 | segment_info.flags, | |
| 3925 | }); | |
| 3926 | try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len))); | |
| 3927 | try writer.writeAll(segment_info.name); | |
| 3928 | try leb.writeUleb128(writer, segment_info.alignment.toLog2Units()); | |
| 3929 | try leb.writeUleb128(writer, segment_info.flags); | |
| 3930 | } | |
| 3931 | ||
| 3932 | var buf: [5]u8 = undefined; | |
| 3933 | leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset))); | |
| 3934 | try binary_bytes.insertSlice(segment_offset, &buf); | |
| 4291 | pub fn getExistingFuncType(wasm: *const Wasm, ft: FunctionType) ?FunctionType.Index { | |
| 4292 | const index = wasm.func_types.getIndex(ft) orelse return null; | |
| 4293 | return @enumFromInt(index); | |
| 3935 | 4294 | } |
| 3936 | 4295 | |
| 3937 | pub fn getUleb128Size(uint_value: anytype) u32 { | |
| 3938 | const T = @TypeOf(uint_value); | |
| 3939 | const U = if (@typeInfo(T).int.bits < 8) u8 else T; | |
| 3940 | var value = @as(U, @intCast(uint_value)); | |
| 3941 | ||
| 3942 | var size: u32 = 0; | |
| 3943 | while (value != 0) : (size += 1) { | |
| 3944 | value >>= 7; | |
| 3945 | } | |
| 3946 | return size; | |
| 4296 | pub fn getExistingFuncType2(wasm: *const Wasm, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype) FunctionType.Index { | |
| 4297 | return getExistingFuncType(wasm, .{ | |
| 4298 | .params = getExistingValtypeList(wasm, params).?, | |
| 4299 | .returns = getExistingValtypeList(wasm, returns).?, | |
| 4300 | }).?; | |
| 3947 | 4301 | } |
| 3948 | 4302 | |
| 3949 | /// For each relocatable section, emits a custom "relocation.<section_name>" section | |
| 3950 | fn emitCodeRelocations( | |
| 4303 | pub fn internFunctionType( | |
| 3951 | 4304 | wasm: *Wasm, |
| 3952 | binary_bytes: *std.ArrayList(u8), | |
| 3953 | section_index: u32, | |
| 3954 | symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), | |
| 3955 | ) !void { | |
| 3956 | const code_index = wasm.code_section_index.unwrap() orelse return; | |
| 3957 | const writer = binary_bytes.writer(); | |
| 3958 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | |
| 3959 | ||
| 3960 | // write custom section information | |
| 3961 | const name = "reloc.CODE"; | |
| 3962 | try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); | |
| 3963 | try writer.writeAll(name); | |
| 3964 | try leb.writeUleb128(writer, section_index); | |
| 3965 | const reloc_start = binary_bytes.items.len; | |
| 3966 | ||
| 3967 | var count: u32 = 0; | |
| 3968 | var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?); | |
| 3969 | // for each atom, we calculate the uleb size and append that | |
| 3970 | var size_offset: u32 = 5; // account for code section size leb128 | |
| 3971 | while (true) { | |
| 3972 | size_offset += getUleb128Size(atom.size); | |
| 3973 | for (atom.relocs.items) |relocation| { | |
| 3974 | count += 1; | |
| 3975 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) }; | |
| 3976 | const symbol_index = symbol_table.get(sym_loc).?; | |
| 3977 | try leb.writeUleb128(writer, @intFromEnum(relocation.relocation_type)); | |
| 3978 | const offset = atom.offset + relocation.offset + size_offset; | |
| 3979 | try leb.writeUleb128(writer, offset); | |
| 3980 | try leb.writeUleb128(writer, symbol_index); | |
| 3981 | if (relocation.relocation_type.addendIsPresent()) { | |
| 3982 | try leb.writeIleb128(writer, relocation.addend); | |
| 3983 | } | |
| 3984 | log.debug("Emit relocation: {}", .{relocation}); | |
| 3985 | } | |
| 3986 | if (atom.prev == .null) break; | |
| 3987 | atom = wasm.getAtomPtr(atom.prev); | |
| 3988 | } | |
| 3989 | if (count == 0) return; | |
| 3990 | var buf: [5]u8 = undefined; | |
| 3991 | leb.writeUnsignedFixed(5, &buf, count); | |
| 3992 | try binary_bytes.insertSlice(reloc_start, &buf); | |
| 3993 | const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6); | |
| 3994 | try writeCustomSectionHeader(binary_bytes.items, header_offset, size); | |
| 4305 | cc: std.builtin.CallingConvention, | |
| 4306 | params: []const InternPool.Index, | |
| 4307 | return_type: Zcu.Type, | |
| 4308 | target: *const std.Target, | |
| 4309 | ) Allocator.Error!FunctionType.Index { | |
| 4310 | try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch); | |
| 4311 | return wasm.addFuncType(.{ | |
| 4312 | .params = try wasm.internValtypeList(wasm.params_scratch.items), | |
| 4313 | .returns = try wasm.internValtypeList(wasm.returns_scratch.items), | |
| 4314 | }); | |
| 3995 | 4315 | } |
| 3996 | 4316 | |
| 3997 | fn emitDataRelocations( | |
| 4317 | pub fn getExistingFunctionType( | |
| 3998 | 4318 | wasm: *Wasm, |
| 3999 | binary_bytes: *std.ArrayList(u8), | |
| 4000 | section_index: u32, | |
| 4001 | symbol_table: std.AutoArrayHashMap(SymbolLoc, u32), | |
| 4002 | ) !void { | |
| 4003 | if (wasm.data_segments.count() == 0) return; | |
| 4004 | const writer = binary_bytes.writer(); | |
| 4005 | const header_offset = try reserveCustomSectionHeader(binary_bytes); | |
| 4006 | ||
| 4007 | // write custom section information | |
| 4008 | const name = "reloc.DATA"; | |
| 4009 | try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); | |
| 4010 | try writer.writeAll(name); | |
| 4011 | try leb.writeUleb128(writer, section_index); | |
| 4012 | const reloc_start = binary_bytes.items.len; | |
| 4013 | ||
| 4014 | var count: u32 = 0; | |
| 4015 | // for each atom, we calculate the uleb size and append that | |
| 4016 | var size_offset: u32 = 5; // account for code section size leb128 | |
| 4017 | for (wasm.data_segments.values()) |segment_index| { | |
| 4018 | var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?); | |
| 4019 | while (true) { | |
| 4020 | size_offset += getUleb128Size(atom.size); | |
| 4021 | for (atom.relocs.items) |relocation| { | |
| 4022 | count += 1; | |
| 4023 | const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) }; | |
| 4024 | const symbol_index = symbol_table.get(sym_loc).?; | |
| 4025 | try leb.writeUleb128(writer, @intFromEnum(relocation.relocation_type)); | |
| 4026 | const offset = atom.offset + relocation.offset + size_offset; | |
| 4027 | try leb.writeUleb128(writer, offset); | |
| 4028 | try leb.writeUleb128(writer, symbol_index); | |
| 4029 | if (relocation.relocation_type.addendIsPresent()) { | |
| 4030 | try leb.writeIleb128(writer, relocation.addend); | |
| 4031 | } | |
| 4032 | log.debug("Emit relocation: {}", .{relocation}); | |
| 4033 | } | |
| 4034 | if (atom.prev == .null) break; | |
| 4035 | atom = wasm.getAtomPtr(atom.prev); | |
| 4036 | } | |
| 4037 | } | |
| 4038 | if (count == 0) return; | |
| 4039 | ||
| 4040 | var buf: [5]u8 = undefined; | |
| 4041 | leb.writeUnsignedFixed(5, &buf, count); | |
| 4042 | try binary_bytes.insertSlice(reloc_start, &buf); | |
| 4043 | const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)); | |
| 4044 | try writeCustomSectionHeader(binary_bytes.items, header_offset, size); | |
| 4319 | cc: std.builtin.CallingConvention, | |
| 4320 | params: []const InternPool.Index, | |
| 4321 | return_type: Zcu.Type, | |
| 4322 | target: *const std.Target, | |
| 4323 | ) ?FunctionType.Index { | |
| 4324 | convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) { | |
| 4325 | error.OutOfMemory => return null, | |
| 4326 | }; | |
| 4327 | return wasm.getExistingFuncType(.{ | |
| 4328 | .params = wasm.getExistingValtypeList(wasm.params_scratch.items) orelse return null, | |
| 4329 | .returns = wasm.getExistingValtypeList(wasm.returns_scratch.items) orelse return null, | |
| 4330 | }); | |
| 4045 | 4331 | } |
| 4046 | 4332 | |
| 4047 | fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { | |
| 4048 | const comp = wasm.base.comp; | |
| 4049 | const import_memory = comp.config.import_memory; | |
| 4050 | ||
| 4051 | var it = wasm.data_segments.iterator(); | |
| 4052 | while (it.next()) |entry| { | |
| 4053 | const segment = wasm.segmentPtr(entry.value_ptr.*); | |
| 4054 | if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) { | |
| 4055 | return true; | |
| 4056 | } | |
| 4057 | } | |
| 4058 | return false; | |
| 4333 | pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr { | |
| 4334 | const gpa = wasm.base.comp.gpa; | |
| 4335 | // We can't use string table deduplication here since these expressions can | |
| 4336 | // have null bytes in them however it may be interesting to explore since | |
| 4337 | // it is likely for globals to share initialization values. Then again | |
| 4338 | // there may not be very many globals in total. | |
| 4339 | try wasm.string_bytes.appendSlice(gpa, bytes); | |
| 4340 | return @enumFromInt(wasm.string_bytes.items.len - bytes.len); | |
| 4059 | 4341 | } |
| 4060 | 4342 | |
| 4061 | /// Searches for a matching function signature. When no matching signature is found, | |
| 4062 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. | |
| 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. | |
| 4343 | pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataPayload { | |
| 4069 | 4344 | const gpa = wasm.base.comp.gpa; |
| 4070 | const index: u32 = @intCast(wasm.func_types.items.len); | |
| 4071 | const params = try gpa.dupe(std.wasm.Valtype, func_type.params); | |
| 4072 | errdefer gpa.free(params); | |
| 4073 | const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns); | |
| 4074 | errdefer gpa.free(returns); | |
| 4075 | try wasm.func_types.append(gpa, .{ | |
| 4076 | .params = params, | |
| 4077 | .returns = returns, | |
| 4078 | }); | |
| 4079 | return index; | |
| 4345 | try wasm.string_bytes.appendSlice(gpa, bytes); | |
| 4346 | return .{ | |
| 4347 | .off = @enumFromInt(wasm.string_bytes.items.len - bytes.len), | |
| 4348 | .len = @intCast(bytes.len), | |
| 4349 | }; | |
| 4080 | 4350 | } |
| 4081 | 4351 | |
| 4082 | /// For the given `nav`, stores the corresponding type representing the function signature. | |
| 4083 | /// Asserts declaration has an associated `Atom`. | |
| 4084 | /// Returns the index into the list of types. | |
| 4085 | pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 { | |
| 4086 | return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type); | |
| 4352 | pub fn uavSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex { | |
| 4353 | const comp = wasm.base.comp; | |
| 4354 | assert(comp.config.output_mode == .Obj); | |
| 4355 | const gpa = comp.gpa; | |
| 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. | |
| 4090 | /// | |
| 4091 | /// When the symbol does not yet exist, it will create a new one instead. | |
| 4092 | pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 { | |
| 4093 | const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt); | |
| 4094 | return @intFromEnum(sym_index); | |
| 4362 | pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Error!SymbolTableIndex { | |
| 4363 | const comp = wasm.base.comp; | |
| 4364 | assert(comp.config.output_mode == .Obj); | |
| 4365 | const zcu = comp.zcu.?; | |
| 4366 | const ip = &zcu.intern_pool; | |
| 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`. | |
| 4098 | /// When the index was not found, a new `Atom` will be created, and its index will be returned. | |
| 4099 | /// The newly created Atom is empty with default fields as specified by `Atom.empty`. | |
| 4100 | pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index { | |
| 4101 | return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav); | |
| 4375 | pub fn errorNameTableSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex { | |
| 4376 | const comp = wasm.base.comp; | |
| 4377 | assert(comp.config.output_mode == .Obj); | |
| 4378 | const gpa = comp.gpa; | |
| 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, | |
| 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; | |
| 4384 | pub fn stackPointerSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex { | |
| 4111 | 4385 | const comp = wasm.base.comp; |
| 4112 | ||
| 4113 | for (wasm.resolved_symbols.keys()) |sym_loc| { | |
| 4114 | const sym = wasm.symbolLocSymbol(sym_loc); | |
| 4115 | if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { | |
| 4116 | try wasm.mark(sym_loc); | |
| 4117 | continue; | |
| 4118 | } | |
| 4119 | ||
| 4120 | // Debug sections may require to be parsed and marked when it contains | |
| 4121 | // relocations to alive symbols. | |
| 4122 | if (sym.tag == .section and comp.config.debug_format != .strip) { | |
| 4123 | const object_id = sym_loc.file.unwrap() orelse continue; // Incremental debug info is done independently | |
| 4124 | _ = try wasm.parseSymbolIntoAtom(object_id, sym_loc.index); | |
| 4125 | sym.mark(); | |
| 4126 | } | |
| 4127 | } | |
| 4386 | assert(comp.config.output_mode == .Obj); | |
| 4387 | const gpa = comp.gpa; | |
| 4388 | const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__stack_pointer); | |
| 4389 | gop.value_ptr.* = {}; | |
| 4390 | return @enumFromInt(gop.index); | |
| 4128 | 4391 | } |
| 4129 | 4392 | |
| 4130 | /// Marks a symbol as 'alive' recursively so itself and any references it contains to | |
| 4131 | /// other symbols will not be omit from the binary. | |
| 4132 | fn mark(wasm: *Wasm, loc: SymbolLoc) !void { | |
| 4133 | const symbol = wasm.symbolLocSymbol(loc); | |
| 4134 | if (symbol.isAlive()) { | |
| 4135 | // Symbol is already marked alive, including its references. | |
| 4136 | // This means we can skip it so we don't end up marking the same symbols | |
| 4137 | // multiple times. | |
| 4138 | return; | |
| 4139 | } | |
| 4140 | symbol.mark(); | |
| 4141 | gc_log.debug("Marked symbol '{s}'", .{wasm.symbolLocName(loc)}); | |
| 4142 | if (symbol.isUndefined()) { | |
| 4143 | // undefined symbols do not have an associated `Atom` and therefore also | |
| 4144 | // do not contain relocations. | |
| 4145 | return; | |
| 4146 | } | |
| 4147 | ||
| 4148 | const atom_index = if (loc.file.unwrap()) |object_id| | |
| 4149 | try wasm.parseSymbolIntoAtom(object_id, loc.index) | |
| 4150 | else | |
| 4151 | wasm.symbol_atom.get(loc) orelse return; | |
| 4152 | ||
| 4153 | const atom = wasm.getAtom(atom_index); | |
| 4154 | for (atom.relocs.items) |reloc| { | |
| 4155 | const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file }; | |
| 4156 | try wasm.mark(wasm.symbolLocFinalLoc(target_loc)); | |
| 4157 | } | |
| 4393 | pub fn tagNameSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex { | |
| 4394 | const comp = wasm.base.comp; | |
| 4395 | assert(comp.config.output_mode == .Obj); | |
| 4396 | const gpa = comp.gpa; | |
| 4397 | const name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(ip_index)}); | |
| 4398 | const gop = try wasm.symbol_table.getOrPut(gpa, name); | |
| 4399 | gop.value_ptr.* = {}; | |
| 4400 | return @enumFromInt(gop.index); | |
| 4158 | 4401 | } |
| 4159 | 4402 | |
| 4160 | fn defaultEntrySymbolName( | |
| 4161 | preloaded_strings: *const PreloadedStrings, | |
| 4162 | wasi_exec_model: std.builtin.WasiExecModel, | |
| 4163 | ) String { | |
| 4164 | return switch (wasi_exec_model) { | |
| 4165 | .reactor => preloaded_strings._initialize, | |
| 4166 | .command => preloaded_strings._start, | |
| 4167 | }; | |
| 4403 | pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableIndex { | |
| 4404 | const comp = wasm.base.comp; | |
| 4405 | assert(comp.config.output_mode == .Obj); | |
| 4406 | const gpa = comp.gpa; | |
| 4407 | const gop = try wasm.symbol_table.getOrPut(gpa, name); | |
| 4408 | gop.value_ptr.* = {}; | |
| 4409 | return @enumFromInt(gop.index); | |
| 4168 | 4410 | } |
| 4169 | 4411 | |
| 4170 | pub const Atom = struct { | |
| 4171 | /// Represents the index of the file this atom was generated from. | |
| 4172 | /// This is `none` when the atom was generated by a synthetic linker symbol. | |
| 4173 | file: OptionalObjectId, | |
| 4174 | /// symbol index of the symbol representing this atom | |
| 4175 | sym_index: Symbol.Index, | |
| 4176 | /// Size of the atom, used to calculate section sizes in the final binary | |
| 4177 | size: u32 = 0, | |
| 4178 | /// List of relocations belonging to this atom | |
| 4179 | relocs: std.ArrayListUnmanaged(Relocation) = .empty, | |
| 4180 | /// Contains the binary data of an atom, which can be non-relocated | |
| 4181 | code: std.ArrayListUnmanaged(u8) = .empty, | |
| 4182 | /// For code this is 1, for data this is set to the highest value of all segments | |
| 4183 | alignment: Wasm.Alignment = .@"1", | |
| 4184 | /// Offset into the section where the atom lives, this already accounts | |
| 4185 | /// for alignment. | |
| 4186 | offset: u32 = 0, | |
| 4187 | /// The original offset within the object file. This value is subtracted from | |
| 4188 | /// relocation offsets to determine where in the `data` to rewrite the value | |
| 4189 | original_offset: u32 = 0, | |
| 4190 | /// Previous atom in relation to this atom. | |
| 4191 | /// is null when this atom is the first in its order | |
| 4192 | prev: Atom.Index = .null, | |
| 4193 | /// Contains atoms local to a decl, all managed by this `Atom`. | |
| 4194 | /// When the parent atom is being freed, it will also do so for all local atoms. | |
| 4195 | locals: std.ArrayListUnmanaged(Atom.Index) = .empty, | |
| 4196 | ||
| 4197 | /// Represents the index of an Atom where `null` is considered | |
| 4198 | /// an invalid atom. | |
| 4199 | pub const Index = enum(u32) { | |
| 4200 | null = std.math.maxInt(u32), | |
| 4201 | _, | |
| 4202 | }; | |
| 4203 | ||
| 4204 | /// Frees all resources owned by this `Atom`. | |
| 4205 | pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void { | |
| 4206 | atom.relocs.deinit(gpa); | |
| 4207 | atom.code.deinit(gpa); | |
| 4208 | atom.locals.deinit(gpa); | |
| 4209 | atom.* = undefined; | |
| 4210 | } | |
| 4211 | ||
| 4212 | /// Sets the length of relocations and code to '0', | |
| 4213 | /// effectively resetting them and allowing them to be re-populated. | |
| 4214 | pub fn clear(atom: *Atom) void { | |
| 4215 | atom.relocs.clearRetainingCapacity(); | |
| 4216 | atom.code.clearRetainingCapacity(); | |
| 4217 | } | |
| 4218 | ||
| 4219 | pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 4220 | _ = fmt; | |
| 4221 | _ = options; | |
| 4222 | try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{ | |
| 4223 | @intFromEnum(atom.sym_index), | |
| 4224 | atom.alignment, | |
| 4225 | atom.size, | |
| 4226 | atom.offset, | |
| 4227 | }); | |
| 4228 | } | |
| 4412 | pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex { | |
| 4413 | const comp = wasm.base.comp; | |
| 4414 | const zcu = comp.zcu.?; | |
| 4415 | const ip = &zcu.intern_pool; | |
| 4416 | const gpa = comp.gpa; | |
| 4417 | assert(comp.config.output_mode == .Obj); | |
| 4229 | 4418 | |
| 4230 | /// Returns the location of the symbol that represents this `Atom` | |
| 4231 | pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { | |
| 4232 | return .{ | |
| 4233 | .file = atom.file, | |
| 4234 | .index = atom.sym_index, | |
| 4235 | }; | |
| 4419 | if (orig_ptr_ty != .none) { | |
| 4420 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); | |
| 4421 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; | |
| 4422 | if (explicit_alignment.compare(.gt, abi_alignment)) { | |
| 4423 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); | |
| 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 | |
| 4239 | /// at the calculated offset. | |
| 4240 | pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void { | |
| 4241 | if (atom.relocs.items.len == 0) return; | |
| 4242 | const symbol_name = wasm.symbolLocName(atom.symbolLoc()); | |
| 4243 | log.debug("Resolving relocs in atom '{s}' count({d})", .{ | |
| 4244 | symbol_name, | |
| 4245 | atom.relocs.items.len, | |
| 4246 | }); | |
| 4247 | ||
| 4248 | for (atom.relocs.items) |reloc| { | |
| 4249 | const value = atom.relocationValue(reloc, wasm); | |
| 4250 | log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{ | |
| 4251 | wasm.symbolLocName(.{ | |
| 4252 | .file = atom.file, | |
| 4253 | .index = @enumFromInt(reloc.index), | |
| 4254 | }), | |
| 4255 | symbol_name, | |
| 4256 | reloc.offset, | |
| 4257 | value, | |
| 4258 | }); | |
| 4428 | const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index); | |
| 4429 | if (!gop.found_existing) gop.value_ptr.* = .{ | |
| 4430 | // Lowering the value is delayed to avoid recursion. | |
| 4431 | .code = undefined, | |
| 4432 | .relocs = undefined, | |
| 4433 | }; | |
| 4434 | return @enumFromInt(gop.index); | |
| 4435 | } | |
| 4259 | 4436 | |
| 4260 | switch (reloc.relocation_type) { | |
| 4261 | .R_WASM_TABLE_INDEX_I32, | |
| 4262 | .R_WASM_FUNCTION_OFFSET_I32, | |
| 4263 | .R_WASM_GLOBAL_INDEX_I32, | |
| 4264 | .R_WASM_MEMORY_ADDR_I32, | |
| 4265 | .R_WASM_SECTION_OFFSET_I32, | |
| 4266 | => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little), | |
| 4267 | .R_WASM_TABLE_INDEX_I64, | |
| 4268 | .R_WASM_MEMORY_ADDR_I64, | |
| 4269 | => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little), | |
| 4270 | .R_WASM_GLOBAL_INDEX_LEB, | |
| 4271 | .R_WASM_EVENT_INDEX_LEB, | |
| 4272 | .R_WASM_FUNCTION_INDEX_LEB, | |
| 4273 | .R_WASM_MEMORY_ADDR_LEB, | |
| 4274 | .R_WASM_MEMORY_ADDR_SLEB, | |
| 4275 | .R_WASM_TABLE_INDEX_SLEB, | |
| 4276 | .R_WASM_TABLE_NUMBER_LEB, | |
| 4277 | .R_WASM_TYPE_INDEX_LEB, | |
| 4278 | .R_WASM_MEMORY_ADDR_TLS_SLEB, | |
| 4279 | => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))), | |
| 4280 | .R_WASM_MEMORY_ADDR_LEB64, | |
| 4281 | .R_WASM_MEMORY_ADDR_SLEB64, | |
| 4282 | .R_WASM_TABLE_INDEX_SLEB64, | |
| 4283 | .R_WASM_MEMORY_ADDR_TLS_SLEB64, | |
| 4284 | => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value), | |
| 4285 | } | |
| 4286 | } | |
| 4287 | } | |
| 4437 | pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex { | |
| 4438 | const comp = wasm.base.comp; | |
| 4439 | const zcu = comp.zcu.?; | |
| 4440 | const ip = &zcu.intern_pool; | |
| 4441 | const gpa = comp.gpa; | |
| 4442 | assert(comp.config.output_mode != .Obj); | |
| 4288 | 4443 | |
| 4289 | /// From a given `relocation` will return the new value to be written. | |
| 4290 | /// All values will be represented as a `u64` as all values can fit within it. | |
| 4291 | /// The final value must be casted to the correct size. | |
| 4292 | fn relocationValue(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 { | |
| 4293 | const target_loc = wasm.symbolLocFinalLoc(.{ | |
| 4294 | .file = atom.file, | |
| 4295 | .index = @enumFromInt(relocation.index), | |
| 4296 | }); | |
| 4297 | const symbol = wasm.symbolLocSymbol(target_loc); | |
| 4298 | if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and | |
| 4299 | symbol.tag != .section and | |
| 4300 | symbol.isDead()) | |
| 4301 | { | |
| 4302 | const val = atom.tombstone(wasm) orelse relocation.addend; | |
| 4303 | return @bitCast(val); | |
| 4304 | } | |
| 4305 | switch (relocation.relocation_type) { | |
| 4306 | .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, | |
| 4307 | .R_WASM_TABLE_NUMBER_LEB => return symbol.index, | |
| 4308 | .R_WASM_TABLE_INDEX_I32, | |
| 4309 | .R_WASM_TABLE_INDEX_I64, | |
| 4310 | .R_WASM_TABLE_INDEX_SLEB, | |
| 4311 | .R_WASM_TABLE_INDEX_SLEB64, | |
| 4312 | => return wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0, | |
| 4313 | .R_WASM_TYPE_INDEX_LEB => { | |
| 4314 | const object_id = atom.file.unwrap() orelse return relocation.index; | |
| 4315 | const original_type = objectFuncTypes(wasm, object_id)[relocation.index]; | |
| 4316 | return wasm.getTypeIndex(original_type).?; | |
| 4317 | }, | |
| 4318 | .R_WASM_GLOBAL_INDEX_I32, | |
| 4319 | .R_WASM_GLOBAL_INDEX_LEB, | |
| 4320 | => return symbol.index, | |
| 4321 | .R_WASM_MEMORY_ADDR_I32, | |
| 4322 | .R_WASM_MEMORY_ADDR_I64, | |
| 4323 | .R_WASM_MEMORY_ADDR_LEB, | |
| 4324 | .R_WASM_MEMORY_ADDR_LEB64, | |
| 4325 | .R_WASM_MEMORY_ADDR_SLEB, | |
| 4326 | .R_WASM_MEMORY_ADDR_SLEB64, | |
| 4327 | => { | |
| 4328 | std.debug.assert(symbol.tag == .data); | |
| 4329 | if (symbol.isUndefined()) { | |
| 4330 | return 0; | |
| 4331 | } | |
| 4332 | const va: i33 = @intCast(symbol.virtual_address); | |
| 4333 | return @intCast(va + relocation.addend); | |
| 4334 | }, | |
| 4335 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, | |
| 4336 | .R_WASM_SECTION_OFFSET_I32 => { | |
| 4337 | const target_atom_index = wasm.symbol_atom.get(target_loc).?; | |
| 4338 | const target_atom = wasm.getAtom(target_atom_index); | |
| 4339 | const rel_value: i33 = @intCast(target_atom.offset); | |
| 4340 | return @intCast(rel_value + relocation.addend); | |
| 4341 | }, | |
| 4342 | .R_WASM_FUNCTION_OFFSET_I32 => { | |
| 4343 | if (symbol.isUndefined()) { | |
| 4344 | const val = atom.tombstone(wasm) orelse relocation.addend; | |
| 4345 | return @bitCast(val); | |
| 4346 | } | |
| 4347 | const target_atom_index = wasm.symbol_atom.get(target_loc).?; | |
| 4348 | const target_atom = wasm.getAtom(target_atom_index); | |
| 4349 | const rel_value: i33 = @intCast(target_atom.offset); | |
| 4350 | return @intCast(rel_value + relocation.addend); | |
| 4351 | }, | |
| 4352 | .R_WASM_MEMORY_ADDR_TLS_SLEB, | |
| 4353 | .R_WASM_MEMORY_ADDR_TLS_SLEB64, | |
| 4354 | => { | |
| 4355 | const va: i33 = @intCast(symbol.virtual_address); | |
| 4356 | return @intCast(va + relocation.addend); | |
| 4357 | }, | |
| 4444 | if (orig_ptr_ty != .none) { | |
| 4445 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); | |
| 4446 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; | |
| 4447 | if (explicit_alignment.compare(.gt, abi_alignment)) { | |
| 4448 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); | |
| 4449 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; | |
| 4358 | 4450 | } |
| 4359 | 4451 | } |
| 4360 | 4452 | |
| 4361 | // For a given `Atom` returns whether it has a tombstone value or not. | |
| 4362 | /// This defines whether we want a specific value when a section is dead. | |
| 4363 | fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 { | |
| 4364 | const atom_name = wasm.symbolLocSymbol(atom.symbolLoc()).name; | |
| 4365 | if (atom_name == wasm.custom_sections.@".debug_ranges".name or | |
| 4366 | atom_name == wasm.custom_sections.@".debug_loc".name) | |
| 4367 | { | |
| 4368 | return -2; | |
| 4369 | } else if (std.mem.startsWith(u8, wasm.stringSlice(atom_name), ".debug_")) { | |
| 4370 | return -1; | |
| 4371 | } else { | |
| 4372 | return null; | |
| 4373 | } | |
| 4453 | const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index); | |
| 4454 | if (gop.found_existing) { | |
| 4455 | gop.value_ptr.count += 1; | |
| 4456 | } else { | |
| 4457 | gop.value_ptr.* = .{ | |
| 4458 | // Lowering the value is delayed to avoid recursion. | |
| 4459 | .code = undefined, | |
| 4460 | .count = 1, | |
| 4461 | }; | |
| 4374 | 4462 | } |
| 4375 | }; | |
| 4463 | return @enumFromInt(gop.index); | |
| 4464 | } | |
| 4376 | 4465 | |
| 4377 | pub const Relocation = struct { | |
| 4378 | /// Represents the type of the `Relocation` | |
| 4379 | relocation_type: RelocationType, | |
| 4380 | /// Offset of the value to rewrite relative to the relevant section's contents. | |
| 4381 | /// When `offset` is zero, its position is immediately after the id and size of the section. | |
| 4382 | offset: u32, | |
| 4383 | /// The index of the symbol used. | |
| 4384 | /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type. | |
| 4385 | index: u32, | |
| 4386 | /// Addend to add to the address. | |
| 4387 | /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`. | |
| 4388 | addend: i32 = 0, | |
| 4389 | ||
| 4390 | /// All possible relocation types currently existing. | |
| 4391 | /// This enum is exhaustive as the spec is WIP and new types | |
| 4392 | /// can be added which means that a generated binary will be invalid, | |
| 4393 | /// so instead we will show an error in such cases. | |
| 4394 | pub const RelocationType = enum(u8) { | |
| 4395 | R_WASM_FUNCTION_INDEX_LEB = 0, | |
| 4396 | R_WASM_TABLE_INDEX_SLEB = 1, | |
| 4397 | R_WASM_TABLE_INDEX_I32 = 2, | |
| 4398 | R_WASM_MEMORY_ADDR_LEB = 3, | |
| 4399 | R_WASM_MEMORY_ADDR_SLEB = 4, | |
| 4400 | R_WASM_MEMORY_ADDR_I32 = 5, | |
| 4401 | R_WASM_TYPE_INDEX_LEB = 6, | |
| 4402 | R_WASM_GLOBAL_INDEX_LEB = 7, | |
| 4403 | R_WASM_FUNCTION_OFFSET_I32 = 8, | |
| 4404 | R_WASM_SECTION_OFFSET_I32 = 9, | |
| 4405 | R_WASM_EVENT_INDEX_LEB = 10, | |
| 4406 | R_WASM_GLOBAL_INDEX_I32 = 13, | |
| 4407 | R_WASM_MEMORY_ADDR_LEB64 = 14, | |
| 4408 | R_WASM_MEMORY_ADDR_SLEB64 = 15, | |
| 4409 | R_WASM_MEMORY_ADDR_I64 = 16, | |
| 4410 | R_WASM_TABLE_INDEX_SLEB64 = 18, | |
| 4411 | R_WASM_TABLE_INDEX_I64 = 19, | |
| 4412 | R_WASM_TABLE_NUMBER_LEB = 20, | |
| 4413 | R_WASM_MEMORY_ADDR_TLS_SLEB = 21, | |
| 4414 | R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25, | |
| 4415 | ||
| 4416 | /// Returns true for relocation types where the `addend` field is present. | |
| 4417 | pub fn addendIsPresent(self: RelocationType) bool { | |
| 4418 | return switch (self) { | |
| 4419 | .R_WASM_MEMORY_ADDR_LEB, | |
| 4420 | .R_WASM_MEMORY_ADDR_SLEB, | |
| 4421 | .R_WASM_MEMORY_ADDR_I32, | |
| 4422 | .R_WASM_MEMORY_ADDR_LEB64, | |
| 4423 | .R_WASM_MEMORY_ADDR_SLEB64, | |
| 4424 | .R_WASM_MEMORY_ADDR_I64, | |
| 4425 | .R_WASM_MEMORY_ADDR_TLS_SLEB, | |
| 4426 | .R_WASM_MEMORY_ADDR_TLS_SLEB64, | |
| 4427 | .R_WASM_FUNCTION_OFFSET_I32, | |
| 4428 | .R_WASM_SECTION_OFFSET_I32, | |
| 4429 | => true, | |
| 4430 | else => false, | |
| 4431 | }; | |
| 4432 | } | |
| 4466 | pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { | |
| 4467 | const comp = wasm.base.comp; | |
| 4468 | const gpa = comp.gpa; | |
| 4469 | assert(comp.config.output_mode != .Obj); | |
| 4470 | const gop = try wasm.navs_obj.getOrPut(gpa, nav_index); | |
| 4471 | if (!gop.found_existing) gop.value_ptr.* = .{ | |
| 4472 | // Lowering the value is delayed to avoid recursion. | |
| 4473 | .code = undefined, | |
| 4474 | .relocs = undefined, | |
| 4433 | 4475 | }; |
| 4476 | return @enumFromInt(gop.index); | |
| 4477 | } | |
| 4434 | 4478 | |
| 4435 | /// Verifies the relocation type of a given `Relocation` and returns | |
| 4436 | /// true when the relocation references a function call or address to a function. | |
| 4437 | pub fn isFunction(self: Relocation) bool { | |
| 4438 | return switch (self.relocation_type) { | |
| 4439 | .R_WASM_FUNCTION_INDEX_LEB, | |
| 4440 | .R_WASM_TABLE_INDEX_SLEB, | |
| 4441 | => true, | |
| 4442 | else => false, | |
| 4479 | pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { | |
| 4480 | const comp = wasm.base.comp; | |
| 4481 | const gpa = comp.gpa; | |
| 4482 | assert(comp.config.output_mode != .Obj); | |
| 4483 | const gop = try wasm.navs_exe.getOrPut(gpa, nav_index); | |
| 4484 | if (gop.found_existing) { | |
| 4485 | gop.value_ptr.count += 1; | |
| 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 { | |
| 4447 | _ = fmt; | |
| 4448 | _ = options; | |
| 4449 | try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{ | |
| 4450 | @tagName(self.relocation_type), | |
| 4451 | self.offset, | |
| 4452 | self.index, | |
| 4453 | }); | |
| 4454 | } | |
| 4455 | }; | |
| 4456 | ||
| 4457 | /// Unlike the `Import` object defined by the wasm spec, and existing | |
| 4458 | /// in the std.wasm namespace, this construct saves the 'module name' and 'name' | |
| 4459 | /// of the import using offsets into a string table, rather than the slices itself. | |
| 4460 | /// This saves us (potentially) 24 bytes per import on 64bit machines. | |
| 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 | }; | |
| 4496 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. | |
| 4497 | pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 { | |
| 4498 | assert(wasm.flush_buffer.memory_layout_finished); | |
| 4499 | const comp = wasm.base.comp; | |
| 4500 | assert(comp.config.output_mode != .Obj); | |
| 4501 | const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); | |
| 4502 | return wasm.flush_buffer.data_segments.get(ds_id).?; | |
| 4503 | } | |
| 4622 | 4504 | |
| 4623 | pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{ | |
| 4624 | .{ "atomics", .atomics }, | |
| 4625 | .{ "bulk-memory", .bulk_memory }, | |
| 4626 | .{ "exception-handling", .exception_handling }, | |
| 4627 | .{ "extended-const", .extended_const }, | |
| 4628 | .{ "half-precision", .half_precision }, | |
| 4629 | .{ "multimemory", .multimemory }, | |
| 4630 | .{ "multivalue", .multivalue }, | |
| 4631 | .{ "mutable-globals", .mutable_globals }, | |
| 4632 | .{ "nontrapping-fptoint", .nontrapping_fptoint }, | |
| 4633 | .{ "reference-types", .reference_types }, | |
| 4634 | .{ "relaxed-simd", .relaxed_simd }, | |
| 4635 | .{ "sign-ext", .sign_ext }, | |
| 4636 | .{ "simd128", .simd128 }, | |
| 4637 | .{ "tail-call", .tail_call }, | |
| 4638 | .{ "shared-mem", .shared_mem }, | |
| 4639 | }); | |
| 4640 | ||
| 4641 | /// Parses an object file into atoms, for code and data sections | |
| 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); | |
| 4505 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. | |
| 4506 | pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 { | |
| 4507 | assert(wasm.flush_buffer.memory_layout_finished); | |
| 4645 | 4508 | const comp = wasm.base.comp; |
| 4646 | const gpa = comp.gpa; | |
| 4647 | const symbol = &object.symtable[@intFromEnum(symbol_index)]; | |
| 4648 | const relocatable_data: Object.RelocatableData = switch (symbol.tag) { | |
| 4649 | .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count], | |
| 4650 | .data => object.relocatable_data.get(.data).?[symbol.index], | |
| 4651 | .section => blk: { | |
| 4652 | const data = object.relocatable_data.get(.custom).?; | |
| 4653 | for (data) |dat| { | |
| 4654 | if (dat.section_index == symbol.index) { | |
| 4655 | break :blk dat; | |
| 4509 | assert(comp.config.output_mode != .Obj); | |
| 4510 | if (wasm.navs_exe.getIndex(nav_index)) |i| { | |
| 4511 | const navs_exe_index: NavsExeIndex = @enumFromInt(i); | |
| 4512 | log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index }); | |
| 4513 | const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index }); | |
| 4514 | return wasm.flush_buffer.data_segments.get(ds_id).?; | |
| 4515 | } | |
| 4516 | const zcu = comp.zcu.?; | |
| 4517 | const ip = &zcu.intern_pool; | |
| 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 { | |
| 4713 | var min: usize = 0; | |
| 4714 | var max: usize = relocs.len; | |
| 4715 | while (min < max) { | |
| 4716 | const index = (min + max) / 2; | |
| 4717 | const curr = relocs[index]; | |
| 4718 | if (curr.offset < address) { | |
| 4719 | min = index + 1; | |
| 4554 | fn convertZcuFnType( | |
| 4555 | comp: *Compilation, | |
| 4556 | cc: std.builtin.CallingConvention, | |
| 4557 | params: []const InternPool.Index, | |
| 4558 | return_type: Zcu.Type, | |
| 4559 | target: *const std.Target, | |
| 4560 | params_buffer: *std.ArrayListUnmanaged(std.wasm.Valtype), | |
| 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 | 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 { | |
| 4728 | for (relocs, 0..relocs.len) |reloc, index| { | |
| 4729 | if (reloc.offset > address) { | |
| 4730 | return index; | |
| 4584 | // param types | |
| 4585 | for (params) |param_type_ip| { | |
| 4586 | const param_type = Zcu.Type.fromInterned(param_type_ip); | |
| 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 { | |
| 4737 | const gpa = wasm.base.comp.gpa; | |
| 4738 | const gop = try wasm.string_table.getOrPutContextAdapted( | |
| 4739 | gpa, | |
| 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); | |
| 4610 | pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool { | |
| 4611 | const s = optional_name.slice(wasm) orelse return false; | |
| 4612 | return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss."); | |
| 4613 | } | |
| 4748 | 4614 | |
| 4749 | wasm.string_bytes.appendSliceAssumeCapacity(bytes); | |
| 4750 | wasm.string_bytes.appendAssumeCapacity(0); | |
| 4615 | /// After this function is called, there may be additional entries in | |
| 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 { | |
| 4758 | return wasm.string_table.getKeyAdapted(bytes, @as(String.TableIndexAdapter, .{ | |
| 4759 | .bytes = wasm.string_bytes.items, | |
| 4760 | })); | |
| 4668 | fn pointerAlignment(wasm: *const Wasm) Alignment { | |
| 4669 | const target = &wasm.base.comp.root_mod.resolved_target.result; | |
| 4670 | return switch (target.cpu.arch) { | |
| 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 { | |
| 4764 | const slice = wasm.string_bytes.items[@intFromEnum(index)..]; | |
| 4765 | return slice[0..mem.indexOfScalar(u8, slice, 0).? :0]; | |
| 4677 | fn pointerSize(wasm: *const Wasm) u32 { | |
| 4678 | const target = &wasm.base.comp.root_mod.resolved_target.result; | |
| 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 { | |
| 4769 | return stringSlice(wasm, index.unwrap() orelse return null); | |
| 4686 | fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex { | |
| 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 { | |
| 4773 | assert(index == 0 or wasm.string_bytes.items[index - 1] == 0); | |
| 4774 | return @enumFromInt(index); | |
| 4692 | fn resolveFunctionSynthetic( | |
| 4693 | wasm: *Wasm, | |
| 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 { | |
| 4778 | return &wasm.segments.items[@intFromEnum(index)]; | |
| 4715 | pub fn addFunction( | |
| 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 | 142 | |
| 143 | 143 | /// From a given file offset, starts reading for a file header. |
| 144 | 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 { | |
| 146 | const header = mem.bytesAsValue(Header, file_contents[0..@sizeOf(Header)]); | |
| 145 | pub fn parseObject( | |
| 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 | 157 | if (!mem.eql(u8, &header.fmag, ARFMAG)) return error.BadHeaderDelimiter; |
| 148 | 158 | |
| 149 | 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 | 167 | }; |
| 158 | 168 | |
| 159 | 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 | 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 | //! 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 | 1 | const Object = @This(); |
| 5 | 2 | |
| 6 | 3 | const Wasm = @import("../Wasm.zig"); |
| 7 | const Atom = Wasm.Atom; | |
| 8 | 4 | const Alignment = Wasm.Alignment; |
| 9 | const Symbol = @import("Symbol.zig"); | |
| 10 | 5 | |
| 11 | 6 | const std = @import("std"); |
| 12 | 7 | const Allocator = std.mem.Allocator; |
| 13 | const leb = std.leb; | |
| 14 | const meta = std.meta; | |
| 15 | 8 | const Path = std.Build.Cache.Path; |
| 16 | ||
| 17 | 9 | const log = std.log.scoped(.object); |
| 10 | const assert = std.debug.assert; | |
| 18 | 11 | |
| 19 | 12 | /// Wasm spec version used for this `Object` |
| 20 | version: u32 = 0, | |
| 13 | version: u32, | |
| 21 | 14 | /// For error reporting purposes only. |
| 22 | 15 | /// Name (read path) of the object or archive file. |
| 23 | 16 | path: Path, |
| 24 | 17 | /// For error reporting purposes only. |
| 25 | 18 | /// If this represents an object in an archive, it's the basename of the |
| 26 | 19 | /// object, and path refers to the archive. |
| 27 | archive_member_name: ?[]const u8, | |
| 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 = &.{}, | |
| 20 | archive_member_name: Wasm.OptionalString, | |
| 44 | 21 | /// Represents the function ID that must be called on startup. |
| 45 | 22 | /// This is `null` by default as runtimes may determine the startup |
| 46 | 23 | /// function themselves. This is essentially legacy. |
| 47 | start: ?u32 = null, | |
| 48 | /// A slice of features that tell the linker what features are mandatory, | |
| 49 | /// used (or therefore missing) and must generate an error when another | |
| 50 | /// object uses features that are not supported by the other. | |
| 51 | features: []const Wasm.Feature = &.{}, | |
| 52 | /// A table that maps the relocations we must perform where the key represents | |
| 53 | /// the section that the list of relocations applies to. | |
| 54 | relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty, | |
| 55 | /// Table of symbols belonging to this Object file | |
| 56 | symtable: []Symbol = &.{}, | |
| 57 | /// Extra metadata about the linking section, such as alignment of segments and their name | |
| 58 | segment_info: []const Wasm.NamedSegment = &.{}, | |
| 59 | /// A sequence of function initializers that must be called on startup | |
| 60 | init_funcs: []const Wasm.InitFunc = &.{}, | |
| 61 | /// Comdat information | |
| 62 | comdat_info: []const Wasm.Comdat = &.{}, | |
| 63 | /// Represents non-synthetic sections that can essentially be mem-cpy'd into place | |
| 64 | /// after performing relocations. | |
| 65 | relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty, | |
| 66 | /// Amount of functions in the `import` sections. | |
| 67 | imported_functions_count: u32 = 0, | |
| 68 | /// Amount of globals in the `import` section. | |
| 69 | imported_globals_count: u32 = 0, | |
| 70 | /// Amount of tables in the `import` section. | |
| 71 | imported_tables_count: u32 = 0, | |
| 72 | ||
| 73 | /// Represents a single item within a section (depending on its `type`) | |
| 74 | pub const RelocatableData = struct { | |
| 75 | /// The type of the relocatable data | |
| 76 | type: Tag, | |
| 77 | /// Pointer to the data of the segment, where its length is written to `size` | |
| 78 | data: [*]u8, | |
| 79 | /// The size in bytes of the data representing the segment within the section | |
| 80 | size: u32, | |
| 81 | /// The index within the section itself, or in case of a debug section, | |
| 82 | /// the offset within the `string_table`. | |
| 83 | index: u32, | |
| 84 | /// The offset within the section where the data starts | |
| 85 | offset: u32, | |
| 86 | /// Represents the index of the section it belongs to | |
| 87 | section_index: u32, | |
| 88 | /// Whether the relocatable section is represented by a symbol or not. | |
| 89 | /// Can only be `true` for custom sections. | |
| 90 | represented: bool = false, | |
| 91 | ||
| 92 | const Tag = enum { data, code, custom }; | |
| 93 | ||
| 94 | /// Returns the alignment of the segment, by retrieving it from the segment | |
| 95 | /// meta data of the given object file. | |
| 96 | /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's | |
| 97 | /// alignment to retrieve the natural alignment. | |
| 98 | pub fn getAlignment(relocatable_data: RelocatableData, object: *const Object) Alignment { | |
| 99 | if (relocatable_data.type != .data) return .@"1"; | |
| 100 | return object.segment_info[relocatable_data.index].alignment; | |
| 101 | } | |
| 102 | ||
| 103 | /// Returns the symbol kind that corresponds to the relocatable section | |
| 104 | pub fn getSymbolKind(relocatable_data: RelocatableData) Symbol.Tag { | |
| 105 | return switch (relocatable_data.type) { | |
| 106 | .data => .data, | |
| 107 | .code => .function, | |
| 108 | .custom => .section, | |
| 109 | }; | |
| 110 | } | |
| 111 | ||
| 112 | /// Returns the index within a section, or in case of a custom section, | |
| 113 | /// returns the section index within the object file. | |
| 114 | pub fn getIndex(relocatable_data: RelocatableData) u32 { | |
| 115 | if (relocatable_data.type == .custom) return relocatable_data.section_index; | |
| 116 | return relocatable_data.index; | |
| 117 | } | |
| 24 | start_function: Wasm.OptionalObjectFunctionIndex, | |
| 25 | /// A slice of features that tell the linker what features are mandatory, used | |
| 26 | /// (or therefore missing) and must generate an error when another object uses | |
| 27 | /// features that are not supported by the other. | |
| 28 | features: Wasm.Feature.Set, | |
| 29 | /// Points into `Wasm.object_functions` | |
| 30 | functions: RelativeSlice, | |
| 31 | /// Points into `Wasm.object_function_imports` | |
| 32 | function_imports: RelativeSlice, | |
| 33 | /// Points into `Wasm.object_global_imports` | |
| 34 | global_imports: RelativeSlice, | |
| 35 | /// Points into `Wasm.object_table_imports` | |
| 36 | table_imports: RelativeSlice, | |
| 37 | // Points into `Wasm.object_data_imports` | |
| 38 | data_imports: RelativeSlice, | |
| 39 | /// Points into Wasm object_custom_segments | |
| 40 | custom_segments: RelativeSlice, | |
| 41 | /// Points into Wasm object_init_funcs | |
| 42 | init_funcs: RelativeSlice, | |
| 43 | /// Points into Wasm object_comdats | |
| 44 | comdats: RelativeSlice, | |
| 45 | /// Guaranteed to be non-null when functions has nonzero length. | |
| 46 | code_section_index: ?Wasm.ObjectSectionIndex, | |
| 47 | /// Guaranteed to be non-null when globals has nonzero length. | |
| 48 | global_section_index: ?Wasm.ObjectSectionIndex, | |
| 49 | /// Guaranteed to be non-null when data segments has nonzero length. | |
| 50 | data_section_index: ?Wasm.ObjectSectionIndex, | |
| 51 | is_included: bool, | |
| 52 | ||
| 53 | pub const RelativeSlice = struct { | |
| 54 | off: u32, | |
| 55 | len: u32, | |
| 118 | 56 | }; |
| 119 | 57 | |
| 120 | /// Initializes a new `Object` from a wasm object file. | |
| 121 | /// This also parses and verifies the object file. | |
| 122 | /// When a max size is given, will only parse up to the given size, | |
| 123 | /// else will read until the end of the file. | |
| 124 | pub fn create( | |
| 125 | wasm: *Wasm, | |
| 126 | file_contents: []const u8, | |
| 127 | path: Path, | |
| 128 | archive_member_name: ?[]const u8, | |
| 129 | ) !Object { | |
| 130 | const gpa = wasm.base.comp.gpa; | |
| 131 | var object: Object = .{ | |
| 132 | .path = path, | |
| 133 | .archive_member_name = archive_member_name, | |
| 58 | pub const SegmentInfo = struct { | |
| 59 | name: Wasm.String, | |
| 60 | flags: Flags, | |
| 61 | ||
| 62 | /// Matches the ABI. | |
| 63 | pub const Flags = packed struct(u32) { | |
| 64 | /// Signals that the segment contains only null terminated strings allowing | |
| 65 | /// the linker to perform merging. | |
| 66 | strings: bool, | |
| 67 | /// The segment contains thread-local data. This means that a unique copy | |
| 68 | /// of this segment will be created for each thread. | |
| 69 | tls: bool, | |
| 70 | /// If the object file is included in the final link, the segment should be | |
| 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 = .{ | |
| 137 | .object = &object, | |
| 138 | .wasm = wasm, | |
| 139 | .reader = std.io.fixedBufferStream(file_contents), | |
| 140 | }; | |
| 141 | try parser.parseObject(gpa); | |
| 80 | pub const FunctionImport = struct { | |
| 81 | module_name: Wasm.String, | |
| 82 | name: Wasm.String, | |
| 83 | function_index: ScratchSpace.FuncTypeIndex, | |
| 84 | }; | |
| 142 | 85 | |
| 143 | return object; | |
| 144 | } | |
| 86 | pub const GlobalImport = struct { | |
| 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 | |
| 147 | /// the same allocator that was used when `init` was called. | |
| 148 | pub fn deinit(object: *Object, gpa: Allocator) void { | |
| 149 | for (object.func_types) |func_ty| { | |
| 150 | gpa.free(func_ty.params); | |
| 151 | gpa.free(func_ty.returns); | |
| 152 | } | |
| 153 | gpa.free(object.func_types); | |
| 154 | gpa.free(object.functions); | |
| 155 | gpa.free(object.imports); | |
| 156 | gpa.free(object.tables); | |
| 157 | gpa.free(object.memories); | |
| 158 | gpa.free(object.globals); | |
| 159 | gpa.free(object.exports); | |
| 160 | for (object.elements) |el| { | |
| 161 | gpa.free(el.func_indexes); | |
| 162 | } | |
| 163 | gpa.free(object.elements); | |
| 164 | gpa.free(object.features); | |
| 165 | for (object.relocations.values()) |val| { | |
| 166 | gpa.free(val); | |
| 167 | } | |
| 168 | object.relocations.deinit(gpa); | |
| 169 | gpa.free(object.symtable); | |
| 170 | gpa.free(object.comdat_info); | |
| 171 | gpa.free(object.init_funcs); | |
| 172 | for (object.segment_info) |info| { | |
| 173 | gpa.free(info.name); | |
| 174 | } | |
| 175 | gpa.free(object.segment_info); | |
| 176 | { | |
| 177 | var it = object.relocatable_data.valueIterator(); | |
| 178 | while (it.next()) |relocatable_data| { | |
| 179 | for (relocatable_data.*) |rel_data| { | |
| 180 | gpa.free(rel_data.data[0..rel_data.size]); | |
| 181 | } | |
| 182 | gpa.free(relocatable_data.*); | |
| 183 | } | |
| 184 | } | |
| 185 | object.relocatable_data.deinit(gpa); | |
| 186 | object.* = undefined; | |
| 187 | } | |
| 93 | pub const TableImport = struct { | |
| 94 | module_name: Wasm.String, | |
| 95 | name: Wasm.String, | |
| 96 | limits_min: u32, | |
| 97 | limits_max: u32, | |
| 98 | limits_has_max: bool, | |
| 99 | limits_is_shared: bool, | |
| 100 | ref_type: std.wasm.RefType, | |
| 101 | }; | |
| 188 | 102 | |
| 189 | /// Finds the import within the list of imports from a given kind and index of that kind. | |
| 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 | } | |
| 103 | pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx }; | |
| 200 | 104 | |
| 201 | /// Checks if the object file is an MVP version. | |
| 202 | /// When that's the case, we check if there's an import table definition with its name | |
| 203 | /// set to '__indirect_function_table". When that's also the case, | |
| 204 | /// we initialize a new table symbol that corresponds to that import and return that symbol. | |
| 205 | /// | |
| 206 | /// When the object file is *NOT* MVP, we return `null`. | |
| 207 | fn checkLegacyIndirectFunctionTable(object: *Object, wasm: *const Wasm) !?Symbol { | |
| 208 | const diags = &wasm.base.comp.link_diags; | |
| 105 | pub const SubsectionType = enum(u8) { | |
| 106 | segment_info = 5, | |
| 107 | init_funcs = 6, | |
| 108 | comdat_info = 7, | |
| 109 | symbol_table = 8, | |
| 110 | }; | |
| 209 | 111 | |
| 210 | var table_count: usize = 0; | |
| 211 | for (object.symtable) |sym| { | |
| 212 | if (sym.tag == .table) table_count += 1; | |
| 213 | } | |
| 112 | /// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md | |
| 113 | pub const RelocationType = enum(u8) { | |
| 114 | function_index_leb = 0, | |
| 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 | |
| 216 | if (object.imported_tables_count == table_count) return null; | |
| 143 | pub const Symbol = struct { | |
| 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) { | |
| 219 | return diags.failParse(object.path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{ | |
| 220 | object.imported_tables_count, | |
| 221 | table_count, | |
| 222 | }); | |
| 223 | } | |
| 158 | const Pointee = union(enum) { | |
| 159 | function: Wasm.ObjectFunctionIndex, | |
| 160 | function_import: ScratchSpace.FuncImportIndex, | |
| 161 | data: Wasm.ObjectData.Index, | |
| 162 | data_import: void, | |
| 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). | |
| 226 | if (object.tables.len > 0) { | |
| 227 | return diags.failParse(object.path, "unexpected table definition without representing table symbols.", .{}); | |
| 228 | } | |
| 171 | pub const ScratchSpace = struct { | |
| 172 | func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty, | |
| 173 | func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty, | |
| 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) { | |
| 231 | return diags.failParse(object.path, "found more than one table import, but no representing table symbols", .{}); | |
| 232 | } | |
| 193 | /// Index into `func_imports`. | |
| 194 | const FuncImportIndex = enum(u32) { | |
| 195 | _, | |
| 233 | 196 | |
| 234 | const table_import: Wasm.Import = for (object.imports) |imp| { | |
| 235 | if (imp.kind == .table) { | |
| 236 | break imp; | |
| 197 | fn ptr(index: FuncImportIndex, ss: *const ScratchSpace) *FunctionImport { | |
| 198 | return &ss.func_imports.items[@intFromEnum(index)]; | |
| 237 | 199 | } |
| 238 | } else unreachable; | |
| 200 | }; | |
| 239 | 201 | |
| 240 | if (table_import.name != wasm.preloaded_strings.__indirect_function_table) { | |
| 241 | return diags.failParse(object.path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{ | |
| 242 | wasm.stringSlice(table_import.name), | |
| 243 | }); | |
| 244 | } | |
| 202 | /// Index into `global_imports`. | |
| 203 | const GlobalImportIndex = enum(u32) { | |
| 204 | _, | |
| 245 | 205 | |
| 246 | var table_symbol: Symbol = .{ | |
| 247 | .flags = 0, | |
| 248 | .name = table_import.name, | |
| 249 | .tag = .table, | |
| 250 | .index = 0, | |
| 251 | .virtual_address = undefined, | |
| 206 | fn ptr(index: GlobalImportIndex, ss: *const ScratchSpace) *GlobalImport { | |
| 207 | return &ss.global_imports.items[@intFromEnum(index)]; | |
| 208 | } | |
| 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 { | |
| 259 | reader: std.io.FixedBufferStream([]const u8), | |
| 260 | /// Object file we're building | |
| 261 | object: *Object, | |
| 262 | /// Mutable so that the string table can be modified. | |
| 263 | wasm: *Wasm, | |
| 211 | /// Index into `table_imports`. | |
| 212 | const TableImportIndex = enum(u32) { | |
| 213 | _, | |
| 214 | ||
| 215 | fn ptr(index: TableImportIndex, ss: *const ScratchSpace) *TableImport { | |
| 216 | return &ss.table_imports.items[@intFromEnum(index)]; | |
| 217 | } | |
| 218 | }; | |
| 264 | 219 | |
| 265 | fn parseObject(parser: *Parser, gpa: Allocator) anyerror!void { | |
| 266 | const wasm = parser.wasm; | |
| 220 | /// Index into `func_types`. | |
| 221 | const FuncTypeIndex = enum(u32) { | |
| 222 | _, | |
| 267 | 223 | |
| 268 | { | |
| 269 | var magic_bytes: [4]u8 = undefined; | |
| 270 | try parser.reader.reader().readNoEof(&magic_bytes); | |
| 271 | if (!std.mem.eql(u8, &magic_bytes, &std.wasm.magic)) return error.BadObjectMagic; | |
| 224 | fn ptr(index: FuncTypeIndex, ss: *const ScratchSpace) *Wasm.FunctionType.Index { | |
| 225 | return &ss.func_types.items[@intFromEnum(index)]; | |
| 272 | 226 | } |
| 227 | }; | |
| 273 | 228 | |
| 274 | const version = try parser.reader.reader().readInt(u32, .little); | |
| 275 | parser.object.version = version; | |
| 276 | ||
| 277 | var saw_linking_section = false; | |
| 278 | ||
| 279 | var section_index: u32 = 0; | |
| 280 | while (parser.reader.reader().readByte()) |byte| : (section_index += 1) { | |
| 281 | const len = try readLeb(u32, parser.reader.reader()); | |
| 282 | var limited_reader = std.io.limitedReader(parser.reader.reader(), len); | |
| 283 | const reader = limited_reader.reader(); | |
| 284 | switch (@as(std.wasm.Section, @enumFromInt(byte))) { | |
| 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; | |
| 229 | pub fn deinit(ss: *ScratchSpace, gpa: Allocator) void { | |
| 230 | ss.exports.deinit(gpa); | |
| 231 | ss.func_types.deinit(gpa); | |
| 232 | ss.func_type_indexes.deinit(gpa); | |
| 233 | ss.func_imports.deinit(gpa); | |
| 234 | ss.global_imports.deinit(gpa); | |
| 235 | ss.table_imports.deinit(gpa); | |
| 236 | ss.symbol_table.deinit(gpa); | |
| 237 | ss.segment_info.deinit(gpa); | |
| 238 | ss.* = undefined; | |
| 239 | } | |
| 328 | 240 | |
| 329 | for (try readVec(&type_val.params, reader, gpa)) |*param| { | |
| 330 | param.* = try readEnum(std.wasm.Valtype, reader); | |
| 331 | } | |
| 241 | fn clear(ss: *ScratchSpace) void { | |
| 242 | ss.exports.clearRetainingCapacity(); | |
| 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| { | |
| 334 | result.* = try readEnum(std.wasm.Valtype, reader); | |
| 253 | pub fn parse( | |
| 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); | |
| 338 | }, | |
| 339 | .import => { | |
| 340 | for (try readVec(&parser.object.imports, reader, gpa)) |*import| { | |
| 341 | const module_len = try readLeb(u32, reader); | |
| 342 | const module_name = try gpa.alloc(u8, module_len); | |
| 343 | defer gpa.free(module_name); | |
| 344 | try reader.readNoEof(module_name); | |
| 345 | ||
| 346 | const name_len = try readLeb(u32, reader); | |
| 347 | const name = try gpa.alloc(u8, name_len); | |
| 348 | defer gpa.free(name); | |
| 349 | try reader.readNoEof(name); | |
| 350 | ||
| 351 | const kind = try readEnum(std.wasm.ExternalKind, reader); | |
| 352 | const kind_value: std.wasm.Import.Kind = switch (kind) { | |
| 353 | .function => val: { | |
| 354 | parser.object.imported_functions_count += 1; | |
| 355 | break :val .{ .function = try readLeb(u32, reader) }; | |
| 499 | } else if (std.mem.startsWith(u8, section_name, "reloc.")) { | |
| 500 | // 'The "reloc." custom sections must come after the "linking" custom section' | |
| 501 | if (!saw_linking_section) return error.RelocBeforeLinkingSection; | |
| 502 | ||
| 503 | // "Relocation sections start with an identifier specifying | |
| 504 | // which section they apply to, and must be sequenced in | |
| 505 | // the module after that section." | |
| 506 | // "Relocation sections can only target code, data and custom sections." | |
| 507 | const local_section, pos = readLeb(u32, bytes, pos); | |
| 508 | const count, pos = readLeb(u32, bytes, pos); | |
| 509 | const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section); | |
| 510 | ||
| 511 | log.debug("found {d} relocations for section={d}", .{ count, section }); | |
| 512 | ||
| 513 | var prev_offset: u32 = 0; | |
| 514 | try wasm.object_relocations.ensureUnusedCapacity(gpa, count); | |
| 515 | for (0..count) |_| { | |
| 516 | const tag: RelocationType = @enumFromInt(bytes[pos]); | |
| 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) }, | |
| 358 | .global => val: { | |
| 359 | parser.object.imported_globals_count += 1; | |
| 360 | break :val .{ .global = .{ | |
| 361 | .valtype = try readEnum(std.wasm.Valtype, reader), | |
| 362 | .mutable = (try reader.readByte()) == 0x01, | |
| 363 | } }; | |
| 575 | .section_offset_i32 => { | |
| 576 | const addend: i32, pos = readLeb(i32, bytes, pos); | |
| 577 | wasm.object_relocations.appendAssumeCapacity(.{ | |
| 578 | .tag = .section_offset_i32, | |
| 579 | .offset = offset, | |
| 580 | .pointee = .{ .section = sym.pointee.section }, | |
| 581 | .addend = addend, | |
| 582 | }); | |
| 364 | 583 | }, |
| 365 | .table => val: { | |
| 366 | parser.object.imported_tables_count += 1; | |
| 367 | break :val .{ .table = .{ | |
| 368 | .reftype = try readEnum(std.wasm.RefType, reader), | |
| 369 | .limits = try readLimits(reader), | |
| 370 | } }; | |
| 584 | .type_index_leb => { | |
| 585 | wasm.object_relocations.appendAssumeCapacity(.{ | |
| 586 | .tag = .type_index_leb, | |
| 587 | .offset = offset, | |
| 588 | .pointee = .{ .type_index = ss.func_types.items[index] }, | |
| 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.* = .{ | |
| 375 | .module_name = try wasm.internString(module_name), | |
| 376 | .name = try wasm.internString(name), | |
| 377 | .kind = kind_value, | |
| 378 | }; | |
| 379 | } | |
| 380 | try assertEnd(reader); | |
| 381 | }, | |
| 382 | .function => { | |
| 383 | for (try readVec(&parser.object.functions, reader, gpa)) |*func| { | |
| 384 | func.* = .{ .type_index = try readLeb(u32, reader) }; | |
| 385 | } | |
| 386 | try assertEnd(reader); | |
| 387 | }, | |
| 388 | .table => { | |
| 389 | for (try readVec(&parser.object.tables, reader, gpa)) |*table| { | |
| 390 | table.* = .{ | |
| 391 | .reftype = try readEnum(std.wasm.RefType, reader), | |
| 392 | .limits = try readLimits(reader), | |
| 393 | }; | |
| 635 | .table_number_leb => { | |
| 636 | wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { | |
| 637 | .table => .{ | |
| 638 | .tag = .fromType(tag), | |
| 639 | .offset = offset, | |
| 640 | .pointee = .{ .table = sym.pointee.table }, | |
| 641 | .addend = undefined, | |
| 642 | }, | |
| 643 | .table_import => .{ | |
| 644 | .tag = .fromTypeImport(tag), | |
| 645 | .offset = offset, | |
| 646 | .pointee = .{ .symbol_name = sym.name.unwrap().? }, | |
| 647 | .addend = undefined, | |
| 648 | }, | |
| 649 | else => unreachable, | |
| 650 | }); | |
| 651 | }, | |
| 652 | .event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}), | |
| 653 | } | |
| 394 | 654 | } |
| 395 | try assertEnd(reader); | |
| 396 | }, | |
| 397 | .memory => { | |
| 398 | for (try readVec(&parser.object.memories, reader, gpa)) |*memory| { | |
| 399 | memory.* = .{ .limits = try readLimits(reader) }; | |
| 655 | ||
| 656 | try wasm.object_relocations_table.putNoClobber(gpa, section, .{ | |
| 657 | .off = @intCast(wasm.object_relocations.len - count), | |
| 658 | .len = count, | |
| 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); | |
| 402 | }, | |
| 403 | .global => { | |
| 404 | for (try readVec(&parser.object.globals, reader, gpa)) |*global| { | |
| 405 | global.* = .{ | |
| 763 | } | |
| 764 | }, | |
| 765 | .function => { | |
| 766 | const functions_len, pos = readLeb(u32, bytes, pos); | |
| 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 | 821 | .global_type = .{ |
| 407 | .valtype = try readEnum(std.wasm.Valtype, reader), | |
| 408 | .mutable = (try reader.readByte()) == 0x01, | |
| 822 | .valtype = .from(valtype), | |
| 823 | .mutable = mutable, | |
| 409 | 824 | }, |
| 410 | .init = try readInit(reader), | |
| 411 | }; | |
| 412 | } | |
| 413 | try assertEnd(reader); | |
| 414 | }, | |
| 415 | .@"export" => { | |
| 416 | for (try readVec(&parser.object.exports, reader, gpa)) |*exp| { | |
| 417 | const name_len = try readLeb(u32, reader); | |
| 418 | const name = try gpa.alloc(u8, name_len); | |
| 419 | defer gpa.free(name); | |
| 420 | try reader.readNoEof(name); | |
| 421 | exp.* = .{ | |
| 422 | .name = try wasm.internString(name), | |
| 423 | .kind = try readEnum(std.wasm.ExternalKind, reader), | |
| 424 | .index = try readLeb(u32, reader), | |
| 425 | }; | |
| 825 | }, | |
| 826 | .expr = expr, | |
| 827 | .object_index = object_index, | |
| 828 | .offset = @intCast(init_start - section_start), | |
| 829 | .size = @intCast(pos - init_start), | |
| 830 | }; | |
| 831 | } | |
| 832 | }, | |
| 833 | .@"export" => { | |
| 834 | const exports_len, pos = readLeb(u32, bytes, pos); | |
| 835 | // Read into scratch space, and then later add this data as if | |
| 836 | // it were extra symbol table entries, but allow merging with | |
| 837 | // existing symbol table data if the name matches. | |
| 838 | for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| { | |
| 839 | const name, pos = readBytes(bytes, pos); | |
| 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); | |
| 428 | }, | |
| 429 | .start => { | |
| 430 | parser.object.start = try readLeb(u32, reader); | |
| 431 | try assertEnd(reader); | |
| 432 | }, | |
| 433 | .element => { | |
| 434 | for (try readVec(&parser.object.elements, reader, gpa)) |*elem| { | |
| 435 | elem.table_index = try readLeb(u32, reader); | |
| 436 | elem.offset = try readInit(reader); | |
| 897 | //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos }; | |
| 898 | if (flags != .passive) pos = try skipInit(bytes, pos); | |
| 899 | const data_len, pos = readLeb(u32, bytes, pos); | |
| 900 | const segment_start = pos; | |
| 901 | const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]); | |
| 902 | pos += data_len; | |
| 903 | elem.* = .{ | |
| 904 | .payload = payload, | |
| 905 | .name = .none, // Populated from segment_info | |
| 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| { | |
| 439 | idx.* = try readLeb(u32, reader); | |
| 440 | } | |
| 441 | } | |
| 442 | try assertEnd(reader); | |
| 920 | const target_features = comp.root_mod.resolved_target.result.cpu.features; | |
| 921 | ||
| 922 | if (has_tls) { | |
| 923 | if (!std.Target.wasm.featureSetHas(target_features, .atomics)) | |
| 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 => { | |
| 445 | const start = reader.context.bytes_left; | |
| 446 | var index: u32 = 0; | |
| 447 | const count = try readLeb(u32, reader); | |
| 448 | const imported_function_count = parser.object.imported_functions_count; | |
| 449 | var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); | |
| 450 | defer relocatable_data.deinit(); | |
| 451 | while (index < count) : (index += 1) { | |
| 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 | }); | |
| 938 | else => { | |
| 939 | const f = feat.tag.toCpuFeature().?; | |
| 940 | if (std.Target.wasm.featureSetHas(target_features, f)) { | |
| 941 | return diags.failParse( | |
| 942 | path, | |
| 943 | "object forbids {s} but specified target features include {s}", | |
| 944 | .{ @tagName(feat.tag), @tagName(f) }, | |
| 945 | ); | |
| 465 | 946 | } |
| 466 | try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice()); | |
| 467 | 947 | }, |
| 468 | .data => { | |
| 469 | const start = reader.context.bytes_left; | |
| 470 | var index: u32 = 0; | |
| 471 | const count = try readLeb(u32, reader); | |
| 472 | var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); | |
| 473 | defer relocatable_data.deinit(); | |
| 474 | while (index < count) : (index += 1) { | |
| 475 | const flags = try readLeb(u32, reader); | |
| 476 | const data_offset = try readInit(reader); | |
| 477 | _ = flags; // TODO: Do we need to check flags to detect passive/active memory? | |
| 478 | _ = data_offset; | |
| 479 | const data_len = try readLeb(u32, reader); | |
| 480 | const offset = @as(u32, @intCast(start - reader.context.bytes_left)); | |
| 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 | }); | |
| 948 | }, | |
| 949 | .@"+", .@"=" => switch (feat.tag) { | |
| 950 | .@"shared-mem" => if (!comp.config.shared_memory) { | |
| 951 | return diags.failParse(path, "object requires shared-mem but compilation disables it", .{}); | |
| 952 | }, | |
| 953 | else => { | |
| 954 | const f = feat.tag.toCpuFeature().?; | |
| 955 | if (!std.Target.wasm.featureSetHas(target_features, f)) { | |
| 956 | return diags.failParse( | |
| 957 | path, | |
| 958 | "object requires {s} but specified target features exclude {s}", | |
| 959 | .{ @tagName(feat.tag), @tagName(f) }, | |
| 960 | ); | |
| 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, .{}), | |
| 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 | }); | |
| 963 | }, | |
| 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 | |
| 564 | /// supported will be an error. `payload_size` is required to be able | |
| 565 | /// to calculate the subsections we need to parse, as that data is not | |
| 566 | /// available within the section itparser. | |
| 567 | fn parseMetadata(parser: *Parser, gpa: Allocator, payload_size: usize) !void { | |
| 568 | var limited = std.io.limitedReader(parser.reader.reader(), payload_size); | |
| 569 | const limited_reader = limited.reader(); | |
| 570 | ||
| 571 | const version = try leb.readUleb128(u32, limited_reader); | |
| 572 | log.debug("Link meta data version: {d}", .{version}); | |
| 573 | if (version != 2) return error.UnsupportedVersion; | |
| 574 | ||
| 575 | while (limited.bytes_left > 0) { | |
| 576 | try parser.parseSubsection(gpa, limited_reader); | |
| 577 | } | |
| 967 | // Apply function type information. | |
| 968 | for (ss.func_type_indexes.items, wasm.object_functions.items[functions_start..]) |func_type, *func| { | |
| 969 | func.type_index = func_type.ptr(ss).*; | |
| 578 | 970 | } |
| 579 | 971 | |
| 580 | /// Parses a `spec.Subsection`. | |
| 581 | /// The `reader` param for this is to provide a `LimitedReader`, which allows | |
| 582 | /// us to only read until a max length. | |
| 583 | /// | |
| 584 | /// `parser` is used to provide access to other sections that may be needed, | |
| 585 | /// such as access to the `import` section to find the name of a symbol. | |
| 586 | fn parseSubsection(parser: *Parser, gpa: Allocator, reader: anytype) !void { | |
| 587 | const wasm = parser.wasm; | |
| 588 | const sub_type = try leb.readUleb128(u8, reader); | |
| 589 | log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))}); | |
| 590 | const payload_len = try leb.readUleb128(u32, reader); | |
| 591 | if (payload_len == 0) return; | |
| 592 | ||
| 593 | var limited = std.io.limitedReader(reader, payload_len); | |
| 594 | const limited_reader = limited.reader(); | |
| 595 | ||
| 596 | // every subsection contains a 'count' field | |
| 597 | const count = try leb.readUleb128(u32, limited_reader); | |
| 598 | ||
| 599 | switch (@as(Wasm.SubsectionType, @enumFromInt(sub_type))) { | |
| 600 | .WASM_SEGMENT_INFO => { | |
| 601 | const segments = try gpa.alloc(Wasm.NamedSegment, count); | |
| 602 | errdefer gpa.free(segments); | |
| 603 | for (segments) |*segment| { | |
| 604 | const name_len = try leb.readUleb128(u32, reader); | |
| 605 | const name = try gpa.alloc(u8, name_len); | |
| 606 | errdefer gpa.free(name); | |
| 607 | try reader.readNoEof(name); | |
| 608 | segment.* = .{ | |
| 609 | .name = name, | |
| 610 | .alignment = @enumFromInt(try leb.readUleb128(u32, reader)), | |
| 611 | .flags = try leb.readUleb128(u32, reader), | |
| 612 | }; | |
| 613 | log.debug("Found segment: {s} align({d}) flags({b})", .{ | |
| 614 | segment.name, | |
| 615 | segment.alignment, | |
| 616 | segment.flags, | |
| 972 | // Apply symbol table information. | |
| 973 | for (ss.symbol_table.items) |symbol| switch (symbol.pointee) { | |
| 974 | .function_import => |index| { | |
| 975 | const ptr = index.ptr(ss); | |
| 976 | const name = symbol.name.unwrap() orelse ptr.name; | |
| 977 | if (symbol.flags.binding == .local) { | |
| 978 | diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); | |
| 979 | continue; | |
| 980 | } | |
| 981 | const gop = try wasm.object_function_imports.getOrPut(gpa, name); | |
| 982 | const fn_ty_index = ptr.function_index.ptr(ss).*; | |
| 983 | if (gop.found_existing) { | |
| 984 | if (gop.value_ptr.type != fn_ty_index) { | |
| 985 | var err = try diags.addErrorWithNotes(2); | |
| 986 | try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)}); | |
| 987 | gop.value_ptr.source_location.addNote(&err, "imported as {} here", .{ | |
| 988 | gop.value_ptr.type.fmt(wasm), | |
| 617 | 989 | }); |
| 618 | ||
| 619 | // support legacy object files that specified being TLS by the name instead of the TLS flag. | |
| 620 | if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) { | |
| 621 | // set the flag so we can simply check for the flag in the rest of the linker. | |
| 622 | segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS); | |
| 990 | source_location.addNote(&err, "imported as {} here", .{fn_ty_index.fmt(wasm)}); | |
| 991 | continue; | |
| 992 | } | |
| 993 | if (gop.value_ptr.module_name != ptr.module_name.toOptional()) { | |
| 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; | |
| 626 | }, | |
| 627 | .WASM_INIT_FUNCS => { | |
| 628 | const funcs = try gpa.alloc(Wasm.InitFunc, count); | |
| 629 | errdefer gpa.free(funcs); | |
| 630 | for (funcs) |*func| { | |
| 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 }); | |
| 1004 | if (gop.value_ptr.name != ptr.name) { | |
| 1005 | var err = try diags.addErrorWithNotes(2); | |
| 1006 | try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)}); | |
| 1007 | gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)}); | |
| 1008 | source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)}); | |
| 1009 | continue; | |
| 636 | 1010 | } |
| 637 | parser.object.init_funcs = funcs; | |
| 638 | }, | |
| 639 | .WASM_COMDAT_INFO => { | |
| 640 | const comdats = try gpa.alloc(Wasm.Comdat, count); | |
| 641 | errdefer gpa.free(comdats); | |
| 642 | for (comdats) |*comdat| { | |
| 643 | const name_len = try leb.readUleb128(u32, reader); | |
| 644 | const name = try gpa.alloc(u8, name_len); | |
| 645 | errdefer gpa.free(name); | |
| 646 | try reader.readNoEof(name); | |
| 647 | ||
| 648 | const flags = try leb.readUleb128(u32, reader); | |
| 649 | if (flags != 0) { | |
| 650 | return error.UnexpectedValue; | |
| 651 | } | |
| 652 | ||
| 653 | const symbol_count = try leb.readUleb128(u32, reader); | |
| 654 | const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count); | |
| 655 | errdefer gpa.free(symbols); | |
| 656 | for (symbols) |*symbol| { | |
| 657 | symbol.* = .{ | |
| 658 | .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))), | |
| 659 | .index = try leb.readUleb128(u32, reader), | |
| 660 | }; | |
| 1011 | } else { | |
| 1012 | gop.value_ptr.* = .{ | |
| 1013 | .flags = symbol.flags, | |
| 1014 | .module_name = ptr.module_name.toOptional(), | |
| 1015 | .name = ptr.name, | |
| 1016 | .source_location = source_location, | |
| 1017 | .resolution = .unresolved, | |
| 1018 | .type = fn_ty_index, | |
| 1019 | }; | |
| 1020 | } | |
| 1021 | }, | |
| 1022 | .global_import => |index| { | |
| 1023 | const ptr = index.ptr(ss); | |
| 1024 | const name = symbol.name.unwrap() orelse ptr.name; | |
| 1025 | if (symbol.flags.binding == .local) { | |
| 1026 | diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); | |
| 1027 | continue; | |
| 1028 | } | |
| 1029 | const gop = try wasm.object_global_imports.getOrPut(gpa, name); | |
| 1030 | if (gop.found_existing) { | |
| 1031 | const existing_ty = gop.value_ptr.type(); | |
| 1032 | if (ptr.valtype != existing_ty.valtype) { | |
| 1033 | var err = try diags.addErrorWithNotes(2); | |
| 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 | ||
| 663 | comdat.* = .{ | |
| 664 | .name = name, | |
| 665 | .flags = flags, | |
| 666 | .symbols = symbols, | |
| 667 | }; | |
| 1058 | source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)}); | |
| 1059 | continue; | |
| 668 | 1060 | } |
| 669 | ||
| 670 | parser.object.comdat_info = comdats; | |
| 671 | }, | |
| 672 | .WASM_SYMBOL_TABLE => { | |
| 673 | var symbols = try std.ArrayList(Symbol).initCapacity(gpa, count); | |
| 674 | ||
| 675 | var i: usize = 0; | |
| 676 | while (i < count) : (i += 1) { | |
| 677 | const symbol = symbols.addOneAssumeCapacity(); | |
| 678 | symbol.* = try parser.parseSymbol(gpa, reader); | |
| 679 | log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{ | |
| 680 | @tagName(symbol.tag), | |
| 681 | wasm.stringSlice(symbol.name), | |
| 682 | symbol.flags, | |
| 1061 | if (gop.value_ptr.name != ptr.name) { | |
| 1062 | var err = try diags.addErrorWithNotes(2); | |
| 1063 | try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)}); | |
| 1064 | gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)}); | |
| 1065 | source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)}); | |
| 1066 | continue; | |
| 1067 | } | |
| 1068 | } else { | |
| 1069 | gop.value_ptr.* = .{ | |
| 1070 | .flags = symbol.flags, | |
| 1071 | .module_name = ptr.module_name.toOptional(), | |
| 1072 | .name = ptr.name, | |
| 1073 | .source_location = source_location, | |
| 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 | ||
| 686 | // we found all symbols, check for indirect function table | |
| 687 | // in case of an MVP object file | |
| 688 | if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm)) |symbol| { | |
| 689 | try symbols.append(symbol); | |
| 690 | log.debug("Found legacy indirect function table. Created symbol", .{}); | |
| 1108 | if (gop.value_ptr.name != ptr.name) { | |
| 1109 | var err = try diags.addErrorWithNotes(2); | |
| 1110 | try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)}); | |
| 1111 | gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)}); | |
| 1112 | source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)}); | |
| 1113 | continue; | |
| 691 | 1114 | } |
| 692 | ||
| 693 | // Not all debug sections may be represented by a symbol, for those sections | |
| 694 | // we manually create a symbol. | |
| 695 | if (parser.object.relocatable_data.get(.custom)) |custom_sections| { | |
| 696 | for (custom_sections) |*data| { | |
| 697 | if (!data.represented) { | |
| 698 | const name = wasm.castToString(data.index); | |
| 699 | try symbols.append(.{ | |
| 700 | .name = name, | |
| 701 | .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | |
| 702 | .tag = .section, | |
| 703 | .virtual_address = 0, | |
| 704 | .index = data.section_index, | |
| 705 | }); | |
| 706 | data.represented = true; | |
| 707 | log.debug("Created synthetic custom section symbol for '{s}'", .{ | |
| 708 | wasm.stringSlice(name), | |
| 709 | }); | |
| 710 | } | |
| 711 | } | |
| 1115 | if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong; | |
| 1116 | if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false; | |
| 1117 | if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true; | |
| 1118 | } else { | |
| 1119 | gop.value_ptr.* = .{ | |
| 1120 | .flags = symbol.flags, | |
| 1121 | .module_name = ptr.module_name, | |
| 1122 | .name = ptr.name, | |
| 1123 | .source_location = source_location, | |
| 1124 | .resolution = .unresolved, | |
| 1125 | .limits_min = ptr.limits_min, | |
| 1126 | .limits_max = ptr.limits_max, | |
| 1127 | }; | |
| 1128 | gop.value_ptr.flags.limits_has_max = ptr.limits_has_max; | |
| 1129 | gop.value_ptr.flags.limits_is_shared = ptr.limits_is_shared; | |
| 1130 | gop.value_ptr.flags.ref_type = .from(ptr.ref_type); | |
| 1131 | } | |
| 1132 | }, | |
| 1133 | .data_import => { | |
| 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, | |
| 720 | /// requires access to `Object` to find the name of a symbol when it's | |
| 721 | /// an import and flag `WASM_SYM_EXPLICIT_NAME` is not set. | |
| 722 | fn parseSymbol(parser: *Parser, gpa: Allocator, reader: anytype) !Symbol { | |
| 723 | const wasm = parser.wasm; | |
| 724 | const tag: Symbol.Tag = @enumFromInt(try leb.readUleb128(u8, reader)); | |
| 725 | const flags = try leb.readUleb128(u32, reader); | |
| 726 | var symbol: Symbol = .{ | |
| 727 | .flags = flags, | |
| 728 | .tag = tag, | |
| 729 | .name = undefined, | |
| 730 | .index = undefined, | |
| 731 | .virtual_address = undefined, | |
| 1316 | // Apply segment_info. | |
| 1317 | const data_segments = wasm.object_data_segments.items[data_segment_start..]; | |
| 1318 | if (data_segments.len != ss.segment_info.items.len) { | |
| 1319 | return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{ | |
| 1320 | data_segments.len, ss.segment_info.items.len, | |
| 1321 | }); | |
| 1322 | } | |
| 1323 | for (data_segments, ss.segment_info.items) |*data, info| { | |
| 1324 | data.name = info.name.toOptional(); | |
| 1325 | data.flags = .{ | |
| 1326 | .is_passive = data.flags.is_passive, | |
| 1327 | .strings = info.flags.strings, | |
| 1328 | .tls = info.flags.tls, | |
| 1329 | .retain = info.flags.retain, | |
| 1330 | .alignment = info.flags.alignment, | |
| 732 | 1331 | }; |
| 1332 | } | |
| 733 | 1333 | |
| 734 | switch (tag) { | |
| 735 | .data => { | |
| 736 | const name_len = try leb.readUleb128(u32, reader); | |
| 737 | const name = try gpa.alloc(u8, name_len); | |
| 738 | defer gpa.free(name); | |
| 739 | try reader.readNoEof(name); | |
| 740 | symbol.name = try wasm.internString(name); | |
| 741 | ||
| 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 | }, | |
| 1334 | // Check for indirect function table in case of an MVP object file. | |
| 1335 | legacy_indirect_function_table: { | |
| 1336 | // If there is a symbol for each import table, this is not a legacy object file. | |
| 1337 | if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table; | |
| 1338 | if (table_import_symbol_count != 0) { | |
| 1339 | return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{ | |
| 1340 | ss.table_imports.items.len, table_import_symbol_count, | |
| 1341 | }); | |
| 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 | |
| 779 | /// a slice of ptr child's element type. | |
| 780 | fn readVec(ptr: anytype, reader: anytype, gpa: Allocator) ![]ElementType(@TypeOf(ptr)) { | |
| 781 | const len = try readLeb(u32, reader); | |
| 782 | const slice = try gpa.alloc(ElementType(@TypeOf(ptr)), len); | |
| 783 | ptr.* = slice; | |
| 784 | return slice; | |
| 1373 | const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start); | |
| 1374 | if (functions_len > 0 and code_section_index == null) | |
| 1375 | return diags.failParse(path, "code section missing ({d} functions)", .{functions_len}); | |
| 1376 | ||
| 1377 | return .{ | |
| 1378 | .version = version, | |
| 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 { | |
| 788 | return meta.Elem(meta.Child(ptr)); | |
| 1422 | /// Based on the "features" custom section, parses it into a list of | |
| 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 | |
| 792 | /// signedness of the given type `T`. | |
| 793 | /// Asserts `T` is an integer. | |
| 794 | fn readLeb(comptime T: type, reader: anytype) !T { | |
| 795 | return switch (@typeInfo(T).int.signedness) { | |
| 796 | .signed => try leb.readIleb128(T, reader), | |
| 797 | .unsigned => try leb.readUleb128(T, reader), | |
| 1462 | fn readLeb(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } { | |
| 1463 | var fbr = std.io.fixedBufferStream(bytes[pos..]); | |
| 1464 | return .{ | |
| 1465 | switch (@typeInfo(T).int.signedness) { | |
| 1466 | .signed => std.leb.readIleb128(T, fbr.reader()) catch unreachable, | |
| 1467 | .unsigned => std.leb.readUleb128(T, fbr.reader()) catch unreachable, | |
| 1468 | }, | |
| 1469 | pos + fbr.pos, | |
| 798 | 1470 | }; |
| 799 | 1471 | } |
| 800 | 1472 | |
| 801 | /// Reads an enum type from the given reader. | |
| 802 | /// Asserts `T` is an enum | |
| 803 | fn readEnum(comptime T: type, reader: anytype) !T { | |
| 804 | switch (@typeInfo(T)) { | |
| 805 | .@"enum" => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))), | |
| 806 | else => @compileError("T must be an enum. Instead was given type " ++ @typeName(T)), | |
| 807 | } | |
| 1473 | fn readBytes(bytes: []const u8, start_pos: usize) struct { []const u8, usize } { | |
| 1474 | const len, const pos = readLeb(u32, bytes, start_pos); | |
| 1475 | return .{ | |
| 1476 | bytes[pos..][0..len], | |
| 1477 | pos + len, | |
| 1478 | }; | |
| 808 | 1479 | } |
| 809 | 1480 | |
| 810 | fn readLimits(reader: anytype) !std.wasm.Limits { | |
| 811 | const flags = try reader.readByte(); | |
| 812 | const min = try readLeb(u32, reader); | |
| 813 | var limits: std.wasm.Limits = .{ | |
| 1481 | fn readEnum(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } { | |
| 1482 | const Tag = @typeInfo(T).@"enum".tag_type; | |
| 1483 | const int, const new_pos = readLeb(Tag, bytes, pos); | |
| 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 | 1492 | .flags = flags, |
| 815 | 1493 | .min = min, |
| 816 | .max = undefined, | |
| 817 | }; | |
| 818 | if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) { | |
| 819 | limits.max = try readLeb(u32, reader); | |
| 820 | } | |
| 821 | return limits; | |
| 1494 | .max = max, | |
| 1495 | }, end_pos }; | |
| 822 | 1496 | } |
| 823 | 1497 | |
| 824 | fn readInit(reader: anytype) !std.wasm.InitExpression { | |
| 825 | const opcode = try reader.readByte(); | |
| 826 | const init_expr: std.wasm.InitExpression = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) { | |
| 827 | .i32_const => .{ .i32_const = try readLeb(i32, reader) }, | |
| 828 | .global_get => .{ .global_get = try readLeb(u32, reader) }, | |
| 829 | else => @panic("TODO: initexpression for other opcodes"), | |
| 830 | }; | |
| 1498 | fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usize } { | |
| 1499 | const end_pos = try skipInit(bytes, pos); // one after the end opcode | |
| 1500 | return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos }; | |
| 1501 | } | |
| 831 | 1502 | |
| 832 | if ((try readEnum(std.wasm.Opcode, reader)) != .end) return error.MissingEndForExpression; | |
| 833 | return init_expr; | |
| 1503 | pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize { | |
| 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 { | |
| 837 | var buf: [1]u8 = undefined; | |
| 838 | const len = try reader.read(&buf); | |
| 839 | if (len != 0) return error.MalformedSection; | |
| 840 | if (reader.context.bytes_left != 0) return error.MalformedSection; | |
| 1515 | fn skipInit(bytes: []const u8, pos: usize) !usize { | |
| 1516 | const end_pos = try exprEndPos(bytes, pos); | |
| 1517 | const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos); | |
| 1518 | if (op != .end) return error.InitExprMissingEnd; | |
| 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 | 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 | 82 | const normal_usage = |
| 79 | 83 | \\Usage: zig [command] [options] |
| 80 | 84 | \\ |
src/register_manager.zig+9-14| ... | ... | @@ -14,19 +14,14 @@ const link = @import("link.zig"); |
| 14 | 14 | |
| 15 | 15 | const log = std.log.scoped(.register_manager); |
| 16 | 16 | |
| 17 | pub const AllocateRegistersError = error{ | |
| 18 | /// No registers are available anymore | |
| 17 | pub const AllocationError = error{ | |
| 19 | 18 | OutOfRegisters, |
| 20 | /// Can happen when spilling an instruction in codegen runs out of | |
| 21 | /// memory, so we propagate that error | |
| 22 | 19 | OutOfMemory, |
| 23 | /// Can happen when spilling an instruction in codegen triggers integer | |
| 24 | /// overflow, so we propagate that error | |
| 20 | /// Compiler was asked to operate on a number larger than supported. | |
| 25 | 21 | Overflow, |
| 26 | /// Can happen when spilling an instruction triggers a codegen | |
| 27 | /// error, so we propagate that error | |
| 22 | /// Indicates the error is already stored in `failed_codegen` on the Zcu. | |
| 28 | 23 | CodegenFail, |
| 29 | } || link.File.UpdateDebugInfoError; | |
| 24 | }; | |
| 30 | 25 | |
| 31 | 26 | pub fn RegisterManager( |
| 32 | 27 | comptime Function: type, |
| ... | ... | @@ -281,7 +276,7 @@ pub fn RegisterManager( |
| 281 | 276 | comptime count: comptime_int, |
| 282 | 277 | insts: [count]?Air.Inst.Index, |
| 283 | 278 | register_class: RegisterBitSet, |
| 284 | ) AllocateRegistersError![count]Register { | |
| 279 | ) AllocationError![count]Register { | |
| 285 | 280 | comptime assert(count > 0 and count <= tracked_registers.len); |
| 286 | 281 | |
| 287 | 282 | var locked_registers = self.locked_registers; |
| ... | ... | @@ -338,7 +333,7 @@ pub fn RegisterManager( |
| 338 | 333 | self: *Self, |
| 339 | 334 | inst: ?Air.Inst.Index, |
| 340 | 335 | register_class: RegisterBitSet, |
| 341 | ) AllocateRegistersError!Register { | |
| 336 | ) AllocationError!Register { | |
| 342 | 337 | return (try self.allocRegs(1, .{inst}, register_class))[0]; |
| 343 | 338 | } |
| 344 | 339 | |
| ... | ... | @@ -349,7 +344,7 @@ pub fn RegisterManager( |
| 349 | 344 | self: *Self, |
| 350 | 345 | tracked_index: TrackedIndex, |
| 351 | 346 | inst: ?Air.Inst.Index, |
| 352 | ) AllocateRegistersError!void { | |
| 347 | ) AllocationError!void { | |
| 353 | 348 | log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst }); |
| 354 | 349 | if (!self.isRegIndexFree(tracked_index)) { |
| 355 | 350 | // Move the instruction that was previously there to a |
| ... | ... | @@ -362,7 +357,7 @@ pub fn RegisterManager( |
| 362 | 357 | } |
| 363 | 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 | 361 | log.debug("getting reg: {}", .{reg}); |
| 367 | 362 | return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst); |
| 368 | 363 | } |
| ... | ... | @@ -370,7 +365,7 @@ pub fn RegisterManager( |
| 370 | 365 | self: *Self, |
| 371 | 366 | comptime reg: Register, |
| 372 | 367 | inst: ?Air.Inst.Index, |
| 373 | ) AllocateRegistersError!void { | |
| 368 | ) AllocationError!void { | |
| 374 | 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 | 31 | _ = @import("behavior/error.zig"); |
| 32 | 32 | _ = @import("behavior/eval.zig"); |
| 33 | 33 | _ = @import("behavior/export_builtin.zig"); |
| 34 | _ = @import("behavior/export_self_referential_type_info.zig"); | |
| 35 | _ = @import("behavior/extern.zig"); | |
| 36 | 34 | _ = @import("behavior/field_parent_ptr.zig"); |
| 37 | 35 | _ = @import("behavior/floatop.zig"); |
| 38 | 36 | _ = @import("behavior/fn.zig"); |
| ... | ... | @@ -45,7 +43,6 @@ test { |
| 45 | 43 | _ = @import("behavior/hasfield.zig"); |
| 46 | 44 | _ = @import("behavior/if.zig"); |
| 47 | 45 | _ = @import("behavior/import.zig"); |
| 48 | _ = @import("behavior/import_c_keywords.zig"); | |
| 49 | 46 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| 50 | 47 | _ = @import("behavior/inline_switch.zig"); |
| 51 | 48 | _ = @import("behavior/int128.zig"); |
| ... | ... | @@ -127,6 +124,16 @@ test { |
| 127 | 124 | { |
| 128 | 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 | 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 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 7 | 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 | 14 | const S = struct { |
| 10 | 15 | const E = enum(c_int) { one, two }; |
| 11 | 16 | const e: E = .two; |
| ... | ... | @@ -33,6 +38,11 @@ test "exporting using namespace access" { |
| 33 | 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 34 | 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 | 46 | const S = struct { |
| 37 | 47 | const Inner = struct { |
| 38 | 48 | const x: u32 = 5; |
| ... | ... | @@ -46,7 +56,6 @@ test "exporting using namespace access" { |
| 46 | 56 | } |
| 47 | 57 | |
| 48 | 58 | test "exporting comptime-known value" { |
| 49 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 50 | 59 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 51 | 60 | if (builtin.zig_backend == .stage2_x86_64 and |
| 52 | 61 | (builtin.target.ofmt != .elf and |
| ... | ... | @@ -56,6 +65,11 @@ test "exporting comptime-known value" { |
| 56 | 65 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 57 | 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 | 73 | const x: u32 = 10; |
| 60 | 74 | @export(&x, .{ .name = "exporting_comptime_known_value_foo" }); |
| 61 | 75 | const S = struct { |
test/incremental/add_decl+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/incremental/add_decl_namespaced+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/incremental/change_generic_line_number+1| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=wasm32-wasi-selfhosted | |
| 2 | 3 | #update=initial version |
| 3 | 4 | #file=main.zig |
| 4 | 5 | const std = @import("std"); |
test/incremental/change_line_number+1| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | #target=wasm32-wasi-selfhosted | |
| 2 | 3 | #update=initial version |
| 3 | 4 | #file=main.zig |
| 4 | 5 | const std = @import("std"); |
test/incremental/change_shift_op+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | pub fn main() !void { |
test/incremental/change_struct_same_fields+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const S = extern struct { x: u8, y: u8 }; |
test/incremental/compile_error_then_log+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version with compile error |
| 5 | 6 | #file=main.zig |
| 6 | 7 | comptime { |
test/incremental/delete_comptime_decls+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | pub fn main() void {} |
test/incremental/fix_astgen_failure+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version with error |
| 5 | 6 | #file=main.zig |
| 6 | 7 | pub fn main() !void { |
test/incremental/hello+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/incremental/modify_inline_fn+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/incremental/move_src+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/incremental/recursive_function_becomes_non_recursive+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | pub fn main() !void { |
test/incremental/remove_enum_field+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const MyEnum = enum(u8) { |
test/incremental/remove_invalid_union_backing_enum+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const E = enum { a, b, c }; |
test/incremental/temporary_parse_error+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/incremental/type_becomes_comptime_only+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const SomeType = u32; |
test/incremental/unreferenced_error+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #target=x86_64-linux-selfhosted |
| 2 | 2 | #target=x86_64-linux-cbe |
| 3 | 3 | #target=x86_64-windows-cbe |
| 4 | #target=wasm32-wasi-selfhosted | |
| 4 | 5 | #update=initial version |
| 5 | 6 | #file=main.zig |
| 6 | 7 | const std = @import("std"); |
test/link/build.zig.zon-6| ... | ... | @@ -24,9 +24,6 @@ |
| 24 | 24 | .wasm_basic_features = .{ |
| 25 | 25 | .path = "wasm/basic-features", |
| 26 | 26 | }, |
| 27 | .wasm_bss = .{ | |
| 28 | .path = "wasm/bss", | |
| 29 | }, | |
| 30 | 27 | .wasm_export = .{ |
| 31 | 28 | .path = "wasm/export", |
| 32 | 29 | }, |
| ... | ... | @@ -48,9 +45,6 @@ |
| 48 | 45 | .wasm_producers = .{ |
| 49 | 46 | .path = "wasm/producers", |
| 50 | 47 | }, |
| 51 | .wasm_segments = .{ | |
| 52 | .path = "wasm/segments", | |
| 53 | }, | |
| 54 | 48 | .wasm_shared_memory = .{ |
| 55 | 49 | .path = "wasm/shared-memory", |
| 56 | 50 | }, |
test/link/wasm/archive/build.zig-2| ... | ... | @@ -1,7 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test it"); |
| 7 | 5 | b.default_step = test_step; |
test/link/wasm/basic-features/build.zig-2| ... | ... | @@ -1,7 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | // Library with explicitly set cpu features |
| 7 | 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 | 4 | const test_step = b.step("test", "Test"); |
| 5 | 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 | 7 | const lib = b.addExecutable(.{ |
| 13 | 8 | .name = "lib", |
| 14 | 9 | .root_module = b.createModule(.{ |
| 15 | 10 | .root_source_file = b.path("lib.zig"), |
| 16 | .optimize = .ReleaseSafe, // to make the output deterministic in address positions | |
| 11 | .optimize = .Debug, | |
| 17 | 12 | .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), |
| 18 | 13 | }), |
| 19 | 14 | }); |
| 20 | 15 | lib.entry = .disabled; |
| 21 | 16 | lib.use_lld = false; |
| 22 | 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 | |
| 24 | ||
| 25 | const check_lib = lib.checkObject(); | |
| 26 | ||
| 27 | check_lib.checkInHeaders(); | |
| 28 | check_lib.checkExact("Section global"); | |
| 29 | check_lib.checkExact("entries 3"); | |
| 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"); | |
| 18 | // Object being linked has neither functions nor globals named "foo" or "bar" and | |
| 19 | // so these names correctly fail to be exported when creating an executable. | |
| 20 | lib.expect_errors = .{ .exact = &.{ | |
| 21 | "error: manually specified export name 'foo' undefined", | |
| 22 | "error: manually specified export name 'bar' undefined", | |
| 23 | } }; | |
| 24 | _ = lib.getEmittedBin(); | |
| 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 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test it"); |
| 7 | 5 | b.default_step = test_step; |
| 8 | 6 | |
| 9 | 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 | 10 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | 11 | const no_export = b.addExecutable(.{ |
| 17 | 12 | .name = "no-export", |
| 18 | 13 | .root_module = b.createModule(.{ |
| 19 | .root_source_file = b.path("main.zig"), | |
| 14 | .root_source_file = b.path("main-hidden.zig"), | |
| 20 | 15 | .optimize = optimize, |
| 21 | 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 | 36 | const force_export = b.addExecutable(.{ |
| 42 | 37 | .name = "force", |
| 43 | 38 | .root_module = b.createModule(.{ |
| 44 | .root_source_file = b.path("main.zig"), | |
| 39 | .root_source_file = b.path("main-hidden.zig"), | |
| 45 | 40 | .optimize = optimize, |
| 46 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test it"); |
| 7 | 5 | b.default_step = test_step; |
| 8 | 6 | |
| 9 | 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 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test it"); |
| 7 | 5 | b.default_step = test_step; |
| 8 | 6 | |
| 9 | 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 | 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 | 11 | const export_table = b.addExecutable(.{ |
| 31 | 12 | .name = "export_table", |
| 32 | 13 | .root_module = b.createModule(.{ |
| ... | ... | @@ -54,24 +35,12 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 54 | 35 | regular_table.use_lld = false; |
| 55 | 36 | regular_table.link_gc_sections = false; // Ensure function table is not empty |
| 56 | 37 | |
| 57 | const check_import = import_table.checkObject(); | |
| 58 | 38 | const check_export = export_table.checkObject(); |
| 59 | 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 | 41 | check_export.checkInHeaders(); |
| 73 | 42 | check_export.checkExact("Section export"); |
| 74 | check_export.checkExact("entries 2"); | |
| 43 | check_export.checkExact("entries 3"); | |
| 75 | 44 | check_export.checkExact("name __indirect_function_table"); // as per linker specification |
| 76 | 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 | 58 | check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1 |
| 90 | 59 | check_regular.checkExact("indexes 1"); // 1 function pointer |
| 91 | 60 | |
| 92 | test_step.dependOn(&check_import.step); | |
| 93 | 61 | test_step.dependOn(&check_export.step); |
| 94 | 62 | test_step.dependOn(&check_regular.step); |
| 95 | 63 | } |
test/link/wasm/infer-features/build.zig+3-22| ... | ... | @@ -1,7 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | // Wasm Object file which we will use to infer the features from |
| 7 | 5 | const c_obj = b.addObject(.{ |
| ... | ... | @@ -37,27 +35,10 @@ pub fn build(b: *std.Build) void { |
| 37 | 35 | lib.use_lld = false; |
| 38 | 36 | lib.root_module.addObject(c_obj); |
| 39 | 37 | |
| 40 | // Verify the result contains the features from the C Object file. | |
| 41 | const check = lib.checkObject(); | |
| 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"); | |
| 38 | lib.expect_errors = .{ .contains = "error: object requires atomics but specified target features exclude atomics" }; | |
| 39 | _ = lib.getEmittedBin(); | |
| 59 | 40 | |
| 60 | 41 | const test_step = b.step("test", "Run linker test"); |
| 61 | test_step.dependOn(&check.step); | |
| 42 | test_step.dependOn(&lib.step); | |
| 62 | 43 | b.default_step = test_step; |
| 63 | 44 | } |
test/link/wasm/producers/build.zig-2| ... | ... | @@ -1,8 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | pub const requires_stage2 = true; | |
| 5 | ||
| 6 | 4 | pub fn build(b: *std.Build) void { |
| 7 | 5 | const test_step = b.step("test", "Test it"); |
| 8 | 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 | 6 | |
| 7 | 7 | add(b, test_step, .Debug); |
| 8 | 8 | add(b, test_step, .ReleaseFast); |
| 9 | add(b, test_step, .ReleaseSmall); | |
| 10 | add(b, test_step, .ReleaseSafe); | |
| 11 | 9 | } |
| 12 | 10 | |
| 13 | 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 | 43 | check_exe.checkInHeaders(); |
| 46 | 44 | check_exe.checkExact("Section export"); |
| 47 | 45 | check_exe.checkExact("entries 2"); |
| 46 | check_exe.checkExact("name foo"); | |
| 48 | 47 | check_exe.checkExact("name memory"); // ensure we also export memory again |
| 49 | 48 | |
| 50 | 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 | 70 | check_exe.checkExact("type function"); |
| 72 | 71 | if (optimize_mode == .Debug) { |
| 73 | 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 | 75 | check_exe.checkExact("type global"); |
| 77 | 76 | |
| 78 | 77 | // In debug mode the symbol __tls_base is resolved to an undefined symbol |
| 79 | 78 | // from the object file, hence its placement differs than in release modes |
| 80 | 79 | // where the entire tls segment is optimized away, and tls_base will have |
| 81 | 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 | 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 | 87 | check_exe.checkExact("names 1"); |
| 89 | 88 | check_exe.checkExact("index 0"); |
| 90 | 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 | 96 | test_step.dependOn(&check_exe.step); |
test/link/wasm/stack_pointer/build.zig-2| ... | ... | @@ -1,7 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test it"); |
| 7 | 5 | b.default_step = test_step; |
test/link/wasm/type/build.zig-5| ... | ... | @@ -1,15 +1,10 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test it"); |
| 7 | 5 | b.default_step = test_step; |
| 8 | 6 | |
| 9 | 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 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub const requires_stage2 = true; | |
| 4 | ||
| 5 | 3 | pub fn build(b: *std.Build) void { |
| 6 | 4 | const test_step = b.step("test", "Test the program"); |
| 7 | 5 | b.default_step = test_step; |
test/tests.zig+5| ... | ... | @@ -1375,6 +1375,7 @@ const ModuleTestOptions = struct { |
| 1375 | 1375 | skip_single_threaded: bool, |
| 1376 | 1376 | skip_non_native: bool, |
| 1377 | 1377 | skip_libc: bool, |
| 1378 | use_llvm: ?bool = null, | |
| 1378 | 1379 | max_rss: usize = 0, |
| 1379 | 1380 | no_builtin: bool = false, |
| 1380 | 1381 | build_options: ?*std.Build.Step.Options = null, |
| ... | ... | @@ -1411,6 +1412,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 1411 | 1412 | if (options.skip_single_threaded and test_target.single_threaded == true) |
| 1412 | 1413 | continue; |
| 1413 | 1414 | |
| 1415 | if (options.use_llvm) |use_llvm| { | |
| 1416 | if (test_target.use_llvm != use_llvm) continue; | |
| 1417 | } | |
| 1418 | ||
| 1414 | 1419 | // TODO get compiler-rt tests passing for self-hosted backends. |
| 1415 | 1420 | if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and |
| 1416 | 1421 | test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) |