diff --git a/CMakeLists.txt b/CMakeLists.txt index bf5af25e9a431de9e2462b353697fd70ad22030e..919f7b5fa0329edc62259bba6a5f4969f27da6fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,6 +383,7 @@ set(ZIG_STAGE2_SOURCES src/link/ConstPool.zig src/link/Coff.zig src/link/Dwarf.zig + src/link/Dwarf2.zig src/link/Elf.zig src/link/Elf/Archive.zig src/link/Elf/Atom.zig diff --git a/README.md b/README.md index 2a8fce25915d6308a6ed1d457ffca689ab82e5b5..7bbd34b69a5e6b51b5ed16e37d39678b0475151c 100644 --- a/README.md +++ b/README.md @@ -781,12 +781,20 @@ If you will be debugging the Zig compiler itself, or if you will be debugging any project compiled with Zig's LLVM backend (not recommended with the LLDB fork, prefer vanilla LLDB with a version that matches the version of LLVM that Zig is using), you can get a better debugging experience by using -[`lldb_pretty_printers.py`](https://codeberg.org/ziglang/zig/src/branch/master/tools/lldb_pretty_printers.py). +[`lldb/pretty_printers.py`](https://codeberg.org/ziglang/zig/src/branch/master/lib/lldb/pretty_printers.py) +which is included in Zig's installed lib dir. Put this line in `~/.lldbinit`: ``` -command script import /path/to/zig/tools/lldb_pretty_printers.py +command script import /path/to/zig/lib/lldb/pretty_printers.py +``` + +If you will be debugging a Zig compiler built using Zig's self-hosted backends, +you will also want this line: + +``` +type category enable zig.compiler ``` If you will be using Zig's LLVM backend (again, not recommended with the LLDB @@ -797,10 +805,9 @@ type category enable zig.lang type category enable zig.std ``` -If you will be debugging a Zig compiler built using Zig's LLVM backend (again, -not recommended with the LLDB fork), you will also want this line: +If you will be debugging a Zig compiler built using Zig's LLVM backend without +using the LLDB fork, you will also want this line: ``` -type category enable zig.stage2 +type category enable zig ``` - diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh index cc590bcdb69677eab65c21b18a3fb3fac2495c31..12beee8d9baebc901a55076e75fe9d1164f8c193 100755 --- a/ci/x86_64-linux-debug-llvm.sh +++ b/ci/x86_64-linux-debug-llvm.sh @@ -53,7 +53,7 @@ stage3-debug/bin/zig build \ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ - -Dlldb=$HOME/deps/lldb-zig/Debug-7c1090fd46/bin/lldb \ + -Dlldb=$HOME/deps/lldb-zig/Debug-aad646607a/bin/lldb \ -Dlibc-test-path=$HOME/deps/libc-test-b95fe84 \ -fqemu \ --libc-runtimes $HOME/deps/glibc-2.43-musl-1.2.5 \ diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index f46f84d21099a927542698e0a872ab6238679691..79787d9cad274c9e06fd70e4527718bf70b4a7d6 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -53,7 +53,7 @@ stage3-debug/bin/zig build \ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ - -Dlldb=$HOME/deps/lldb-zig/Debug-7c1090fd46/bin/lldb \ + -Dlldb=$HOME/deps/lldb-zig/Debug-aad646607a/bin/lldb \ -fqemu \ --libc-runtimes $HOME/deps/glibc-2.43-musl-1.2.5 \ -fwasmtime \ diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index a9671222f4099be694c9fa6d711c8ced1d6f13ad..d8ac56e1af086d1ee298e2352cda71e1504443e9 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -61,7 +61,7 @@ stage3-release/bin/zig build \ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ - -Dlldb=$HOME/deps/lldb-zig/Release-7c1090fd46/bin/lldb \ + -Dlldb=$HOME/deps/lldb-zig/Release-aad646607a/bin/lldb \ -Dlibc-test-path=$HOME/deps/libc-test-b95fe84 \ -fqemu \ --libc-runtimes $HOME/deps/glibc-2.43-musl-1.2.5 \ diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 492a6a03bea5a1b181fa71e5a656a06ca63488f6..27192cc60d4055525e5ce8aabbbcee7570464c1c 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -487,7 +487,7 @@ pub fn writableSliceGreedyPreserve(w: *Writer, preserve: usize, minimum_len: usi @branchHint(.likely); return w.buffer[w.end..]; } - try rebase(w, preserve, minimum_len); + try w.vtable.rebase(w, preserve, minimum_len); assert(w.buffer.len >= preserve + minimum_len); return w.buffer[w.end..]; } @@ -845,13 +845,10 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void { /// /// Asserts buffer capacity is at least `preserve`. pub fn writeBytePreserve(w: *Writer, preserve: usize, byte: u8) Error!void { - if (w.buffer.len - w.end != 0) { - @branchHint(.likely); - w.buffer[w.end] = byte; - w.end += 1; - return; + if (w.buffer.len - w.end == 0) { + @branchHint(.unlikely); + try w.vtable.rebase(w, preserve -| 1, 1); } - try w.vtable.rebase(w, preserve -| 1, 1); w.buffer[w.end] = byte; w.end += 1; } diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 525e9ce078882cc4fb8013762e323579a1d05126..3bbad635ca9f613f15dbfe2fa68996de39526b8f 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -387,18 +387,19 @@ fn scanAllFunctions(di: *Dwarf, gpa: Allocator, endian: Endian) ScanError!void { const next_offset = unit_header.header_length + unit_header.unit_length; const version = try fr.takeInt(u16, endian); - if (version < 2 or version > 5) return bad(); - var address_size: u8 = undefined; var debug_abbrev_offset: u64 = undefined; - if (version >= 5) { + if (version == 5) { const unit_type = try fr.takeByte(); if (unit_type != DW.UT.compile) return bad(); address_size = try fr.takeByte(); debug_abbrev_offset = try readFormatSizedInt(&fr, unit_header.format, endian); - } else { + } else if (version >= 2 and version < 5) { debug_abbrev_offset = try readFormatSizedInt(&fr, unit_header.format, endian); address_size = try fr.takeByte(); + } else { + this_unit_offset += next_offset; + continue; } const abbrev_table = try di.getAbbrevTable(gpa, debug_abbrev_offset); @@ -585,18 +586,19 @@ fn scanAllCompileUnits(di: *Dwarf, gpa: Allocator, endian: Endian) ScanError!voi const next_offset = unit_header.header_length + unit_header.unit_length; const version = try fr.takeInt(u16, endian); - if (version < 2 or version > 5) return bad(); - var address_size: u8 = undefined; var debug_abbrev_offset: u64 = undefined; - if (version >= 5) { + if (version == 5) { const unit_type = try fr.takeByte(); if (unit_type != UT.compile) return bad(); address_size = try fr.takeByte(); debug_abbrev_offset = try readFormatSizedInt(&fr, unit_header.format, endian); - } else { + } else if (version >= 2 and version < 5) { debug_abbrev_offset = try readFormatSizedInt(&fr, unit_header.format, endian); address_size = try fr.takeByte(); + } else { + this_unit_offset += next_offset; + continue; } const abbrev_table = try di.getAbbrevTable(gpa, debug_abbrev_offset); diff --git a/lib/std/debug/Dwarf/Unwind.zig b/lib/std/debug/Dwarf/Unwind.zig index d351c0421e5c4b164c2f42e27125b8670ccde649..d41c3f358885c0d3055fd1167bffa2ed80d6f877 100644 --- a/lib/std/debug/Dwarf/Unwind.zig +++ b/lib/std/debug/Dwarf/Unwind.zig @@ -362,8 +362,7 @@ pub const CommonInformationEntry = struct { if (aug_str.len == 0) break :aug .none; if (aug_str[0] == 'z') break :aug .lsb_z; if (std.mem.eql(u8, aug_str, "eh")) break :aug .gcc_eh; - // We can't finish parsing the CIE if we don't know what its augmentation means. - return bad(); + return error.UnsupportedAugmentation; }; switch (aug_kind) { @@ -396,7 +395,7 @@ pub const CommonInformationEntry = struct { 'R' => fde_pointer_enc = @bitCast(try aug_data.takeByte()), 'S' => is_signal_frame = true, 'B', 'G' => {}, - else => return bad(), + else => return error.UnsupportedAugmentation, }; break :aug .{ fde_pointer_enc, is_signal_frame }; }; @@ -502,7 +501,15 @@ pub fn prepare( const idx = unwind.cie_list.len; try unwind.cie_list.append(gpa, .{ .offset = entry_offset, - .cie = try .parse(cie_info.format, try r.take(bytes_len), section.id, addr_size_bytes), + .cie = CommonInformationEntry.parse(cie_info.format, try r.take(bytes_len), section.id, addr_size_bytes) catch |err| switch (err) { + error.UnsupportedDwarfVersion, + error.UnsupportedAugmentation, + => { + // These are recoverable by just skipping the CIE. + continue; + }, + else => |e| return e, + }, }); errdefer _ = unwind.cie_list.pop().?; try VirtualMachine.populateCieLastRow(gpa, &unwind.cie_list.items(.cie)[idx], addr_size_bytes, endian); @@ -514,8 +521,10 @@ pub fn prepare( try r.discardAll(bytes_len); continue; } - const cie = unwind.findCie(fde_info.cie_offset) orelse return error.InvalidDebugInfo; - const fde: FrameDescriptionEntry = try .parse(section.vaddr + r.seek, try r.take(bytes_len), cie, endian); + const fde_vaddr = section.vaddr + r.seek; + const fde_bytes = try r.take(bytes_len); + const cie = unwind.findCie(fde_info.cie_offset) orelse continue; + const fde: FrameDescriptionEntry = try .parse(fde_vaddr, fde_bytes, cie, endian); try fde_list.append(gpa, .{ .pc_begin = fde.pc_begin, .fde_offset = entry_offset, @@ -612,7 +621,7 @@ pub fn getFde(unwind: *const Unwind, fde_offset: u64, endian: Endian) !struct { .cie, .terminator => return bad(), // This is meant to be an FDE }; - const cie = unwind.findCie(fde_info.cie_offset) orelse return error.InvalidDebugInfo; + const cie = unwind.findCie(fde_info.cie_offset) orelse return bad(); const fde: FrameDescriptionEntry = try .parse( section.vaddr + fde_offset + fde_reader.seek, try fde_reader.take(cast(usize, fde_info.bytes_len) orelse return error.EndOfStream), diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 398a3f95a8933d489f67e3c9958084f2ed4e5660..93cee2810793e22208c86dd63b09b420f7f7f089 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -340,7 +340,6 @@ const Module = struct { error.InvalidOperation, => return error.InvalidDebugInfo, error.UnsupportedAddrSize, - error.UnsupportedDwarfVersion, error.UnimplementedUserOpcode, => return error.UnsupportedDebugInfo, }; diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index e771d7a9f5e45b3b5c217053f3394fd7b713a029..22430159cfd00e1efa57933a70af214b4b3803c3 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -578,7 +578,6 @@ const Module = struct { error.InvalidOperation, => return error.InvalidDebugInfo, error.UnsupportedAddrSize, - error.UnsupportedDwarfVersion, error.UnimplementedUserOpcode, => return error.UnsupportedDebugInfo, }; diff --git a/lib/std/dwarf/AT.zig b/lib/std/dwarf/AT.zig index d667781fb7b0994e286740eacc7beb73f1cab15b..88ccbc923bb46024706f8a9d141301ad4bb0531d 100644 --- a/lib/std/dwarf/AT.zig +++ b/lib/std/dwarf/AT.zig @@ -225,6 +225,7 @@ pub const ZIG_padding = 0x2cce; pub const ZIG_relative_decl = 0x2cd0; pub const ZIG_decl_line_relative = 0x2cd1; pub const ZIG_comptime_value = 0x2cd2; +pub const ZIG_call_line_relative = 0x2cd3; pub const ZIG_sentinel = 0x2ce2; // UPC extension. diff --git a/lib/std/dwarf/TAG.zig b/lib/std/dwarf/TAG.zig index 6838c1dd02dc9b00881850e9a9f47aab309cea37..14b5ab13f9e33ad597b12379616f894ff4b2221a 100644 --- a/lib/std/dwarf/TAG.zig +++ b/lib/std/dwarf/TAG.zig @@ -40,8 +40,8 @@ pub const namelist = 0x2b; pub const namelist_item = 0x2c; pub const packed_type = 0x2d; pub const subprogram = 0x2e; -pub const template_type_param = 0x2f; -pub const template_value_param = 0x30; +pub const template_type_parameter = 0x2f; +pub const template_value_parameter = 0x30; pub const thrown_type = 0x31; pub const try_block = 0x32; pub const variant_part = 0x33; @@ -120,3 +120,4 @@ pub const PGI_interface_block = 0xA020; // ZIG extensions. pub const ZIG_padding = 0xfdb1; pub const ZIG_comptime_value = 0xfdb2; +pub const ZIG_lost_declaration = 0xfdb3; diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index ef299a3fe7e255a9020b1578d1c684a35449f46c..3e66c8747d02b708de072d507eef08ed8c203357 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -1884,7 +1884,8 @@ fn structInitExprAnon( const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{ .abs_node = node, - .abs_line = astgen.source_line, + .src_line = astgen.source_line, + .src_column = astgen.source_column, .fields_len = @intCast(struct_init.ast.fields.len), }); const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".field_names.len; @@ -1917,7 +1918,8 @@ fn structInitExprTyped( const payload_index = try addExtra(astgen, Zir.Inst.StructInit{ .abs_node = node, - .abs_line = astgen.source_line, + .src_line = astgen.source_line, + .src_column = astgen.source_column, .fields_len = @intCast(struct_init.ast.fields.len), }); const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".field_names.len; @@ -4846,9 +4848,13 @@ fn structDeclInner( astgen.advanceSourceCursorToNode(node); const decl_inst = try gz.reserveInstructionIndex(); + const src_line = astgen.source_line; + const src_column = astgen.source_column; if (container_decl.ast.members.len == 0 and maybe_backing_int_node == .none) { try gz.setStruct(decl_inst, .{ + .src_line = src_line, + .src_column = src_column, .src_node = node, .name_strat = name_strat, .layout = layout, @@ -5003,6 +5009,8 @@ fn structDeclInner( astgen.src_hasher.final(&fields_hash); try gz.setStruct(decl_inst, .{ + .src_line = src_line, + .src_column = src_column, .src_node = node, .name_strat = name_strat, .layout = layout, @@ -5151,6 +5159,8 @@ fn unionDeclInner( astgen.advanceSourceCursorToNode(node); const decl_inst = try gz.reserveInstructionIndex(); + const src_line = astgen.source_line; + const src_column = astgen.source_column; var namespace: Scope.Namespace = .{ .parent = scope, @@ -5284,6 +5294,8 @@ fn unionDeclInner( astgen.src_hasher.final(&fields_hash); try gz.setUnion(decl_inst, .{ + .src_line = src_line, + .src_column = src_column, .src_node = node, .name_strat = name_strat, .kind = switch (layout) { @@ -5358,6 +5370,8 @@ fn containerDecl( astgen.advanceSourceCursorToNode(node); const decl_inst = try gz.reserveInstructionIndex(); + const src_line = astgen.source_line; + const src_column = astgen.source_column; var namespace: Scope.Namespace = .{ .parent = scope, @@ -5482,6 +5496,8 @@ fn containerDecl( astgen.src_hasher.final(&fields_hash); try gz.setEnum(decl_inst, .{ + .src_line = src_line, + .src_column = src_column, .src_node = node, .name_strat = name_strat, .tag_type_body_len = tag_type_body_len, @@ -5504,6 +5520,8 @@ fn containerDecl( astgen.advanceSourceCursorToNode(node); const decl_inst = try gz.reserveInstructionIndex(); + const src_line = astgen.source_line; + const src_column = astgen.source_column; var namespace: Scope.Namespace = .{ .parent = scope, @@ -5545,6 +5563,8 @@ fn containerDecl( wip_decls.finish(); try gz.setOpaque(decl_inst, .{ + .src_line = src_line, + .src_column = src_column, .src_node = node, .name_strat = name_strat, .decls_len = scan_result.decls_len, @@ -9302,6 +9322,7 @@ fn builtinCall( const field_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_attrs_ty } }, params[4], .struct_field_attrs); const result = try gz.addExtendedPayloadSmall(.reify_struct, @backingInt(reify_name_strat), Zir.Inst.ReifyStruct{ .src_line = gz.astgen.source_line, + .src_column = gz.astgen.source_column, .node = node, .layout = layout, .backing_ty = backing_ty, @@ -9330,6 +9351,7 @@ fn builtinCall( const field_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_attrs_ty } }, params[4], .union_field_attrs); const result = try gz.addExtendedPayloadSmall(.reify_union, @backingInt(reify_name_strat), Zir.Inst.ReifyUnion{ .src_line = gz.astgen.source_line, + .src_column = gz.astgen.source_column, .node = node, .layout = layout, .arg_ty = arg_ty, @@ -9352,6 +9374,7 @@ fn builtinCall( const field_values = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_values_ty } }, params[3], .enum_field_values); const result = try gz.addExtendedPayloadSmall(.reify_enum, @backingInt(reify_name_strat), Zir.Inst.ReifyEnum{ .src_line = gz.astgen.source_line, + .src_column = gz.astgen.source_column, .node = node, .tag_ty = tag_ty, .mode = mode, @@ -9365,6 +9388,7 @@ fn builtinCall( const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = spirv_type_options_ty } }, params[0], .type); const result = try gz.addExtendedPayload(.reify_spirv_type, Zir.Inst.ReifySpirvType{ .src_line = gz.astgen.source_line, + .src_column = gz.astgen.source_column, .node = node, .operand = operand, }); @@ -12392,6 +12416,8 @@ const GenZir = struct { } fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, layout: std.lang.Type.ContainerLayout, @@ -12428,7 +12454,8 @@ const GenZir = struct { .fields_hash_1 = fields_hash_arr[1], .fields_hash_2 = fields_hash_arr[2], .fields_hash_3 = fields_hash_arr[3], - .src_line = astgen.source_line, + .src_line = args.src_line, + .src_column = args.src_column, .src_node = args.src_node, }); @@ -12461,6 +12488,8 @@ const GenZir = struct { } fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, kind: Zir.Inst.UnionDecl.Kind, @@ -12495,7 +12524,8 @@ const GenZir = struct { .fields_hash_1 = fields_hash_arr[1], .fields_hash_2 = fields_hash_arr[2], .fields_hash_3 = fields_hash_arr[3], - .src_line = astgen.source_line, + .src_line = args.src_line, + .src_column = args.src_column, .src_node = args.src_node, }); @@ -12530,6 +12560,8 @@ const GenZir = struct { } fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, tag_type_body_len: ?u32, @@ -12563,7 +12595,8 @@ const GenZir = struct { .fields_hash_1 = fields_hash_arr[1], .fields_hash_2 = fields_hash_arr[2], .fields_hash_3 = fields_hash_arr[3], - .src_line = astgen.source_line, + .src_line = args.src_line, + .src_column = args.src_column, .src_node = args.src_node, }); @@ -12594,6 +12627,8 @@ const GenZir = struct { } fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, decls_len: u32, @@ -12615,7 +12650,8 @@ const GenZir = struct { args.decls.len); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{ - .src_line = astgen.source_line, + .src_line = args.src_line, + .src_column = args.src_column, .src_node = args.src_node, }); if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index d79251c7db366d256a43487c0722c6810050ab31..e506d768c93851e8da5db298649a80f199e41834 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -3277,6 +3277,7 @@ pub const Inst = struct { pub const ReifyStruct = struct { src_line: u32, + src_column: u32, /// This node is absolute, because `reify` instructions are tracked across updates, and /// this simplifies the logic for getting source locations for types. node: Ast.Node.Index, @@ -3289,6 +3290,7 @@ pub const Inst = struct { pub const ReifyUnion = struct { src_line: u32, + src_column: u32, /// This node is absolute, because `reify` instructions are tracked across updates, and /// this simplifies the logic for getting source locations for types. node: Ast.Node.Index, @@ -3301,6 +3303,7 @@ pub const Inst = struct { pub const ReifyEnum = struct { src_line: u32, + src_column: u32, /// This node is absolute, because `reify` instructions are tracked across updates, and /// this simplifies the logic for getting source locations for types. node: Ast.Node.Index, @@ -3312,6 +3315,7 @@ pub const Inst = struct { pub const ReifySpirvType = struct { src_line: u32, + src_column: u32, /// This node is absolute, because `reify` instructions are tracked across updates, and /// this simplifies the logic for getting source locations for types. node: Ast.Node.Index, @@ -3509,6 +3513,7 @@ pub const Inst = struct { fields_hash_2: u32, fields_hash_3: u32, src_line: u32, + src_column: u32, /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, @@ -3664,6 +3669,7 @@ pub const Inst = struct { fields_hash_2: u32, fields_hash_3: u32, src_line: u32, + src_column: u32, /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, @@ -3701,6 +3707,7 @@ pub const Inst = struct { fields_hash_2: u32, fields_hash_3: u32, src_line: u32, + src_column: u32, /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, @@ -3756,6 +3763,7 @@ pub const Inst = struct { /// 4. decl: Index, // for every decls_len; points to a `declaration` instruction pub const OpaqueDecl = struct { src_line: u32, + src_column: u32, /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, @@ -3803,8 +3811,8 @@ pub const Inst = struct { /// If this is an anonymous initialization (the operand is poison), this instruction becomes the owner of a type. /// To resolve source locations, we need an absolute source node. abs_node: Ast.Node.Index, - /// Likewise, we need an absolute line number. - abs_line: u32, + src_line: u32, + src_column: u32, fields_len: u32, pub const Item = struct { @@ -3823,8 +3831,8 @@ pub const Inst = struct { /// This is an anonymous initialization, meaning this instruction becomes the owner of a type. /// To resolve source locations, we need an absolute source node. abs_node: Ast.Node.Index, - /// Likewise, we need an absolute line number. - abs_line: u32, + src_line: u32, + src_column: u32, fields_len: u32, pub const Item = struct { @@ -5318,6 +5326,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe const field_bodies_overlong: []const Inst.Index = @ptrCast(zir.extra[extra_index..]); return .{ .src_line = extra.data.src_line, + .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, .captures = captures, @@ -5335,6 +5344,7 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe } pub const UnwrappedStructDecl = struct { src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, @@ -5463,6 +5473,7 @@ pub fn getUnionDecl(zir: *const Zir, union_decl: Inst.Index) UnwrappedUnionDecl const field_bodies_overlong: []const Inst.Index = @ptrCast(zir.extra[extra_index..]); return .{ .src_line = extra.data.src_line, + .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, .captures = captures, @@ -5479,6 +5490,7 @@ pub fn getUnionDecl(zir: *const Zir, union_decl: Inst.Index) UnwrappedUnionDecl } pub const UnwrappedUnionDecl = struct { src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, @@ -5590,6 +5602,7 @@ pub fn getEnumDecl(zir: *const Zir, enum_decl: Inst.Index) UnwrappedEnumDecl { const field_bodies_overlong: []const Inst.Index = @ptrCast(zir.extra[extra_index..]); return .{ .src_line = extra.data.src_line, + .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, .captures = captures, @@ -5604,6 +5617,7 @@ pub fn getEnumDecl(zir: *const Zir, enum_decl: Inst.Index) UnwrappedEnumDecl { } pub const UnwrappedEnumDecl = struct { src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, @@ -5682,6 +5696,7 @@ pub fn getOpaqueDecl(zir: *const Zir, opaque_decl: Inst.Index) UnwrappedOpaqueDe extra_index += decls_len; return .{ .src_line = extra.data.src_line, + .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, .captures = captures, @@ -5691,6 +5706,7 @@ pub fn getOpaqueDecl(zir: *const Zir, opaque_decl: Inst.Index) UnwrappedOpaqueDe } pub const UnwrappedOpaqueDecl = struct { src_line: u32, + src_column: u32, src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, captures: []const Inst.Capture, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 257d5627ba7f39866025be8352074aae97e96f65..f7a217159e34060f6e5373e73e3a0808307df518 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -3461,8 +3461,7 @@ pub const Global = struct { const old_name = self.name(builder); if (new_name == old_name) return; const index = @backingInt(self.unwrap(builder)); - _ = builder.addGlobalAssumeCapacity(new_name, builder.globals.values()[index]); - builder.globals.swapRemoveAt(index); + builder.globals.setKey(index, new_name); if (!old_name.isAnon()) return; builder.next_unnamed_global = @fromBackingInt(@backingInt(builder.next_unnamed_global) - 1); if (builder.next_unnamed_global == old_name) return; diff --git a/src/Compilation.zig b/src/Compilation.zig index a8b12da5e9fcccdcf34d1e6f42c34caf35c7c900..711b434c57d85cd9f6ae6df784f4b73a529b4b1e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2101,6 +2101,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .analysis_roots_buffer = undefined, .analysis_roots_len = 0, .codegen_task_pool = try .init(arena), + .anon_name_counter = 0, }; try zcu.init(gpa, io, options.thread_limit); break :blk zcu; @@ -2383,6 +2384,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, comp.verbose_llvm_bc != null)) { if (opt_zcu) |zcu| { + dev.check(.llvm_backend); zcu.llvm_object = try LlvmObject.create(arena, zcu); } } @@ -2829,6 +2831,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE } const is_hit = man.hit(main_progress_node) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, error.CacheCheckFailed => switch (man.diagnostic) { .none => unreachable, .manifest_create, .manifest_read, .manifest_lock => |e| return comp.setMiscFailure( @@ -2844,7 +2847,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE }); }, }, - error.OutOfMemory, error.Canceled => |e| return e, error.InvalidFormat => return comp.setMiscFailure( .check_whole_cache, "failed to check cache: invalid manifest file format", @@ -3283,8 +3285,8 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error .fuzz = comp.config.any_fuzz, .lto = comp.config.lto, }) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, error.AlreadyReported => {}, - error.OutOfMemory => |e| return e, }; if (zcu_obj_path) |path| { @@ -3293,8 +3295,8 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error // `link.Queue` has not called `prelink` because it knew we would want to send that // final link input. It is *our* responsibility to call `prelink` now we're done. comp.bin_file.?.prelink() catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, error.AlreadyReported => return, - else => |e| return e, }; } } @@ -3308,8 +3310,8 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error }; // This is needed before reading the error flags. lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, error.AlreadyReported => return, - error.OutOfMemory, error.Canceled => |e| return e, }; } } @@ -3709,8 +3711,15 @@ pub fn saveState(comp: *Compilation) !void { // linker state switch (lf.tag) { + .elf => {}, + .elf2 => { + const elf = lf.cast(.elf2).?; + try bufs.ensureUnusedCapacity(3); + addBuf(&bufs, @ptrCast(elf.mf.nodes.items)); + addBuf(&bufs, @ptrCast(&elf.mf.free_ni)); + addBuf(&bufs, @ptrCast(elf.mf.large.items)); + }, .wasm => { - dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; const is_obj = comp.config.output_mode == .Obj; try bufs.ensureUnusedCapacity(85); diff --git a/src/IncrementalDebugServer.zig b/src/IncrementalDebugServer.zig index c9302516c1fb433cc2a7bb4b11e61e3e86be9e7c..d88691372b30f5f0bfd2ce7638c07816a6661723 100644 --- a/src/IncrementalDebugServer.zig +++ b/src/IncrementalDebugServer.zig @@ -243,7 +243,7 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const var num_results: usize = 0; for (zcu.incremental_debug_state.types.keys()) |type_ip_index| { const ty: Type = .fromInterned(type_ip_index); - const ty_name = ty.containerTypeName(ip).toSlice(ip); + const ty_name = ty.containerTypeName(ip).fqn.toSlice(ip); const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { 0b00 => std.mem.find(u8, ty_name, query) != null, 0b01 => std.mem.endsWith(u8, ty_name, query), @@ -347,7 +347,7 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const \\created on generation: {d} \\ , .{ - Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip), + Type.fromInterned(ip_index).containerTypeName(ip).fqn.fmt(ip), create_gen, }); } else if (std.mem.eql(u8, cmd_str, "type_namespace")) { @@ -451,7 +451,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: *Io.Writer) Io.Writer.Error!void { .union_type, .enum_type, .opaque_type, - => try w.print("{f}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @backingInt(ty.toIntern()) }), + => try w.print("{f}[{d}]", .{ ty.containerTypeName(ip).fqn.fmt(ip), @backingInt(ty.toIntern()) }), else => unreachable, } diff --git a/src/InternPool.zig b/src/InternPool.zig index 3451106fc2d00ab4be5642d729a7c15784b0d146..30b9ab71f2d317be392537b01e1a1bc819cbb25d 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -3207,9 +3207,10 @@ pub const LoadedStructType = struct { captures: CaptureValue.Slice, is_reified: bool, - // TODO: the non-fqn will be needed by the new dwarf structure /// The name of this struct type. name: NullTerminatedString, + /// The fully-qualified name of this struct type. + fqn: NullTerminatedString, /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. /// Otherwise, or if this is a file's root struct type, this is `.none`. name_nav: Nav.Index.Optional, @@ -3390,9 +3391,10 @@ pub const LoadedUnionType = struct { captures: CaptureValue.Slice, is_reified: bool, - // TODO: the non-fqn will be needed by the new dwarf structure /// The name of this union type. name: NullTerminatedString, + /// The fully-qualified name of this union type. + fqn: NullTerminatedString, /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. /// Otherwise, this is `.none`. name_nav: Nav.Index.Optional, @@ -3457,9 +3459,10 @@ pub const LoadedEnumType = struct { owner_union: Index, is_reified: bool, - // TODO: the non-fqn will be needed by the new dwarf structure /// The name of this enum type. name: NullTerminatedString, + /// The fully-qualified name of this enum type. + fqn: NullTerminatedString, /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. /// Otherwise, this is `.none`. name_nav: Nav.Index.Optional, @@ -3519,9 +3522,10 @@ pub const LoadedOpaqueType = struct { zir_index: TrackedInst.Index, captures: CaptureValue.Slice, - // TODO: the non-fqn will be needed by the new dwarf structure /// The name of this opaque type. name: NullTerminatedString, + /// The fully-qualified name of this opaque type. + fqn: NullTerminatedString, /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. /// Otherwise, this is `.none`. name_nav: Nav.Index.Optional, @@ -3607,6 +3611,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .captures = captures, .is_reified = extra.data.flags.any_captures == .reified, .name = extra.data.name, + .fqn = extra.data.fqn, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .layout = switch (extra.data.flags.layout) { @@ -3670,6 +3675,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .captures = captures, .is_reified = extra.data.bits.captures_len == .reified, .name = extra.data.name, + .fqn = extra.data.fqn, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .layout = .@"packed", @@ -3745,6 +3751,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .captures = captures, .is_reified = extra.data.flags.any_captures == .reified, .name = extra.data.name, + .fqn = extra.data.fqn, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .layout = switch (extra.data.flags.layout) { @@ -3800,6 +3807,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .captures = captures, .is_reified = extra.data.bits.captures_len == .reified, .name = extra.data.name, + .fqn = extra.data.fqn, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .layout = .@"packed", @@ -3880,6 +3888,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .is_reified = extra.data.bits.captures_len == .reified, .owner_union = owner_union, .name = extra.data.name, + .fqn = extra.data.fqn, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .int_tag_type = extra.data.int_tag_type, @@ -3906,6 +3915,7 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType { .len = extra.data.captures_len, }, .name = extra.data.name, + .fqn = extra.data.fqn, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, }; @@ -5538,6 +5548,7 @@ pub const Tag = enum(u8) { zir_index: TrackedInst.Index, name: NullTerminatedString, + fqn: NullTerminatedString, name_nav: Nav.Index.Optional, namespace: NamespaceIndex, @@ -5580,6 +5591,7 @@ pub const Tag = enum(u8) { bits: Bits, name: NullTerminatedString, + fqn: NullTerminatedString, name_nav: Nav.Index.Optional, namespace: NamespaceIndex, @@ -5614,6 +5626,7 @@ pub const Tag = enum(u8) { zir_index: TrackedInst.Index, name: NullTerminatedString, + fqn: NullTerminatedString, name_nav: Nav.Index.Optional, namespace: NamespaceIndex, /// The enum that provides the list of field names and values. @@ -5673,6 +5686,7 @@ pub const Tag = enum(u8) { bits: Bits, name: NullTerminatedString, + fqn: NullTerminatedString, name_nav: Nav.Index.Optional, namespace: NamespaceIndex, @@ -5708,6 +5722,7 @@ pub const Tag = enum(u8) { bits: Bits, name: NullTerminatedString, + fqn: NullTerminatedString, name_nav: Nav.Index.Optional, namespace: NamespaceIndex, @@ -5735,6 +5750,7 @@ pub const Tag = enum(u8) { captures_len: u32, name: NullTerminatedString, + fqn: NullTerminatedString, name_nav: Nav.Index.Optional, namespace: NamespaceIndex, }; @@ -8063,6 +8079,7 @@ pub fn getDeclaredStructType( .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .backing_int_type = .none, @@ -8086,6 +8103,7 @@ pub fn getDeclaredStructType( .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?, .field_names = undefined, @@ -8111,6 +8129,7 @@ pub fn getDeclaredStructType( const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{ .zir_index = ini.zir_index, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .fields_len = ini.fields_len, @@ -8154,6 +8173,7 @@ pub fn getDeclaredStructType( .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?, .field_names = undefined, @@ -8207,6 +8227,7 @@ pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Pe .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .backing_int_type = ini.packed_backing_int_type, @@ -8233,6 +8254,7 @@ pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Pe .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?, .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, @@ -8260,6 +8282,7 @@ pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Pe const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{ .zir_index = ini.zir_index, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .fields_len = ini.fields_len, @@ -8305,6 +8328,7 @@ pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Pe .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?, .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, @@ -8378,6 +8402,7 @@ pub fn getDeclaredUnionType( .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .backing_int_type = .none, @@ -8397,6 +8422,7 @@ pub fn getDeclaredUnionType( .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?, .field_names = undefined, @@ -8417,6 +8443,7 @@ pub fn getDeclaredUnionType( const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{ .zir_index = ini.zir_index, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .enum_tag_type = .none, @@ -8451,6 +8478,7 @@ pub fn getDeclaredUnionType( .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?, .field_names = undefined, @@ -8501,6 +8529,7 @@ pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Per .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .backing_int_type = ini.packed_backing_int_type, @@ -8523,6 +8552,7 @@ pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Per .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?, .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, @@ -8543,6 +8573,7 @@ pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Per const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{ .zir_index = ini.zir_index, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .enum_tag_type = ini.enum_tag_type, @@ -8578,6 +8609,7 @@ pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Per .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?, .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, @@ -8654,6 +8686,7 @@ pub fn getDeclaredEnumType( .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .int_tag_type = .none, @@ -8673,6 +8706,7 @@ pub fn getDeclaredEnumType( .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?, .field_names = undefined, @@ -8729,6 +8763,7 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .int_tag_type = ini.int_tag_type, @@ -8750,6 +8785,7 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?, .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, @@ -8828,6 +8864,7 @@ pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu .want_layout = false, }, .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` .int_tag_type = .none, @@ -8849,6 +8886,7 @@ pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?, .field_names = undefined, @@ -8880,6 +8918,7 @@ pub fn getDeclaredOpaqueType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.P .zir_index = ini.zir_index, .captures_len = @intCast(ini.captures.len), .name = undefined, // set by `finish` + .fqn = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` }); @@ -8892,6 +8931,7 @@ pub fn getDeclaredOpaqueType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.P .index = gop.put(), .tid = tid, .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?, + .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "fqn").?, .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?, .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?, .field_names = undefined, @@ -8906,6 +8946,7 @@ pub const WipContainerType = struct { index: Index, tid: Zcu.PerThread.Id, type_name_index: u32, + type_fqn_index: u32, name_nav_index: u32, namespace_index: u32, @@ -8923,6 +8964,7 @@ pub const WipContainerType = struct { wip: WipContainerType, ip: *InternPool, type_name: NullTerminatedString, + type_fqn: NullTerminatedString, /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise. /// This is also `.none` if we use `.parent` because we are the root struct type for a file. name_nav: Nav.Index.Optional, @@ -8930,6 +8972,7 @@ pub const WipContainerType = struct { const extra = ip.getLocalShared(wip.tid).extra.acquire(); const extra_items = extra.view().items(.@"0"); extra_items[wip.type_name_index] = @backingInt(type_name); + extra_items[wip.type_fqn_index] = @backingInt(type_fqn); extra_items[wip.name_nav_index] = @backingInt(name_nav); } @@ -9503,6 +9546,7 @@ pub const GetFuncInstanceKey = struct { is_noinline: bool, generic_owner: Index, inferred_error_set: bool, + anon_name_counter: *u32, }; pub fn getFuncInstance( @@ -9580,6 +9624,7 @@ pub fn getFuncInstance( generic_owner, func_index, func_extra_index, + arg.anon_name_counter, ); return gop.put(); } @@ -9731,6 +9776,7 @@ fn getFuncInstanceIes( generic_owner, func_index, func_extra_index, + arg.anon_name_counter, ); func_gop.putFinal(func_index); @@ -9749,14 +9795,16 @@ fn finishFuncInstance( generic_owner: Index, func_index: Index, func_extra_index: u32, + anon_name_counter: *u32, ) Allocator.Error!void { const fn_owner_nav = ip.getNav(ip.funcDeclInfo(generic_owner).owner_nav); const fn_namespace = fn_owner_nav.analysis.?.namespace; // TODO: improve this name - const nav_name = try ip.getOrPutStringFmt(gpa, io, tid, "{f}__anon_{d}", .{ - fn_owner_nav.name.fmt(ip), @backingInt(func_index), + const nav_name = try ip.getOrPutStringFmt(gpa, io, tid, "{f}__func_{d}", .{ + fn_owner_nav.name.fmt(ip), anon_name_counter.*, }, .no_embedded_nulls); + anon_name_counter.* += 1; const nav_fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, io, tid, nav_name); const nav_index = try ip.createNav(gpa, io, tid, nav_name, nav_fqn, .{ .type = ip.typeOf(func_index), diff --git a/src/Sema.zig b/src/Sema.zig index 7eda20bf930243883aa8f87d0b454da0b74eb6c6..1090ecd135c01218c2020d740b8eada0bc5fef26 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -403,6 +403,7 @@ pub const Block = struct { /// is always incorporated into the type name somehow. /// See `Sema.setTypeName`. type_name_ctx: InternPool.NullTerminatedString, + type_fqn_ctx: InternPool.NullTerminatedString, /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block. /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`. @@ -531,6 +532,7 @@ pub const Block = struct { .need_debug_scope = parent.need_debug_scope, .src_base_inst = parent.src_base_inst, .type_name_ctx = parent.type_name_ctx, + .type_fqn_ctx = parent.type_fqn_ctx, }; } @@ -4782,7 +4784,7 @@ fn failWithBadStructFieldAccess( const msg = try sema.errMsg( field_src, "no field named '{f}' in struct '{f}'", - .{ field_name.fmt(ip), struct_type.name.fmt(ip) }, + .{ field_name.fmt(ip), struct_type.fqn.fmt(ip) }, ); errdefer msg.destroy(sema.gpa); try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{}); @@ -4808,7 +4810,7 @@ fn failWithBadUnionFieldAccess( const msg = try sema.errMsg( field_src, "no field named '{f}' in union '{f}'", - .{ field_name.fmt(ip), union_obj.name.fmt(ip) }, + .{ field_name.fmt(ip), union_obj.fqn.fmt(ip) }, ); errdefer msg.destroy(gpa); try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{}); @@ -5291,6 +5293,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro .error_return_trace_index = parent_block.error_return_trace_index, .src_base_inst = parent_block.src_base_inst, .type_name_ctx = parent_block.type_name_ctx, + .type_fqn_ctx = parent_block.type_fqn_ctx, }; defer child_block.instructions.deinit(gpa); @@ -6773,7 +6776,8 @@ fn analyzeCall( .instructions = .empty, .inlining = &generic_inlining, .src_base_inst = fn_nav.analysis.?.zir_index, - .type_name_ctx = fn_nav.fqn, + .type_name_ctx = fn_nav.name, + .type_fqn_ctx = fn_nav.fqn, } else undefined; defer if (any_generic_types) generic_block.instructions.deinit(gpa); @@ -7039,6 +7043,7 @@ fn analyzeCall( .inferred_error_set = fn_zir_info.inferred_error_set, .generic_owner = func_val.?.toIntern(), .comptime_args = comptime_args, + .anon_name_counter = &zcu.anon_name_counter, }); if (zcu.comp.debugIncremental()) { const nav = ip.indexToKey(func_instance).func.owner_nav; @@ -7308,7 +7313,8 @@ fn analyzeCall( .runtime_loop = block.runtime_loop, .runtime_index = block.runtime_index, .src_base_inst = fn_nav.analysis.?.zir_index, - .type_name_ctx = fn_nav.fqn, + .type_name_ctx = fn_nav.name, + .type_fqn_ctx = fn_nav.fqn, }; defer child_block.instructions.deinit(gpa); @@ -17314,6 +17320,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .error_return_trace_index = block.error_return_trace_index, .src_base_inst = block.src_base_inst, .type_name_ctx = block.type_name_ctx, + .type_fqn_ctx = block.type_fqn_ctx, }; defer child_block.instructions.deinit(sema.gpa); @@ -17380,6 +17387,7 @@ fn zirTypeofPeer( .runtime_index = block.runtime_index, .src_base_inst = block.src_base_inst, .type_name_ctx = block.type_name_ctx, + .type_fqn_ctx = block.type_fqn_ctx, }; defer child_block.instructions.deinit(sema.gpa); // Ignore the result, we only care about the instructions in `args`. @@ -17939,6 +17947,7 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label .comptime_reason = block.comptime_reason, .src_base_inst = block.src_base_inst, .type_name_ctx = block.type_name_ctx, + .type_fqn_ctx = block.type_fqn_ctx, }, }; sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); @@ -25934,6 +25943,7 @@ fn addSafetyCheck( .comptime_reason = null, .src_base_inst = parent_block.src_base_inst, .type_name_ctx = parent_block.type_name_ctx, + .type_fqn_ctx = parent_block.type_fqn_ctx, }; defer fail_block.instructions.deinit(gpa); @@ -26028,6 +26038,7 @@ fn addSafetyCheckUnwrapError( .comptime_reason = null, .src_base_inst = parent_block.src_base_inst, .type_name_ctx = parent_block.type_name_ctx, + .type_fqn_ctx = parent_block.type_fqn_ctx, }; defer fail_block.instructions.deinit(gpa); @@ -26151,6 +26162,7 @@ fn addSafetyCheckCall( .comptime_reason = null, .src_base_inst = parent_block.src_base_inst, .type_name_ctx = parent_block.type_name_ctx, + .type_fqn_ctx = parent_block.type_fqn_ctx, }; defer fail_block.instructions.deinit(gpa); @@ -34993,6 +35005,7 @@ pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) C .comptime_reason = null, .src_base_inst = std_type.typeDeclInst(zcu).?, .type_name_ctx = .empty, + .type_fqn_ctx = .empty, }; }; defer block.instructions.deinit(gpa); @@ -35222,11 +35235,19 @@ pub fn setTypeName( io, pt.tid, "{f}__{s}_{d}", - .{ block.type_name_ctx.fmt(ip), anon_prefix, @backingInt(wip.index) }, + .{ block.type_name_ctx.fmt(ip), anon_prefix, zcu.anon_name_counter }, + .no_embedded_nulls, + ), try ip.getOrPutStringFmt( + gpa, + io, + pt.tid, + "{f}__{s}_{d}", + .{ block.type_fqn_ctx.fmt(ip), anon_prefix, zcu.anon_name_counter }, .no_embedded_nulls, ), .none); + zcu.anon_name_counter += 1; }, - .parent => wip.setName(ip, block.type_name_ctx, sema.owner.unwrap().nav_val.toOptional()), + .parent => wip.setName(ip, block.type_name_ctx, block.type_fqn_ctx, sema.owner.unwrap().nav_val.toOptional()), .func => { const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse { return sema.failTransitive(.{ .lost_tracking = ip.funcZirBodyInst(sema.func_index) }); @@ -35236,7 +35257,7 @@ pub fn setTypeName( var aw: std.Io.Writer.Allocating = .init(gpa); defer aw.deinit(); const w = &aw.writer; - w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; + w.writeByte('(') catch return error.OutOfMemory; var arg_i: usize = 0; for (fn_info.param_body) |zir_inst| switch (zir_tags[@backingInt(zir_inst)]) { @@ -35271,8 +35292,13 @@ pub fn setTypeName( }; w.writeByte(')') catch return error.OutOfMemory; - const name = try ip.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls); - wip.setName(ip, name, .none); + wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}{s}", .{ + block.type_name_ctx.fmt(ip), + aw.written(), + }, .no_embedded_nulls), try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}{s}", .{ + block.type_fqn_ctx.fmt(ip), + aw.written(), + }, .no_embedded_nulls), .none); }, .dbg_var => { // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. @@ -35287,10 +35313,12 @@ pub fn setTypeName( } else { continue :strat .anon; }; - const name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ + wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ + // this "{f}." should be elided, but there's currently no way to get the parent function block.type_name_ctx.fmt(ip), var_name, - }, .no_embedded_nulls); - wip.setName(ip, name, .none); + }, .no_embedded_nulls), try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ + block.type_fqn_ctx.fmt(ip), var_name, + }, .no_embedded_nulls), .none); }, } } diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index 66f4aa63bb6a27fb96d24b0fcc702800b7d55bbf..164fe262037b8262cc1324319751a7e3173be4e4 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -187,7 +187,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { const tracy = trace(@src()); defer tracy.end(); - tracy.addText(struct_ty.containerTypeName(ip).toSlice(ip)); + tracy.addText(struct_ty.containerTypeName(ip).fqn.toSlice(ip)); tracy.addTextFmt("ip_index={d}", .{struct_ty.toIntern()}); assert(sema.owner.unwrap().type_layout == struct_ty.toIntern()); @@ -207,6 +207,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { .comptime_reason = undefined, // always set before using `block` .src_base_inst = struct_obj.zir_index, .type_name_ctx = struct_obj.name, + .type_fqn_ctx = struct_obj.fqn, }; defer block.instructions.deinit(gpa); @@ -613,7 +614,7 @@ pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void { const tracy = trace(@src()); defer tracy.end(); - tracy.addText(struct_ty.containerTypeName(ip).toSlice(ip)); + tracy.addText(struct_ty.containerTypeName(ip).fqn.toSlice(ip)); tracy.addTextFmt("ip_index={d}", .{struct_ty.toIntern()}); assert(sema.owner.unwrap().struct_defaults == struct_ty.toIntern()); @@ -653,6 +654,7 @@ pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void { .comptime_reason = undefined, // always set before using `block` .src_base_inst = struct_obj.zir_index, .type_name_ctx = struct_obj.name, + .type_fqn_ctx = struct_obj.fqn, }; defer block.instructions.deinit(gpa); @@ -727,7 +729,7 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { const tracy = trace(@src()); defer tracy.end(); - tracy.addText(union_ty.containerTypeName(ip).toSlice(ip)); + tracy.addText(union_ty.containerTypeName(ip).fqn.toSlice(ip)); tracy.addTextFmt("ip_index={d}", .{union_ty.toIntern()}); assert(sema.owner.unwrap().type_layout == union_ty.toIntern()); @@ -747,6 +749,7 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { .comptime_reason = undefined, // always set before using `block` .src_base_inst = union_obj.zir_index, .type_name_ctx = union_obj.name, + .type_fqn_ctx = union_obj.fqn, }; defer block.instructions.deinit(gpa); @@ -801,6 +804,13 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { "@typeInfo({f}).@\"union\".tag_type.?", .{union_obj.name.fmt(ip)}, .no_embedded_nulls, + ), try ip.getOrPutStringFmt( + gpa, + io, + pt.tid, + "@typeInfo({f}).@\"union\".tag_type.?", + .{union_obj.fqn.fmt(ip)}, + .no_embedded_nulls, ), .none); const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ .parent = union_obj.namespace.toOptional(), @@ -1221,7 +1231,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { const tracy = trace(@src()); defer tracy.end(); - tracy.addText(enum_ty.containerTypeName(ip).toSlice(ip)); + tracy.addText(enum_ty.containerTypeName(ip).fqn.toSlice(ip)); tracy.addTextFmt("ip_index={d}", .{enum_ty.toIntern()}); assert(sema.owner.unwrap().type_layout == enum_ty.toIntern()); @@ -1248,6 +1258,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { .comptime_reason = undefined, // always set before using `block` .src_base_inst = tracked_inst, .type_name_ctx = enum_obj.name, + .type_fqn_ctx = enum_obj.fqn, }; defer block.instructions.deinit(gpa); diff --git a/src/Type.zig b/src/Type.zig index f57216168a306d75648fb37ce18ad1770d35892f..b95b0df556f68de7bc40f3ef8c07ca7767ee7a11 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -109,6 +109,20 @@ pub const Class = enum(u3) { /// Then, aggregates containing fully-comptime types may themselves be either fully-comptime or /// partially-comptime; see the doc comment on `.partially_comptime` for details. fully_comptime, + + pub fn hasRuntimeBits(class: Class) bool { + return switch (class) { + .no_possible_value, .one_possible_value, .fully_comptime => false, + .runtime, .partially_comptime => true, + }; + } + + pub fn comptimeOnly(class: Class) bool { + return switch (class) { + .no_possible_value, .one_possible_value, .runtime => false, + .partially_comptime, .fully_comptime => true, + }; + } }; /// Returns the `Class` for the type `ty`. Asserts that the layout of `ty` is resolved. @@ -593,8 +607,8 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari .generic_poison => unreachable, }, .struct_type => { - const name = ip.loadStructType(ty.toIntern()).name; - try writer.print("{f}", .{name.fmt(ip)}); + const fqn = ip.loadStructType(ty.toIntern()).fqn; + try writer.print("{f}", .{fqn.fmt(ip)}); }, .tuple_type => |tuple| { if (tuple.types.len == 0) { @@ -611,16 +625,16 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari }, .union_type => { - const name = ip.loadUnionType(ty.toIntern()).name; - try writer.print("{f}", .{name.fmt(ip)}); + const fqn = ip.loadUnionType(ty.toIntern()).fqn; + try writer.print("{f}", .{fqn.fmt(ip)}); }, .opaque_type => { - const name = ip.loadOpaqueType(ty.toIntern()).name; - try writer.print("{f}", .{name.fmt(ip)}); + const fqn = ip.loadOpaqueType(ty.toIntern()).fqn; + try writer.print("{f}", .{fqn.fmt(ip)}); }, .enum_type => { - const name = ip.loadEnumType(ty.toIntern()).name; - try writer.print("{f}", .{name.fmt(ip)}); + const fqn = ip.loadEnumType(ty.toIntern()).fqn; + try writer.print("{f}", .{fqn.fmt(ip)}); }, .spirv_type => { const info = ip.loadSpirvType(ty.toIntern()); @@ -761,10 +775,7 @@ pub fn toValue(self: Type) Value { /// /// * All other types contain some runtime state, so have runtime bits and a non-zero ABI size. pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool { - return switch (ty.classify(zcu)) { - .no_possible_value, .one_possible_value, .fully_comptime => false, - .runtime, .partially_comptime => true, - }; + return ty.classify(zcu).hasRuntimeBits(); } /// Returns `true` iff the memory layout of `ty` is defined by the Zig language specification. @@ -2195,10 +2206,7 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value { pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool { if (ty.toIntern() == .generic_poison_type) return false; if (ty.zigTypeTag(zcu) == .error_union and ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) return false; - return switch (ty.classify(zcu)) { - .no_possible_value, .one_possible_value, .runtime => false, - .partially_comptime, .fully_comptime => true, - }; + return ty.classify(zcu).comptimeOnly(); } pub fn isVector(ty: Type, zcu: *const Zcu) bool { @@ -2685,8 +2693,8 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { }; const inst = zir.instructions.get(@backingInt(info.inst)); return switch (inst.tag) { - .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line, - .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_line, + .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.src_line, + .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.src_line, .extended => switch (inst.data.extended.opcode) { .struct_decl => zir.getStructDecl(info.inst).src_line, .union_decl => zir.getUnionDecl(info.inst).src_line, @@ -3012,14 +3020,29 @@ pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator return pt.ptrType(field_ptr_info); } -pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTerminatedString { - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).name, - .union_type => ip.loadUnionType(ty.toIntern()).name, - .enum_type => ip.loadEnumType(ty.toIntern()).name, - .opaque_type => ip.loadOpaqueType(ty.toIntern()).name, +pub fn containerTypeName(ty: Type, ip: *const InternPool) struct { + name: InternPool.NullTerminatedString, + fqn: InternPool.NullTerminatedString, +} { + switch (ip.indexToKey(ty.toIntern())) { + .struct_type => { + const loaded_struct = ip.loadStructType(ty.toIntern()); + return .{ .name = loaded_struct.name, .fqn = loaded_struct.fqn }; + }, + .union_type => { + const loaded_union = ip.loadUnionType(ty.toIntern()); + return .{ .name = loaded_union.name, .fqn = loaded_union.fqn }; + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(ty.toIntern()); + return .{ .name = loaded_enum.name, .fqn = loaded_enum.fqn }; + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(ty.toIntern()); + return .{ .name = loaded_opaque.name, .fqn = loaded_opaque.fqn }; + }, else => unreachable, - }; + } } pub fn destructurable(ty: Type, zcu: *const Zcu) bool { diff --git a/src/Zcu.zig b/src/Zcu.zig index 1d309e0d599476fec9b3a5780b6ca1fe5615f5b0..0e812d3faa42a1f0bb189f86f856e61149d5484d 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -348,6 +348,9 @@ codegen_task_pool: CodegenTaskPool, generation: u32 = 0, +/// Only access from the Sema thread. +anon_name_counter: u32, + pub const DependencyReason = struct { src: LazySrcLoc, /// Only populated if this is for a `.type_layout` unit. @@ -944,9 +947,9 @@ pub const Namespace = struct { tid: Zcu.PerThread.Id, name: InternPool.NullTerminatedString, ) !InternPool.NullTerminatedString { - const ns_name = Type.fromInterned(ns.owner_type).containerTypeName(ip); - if (name == .empty) return ns_name; - return ip.getOrPutStringFmt(gpa, io, tid, "{f}.{f}", .{ ns_name.fmt(ip), name.fmt(ip) }, .no_embedded_nulls); + const ns_fqn = Type.fromInterned(ns.owner_type).containerTypeName(ip).fqn; + if (name == .empty) return ns_fqn; + return ip.getOrPutStringFmt(gpa, io, tid, "{f}.{f}", .{ ns_fqn.fmt(ip), name.fmt(ip) }, .no_embedded_nulls); } }; @@ -4234,7 +4237,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana const referencer = types.values()[type_idx]; type_idx += 1; - refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}); + refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip)}); // Queue any decls within this type which would be automatically analyzed. // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`. @@ -4245,7 +4248,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana const gop = try units.getOrPut(gpa, unit); if (!gop.found_existing) { refs_log.debug("type '{f}': ref comptime %{}", .{ - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), @backingInt(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue), }); gop.value_ptr.* = referencer; @@ -4279,7 +4282,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana const gop = try units.getOrPut(gpa, .wrap(.{ .nav_val = nav_id })); if (!gop.found_existing) { refs_log.debug("type '{f}': ref test %{}", .{ - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), @backingInt(inst_info.inst), }); gop.value_ptr.* = referencer; @@ -4302,7 +4305,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana const gop = try units.getOrPut(gpa, unit); if (!gop.found_existing) { refs_log.debug("type '{f}': ref named %{}", .{ - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), @backingInt(inst_info.inst), }); gop.value_ptr.* = referencer; @@ -4319,7 +4322,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana const gop = try units.getOrPut(gpa, unit); if (!gop.found_existing) { refs_log.debug("type '{f}': ref named %{}", .{ - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), @backingInt(inst_info.inst), }); gop.value_ptr.* = referencer; @@ -4382,7 +4385,7 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana if (!gop.found_existing) { refs_log.debug("unit '{f}': ref type '{f}'", .{ zcu.fmtAnalUnit(unit), - Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip), + Type.fromInterned(ref.referenced).containerTypeName(ip).fqn.fmt(ip), }); gop.value_ptr.* = .{ .referencer = unit, @@ -4495,7 +4498,7 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void } }, .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @backingInt(nav) }), - .type_layout, .struct_defaults => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @backingInt(ty) }), + .type_layout, .struct_defaults => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), @backingInt(ty) }), .func => |func| { const nav = zcu.funcInfo(func).owner_nav; return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @backingInt(func) }); @@ -4521,8 +4524,8 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) }); }, .type_layout, .struct_defaults => |ip_index, tag| { - const name = Type.fromInterned(ip_index).containerTypeName(ip); - return writer.print("{t}('{f}')", .{ tag, name.fmt(ip) }); + const fqn = Type.fromInterned(ip_index).containerTypeName(ip).fqn; + return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) }); }, .func_ies => |ip_index| { const fqn = ip.getNav(ip.indexToKey(ip_index).func.owner_nav).fqn; @@ -4569,13 +4572,17 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) if (allowed_arch == target.cpu.arch) break; } else return .{ .bad_arch = cc.archs() }, } - const backend_ok = switch (backend) { + const backend_ok = ok: switch (backend) { .stage1 => unreachable, .other => unreachable, _ => unreachable, - .stage2_llvm => @import("codegen/llvm.zig").toLlvmCallConv(cc, target) != null, - .stage2_c => ok: { + .stage2_llvm => { + dev.check(.llvm_backend); + break :ok @import("codegen/llvm.zig").toLlvmCallConv(cc, target) != null; + }, + .stage2_c => { + dev.check(.c_backend); if (target.cCallingConvention()) |default_c| { if (cc.eql(default_c)) { break :ok true; @@ -4633,81 +4640,114 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) else => false, }; }, - .stage2_wasm => switch (cc) { - .wasm_mvp => |opts| opts.incoming_stack_alignment == null, - else => false, - }, - .stage2_arm => switch (cc) { - .arm_aapcs => |opts| opts.incoming_stack_alignment == null, - .naked => true, - else => false, - }, - .stage2_x86_64 => switch (cc) { - .x86_64_sysv, .x86_64_win, .naked => true, // incoming stack alignment supported - else => false, + .stage2_wasm => { + dev.check(.wasm_backend); + break :ok switch (cc) { + .wasm_mvp => |opts| opts.incoming_stack_alignment == null, + else => false, + }; }, - .stage2_aarch64 => switch (cc) { - .aarch64_aapcs, .aarch64_aapcs_darwin, .naked => true, - else => false, + .stage2_arm => { + dev.check(.arm_backend); + break :ok switch (cc) { + .arm_aapcs => |opts| opts.incoming_stack_alignment == null, + .naked => true, + else => false, + }; }, - .stage2_x86 => switch (cc) { - .x86_sysv, - .x86_win, - .x86_mingw, - => |opts| opts.incoming_stack_alignment == null and opts.register_params == 0, - .naked => true, - else => false, + .stage2_x86_64 => { + dev.check(.x86_64_backend); + break :ok switch (cc) { + .x86_64_sysv, .x86_64_win, .naked => true, // incoming stack alignment supported + else => false, + }; }, - .stage2_powerpc => switch (target.cpu.arch) { - .powerpc, .powerpcle => switch (cc) { - .powerpc_sysv, - .powerpc_sysv_altivec, - .powerpc_aix, - .powerpc_aix_altivec, - .naked, - => true, + .stage2_aarch64 => { + dev.check(.aarch64_backend); + break :ok switch (cc) { + .aarch64_aapcs, .aarch64_aapcs_darwin, .naked => true, else => false, - }, - .powerpc64, .powerpc64le => switch (cc) { - .powerpc64_elf, - .powerpc64_elf_altivec, - .powerpc64_elf_v2, - .naked, - => true, + }; + }, + .stage2_x86 => { + dev.check(.x86_backend); + break :ok switch (cc) { + .x86_sysv, + .x86_win, + .x86_mingw, + => |opts| opts.incoming_stack_alignment == null and opts.register_params == 0, + .naked => true, else => false, - }, - else => unreachable, + }; }, - .stage2_riscv64 => switch (cc) { - .riscv64_lp64 => |opts| opts.incoming_stack_alignment == null, - .naked => true, - else => false, + .stage2_powerpc => { + dev.check(.powerpc_backend); + break :ok switch (target.cpu.arch) { + .powerpc, .powerpcle => switch (cc) { + .powerpc_sysv, + .powerpc_sysv_altivec, + .powerpc_aix, + .powerpc_aix_altivec, + .naked, + => true, + else => false, + }, + .powerpc64, .powerpc64le => switch (cc) { + .powerpc64_elf, + .powerpc64_elf_altivec, + .powerpc64_elf_v2, + .naked, + => true, + else => false, + }, + else => unreachable, + }; }, - .stage2_sparc64 => switch (cc) { - .sparc64_sysv => |opts| opts.incoming_stack_alignment == null, - .naked => true, - else => false, + .stage2_riscv64 => { + dev.check(.riscv64_backend); + break :ok switch (cc) { + .riscv64_lp64 => |opts| opts.incoming_stack_alignment == null, + .naked => true, + else => false, + }; + }, + .stage2_sparc64 => { + dev.check(.sparc64_backend); + break :ok switch (cc) { + .sparc64_sysv => |opts| opts.incoming_stack_alignment == null, + .naked => true, + else => false, + }; }, - .stage2_spirv => switch (cc) { - .spirv_device, .spirv_kernel => true, - .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl, - .spirv_task, .spirv_mesh => target.os.tag == .vulkan, - else => false, + .stage2_spirv => { + dev.check(.spirv_backend); + break :ok switch (cc) { + .spirv_device, .spirv_kernel => true, + .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl, + .spirv_task, .spirv_mesh => target.os.tag == .vulkan, + else => false, + }; }, - .stage2_loongarch => switch (cc) { - .loongarch64_lp64, .loongarch32_ilp32, .naked => true, - else => false, + .stage2_loongarch => { + dev.check(.loongarch_backend); + break :ok switch (cc) { + .loongarch64_lp64, .loongarch32_ilp32, .naked => true, + else => false, + }; }, - .zsf_spork8 => switch (cc) { - .spork8, .naked => true, - else => false, + .zsf_spork8 => { + dev.check(.spork8_backend); + break :ok switch (cc) { + .spork8, .naked => true, + else => false, + }; }, }; if (!backend_ok) return .{ .bad_backend = backend }; return .ok; } -pub const CodegenFailError = error{ +pub const CodegenFailError = Io.Cancelable || error{ /// Indicates the error message has been already stored at `Zcu.failed_codegen`. AlreadyReported, OutOfMemory, @@ -4999,7 +5039,7 @@ fn addDependencyLoopErrorLine( }), .struct_defaults => |ty| try eb.printString( "default field values of '{f}' depend on themselves for initialization here", - .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}, + .{Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip)}, ), } else switch (dep_node.unit.unwrap()) { .@"comptime" => unreachable, // cannot be involved in a dependency loop @@ -5018,12 +5058,12 @@ fn addDependencyLoopErrorLine( }), .type_layout => |ty| try eb.printString("{f} depends on type '{f}' {s}", .{ fmt_source, - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), dep_node.reason.type_layout_reason.msg(), }), .struct_defaults => |ty| try eb.printString( "{f} uses default field values of '{f}' here", - .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fmt(ip) }, + .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip) }, ), }; @@ -5065,10 +5105,10 @@ fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer else => try w.writeAll("'std.lang' declarations"), }, .type_layout => |ty| try w.print("type '{f}'", .{ - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), }), .struct_defaults => |ty| try w.print("default field value of '{f}'", .{ - Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), }), .func => |func| try w.print("function '{f}'", .{ ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), @@ -5118,7 +5158,7 @@ pub fn populateReferenceTrace( const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) { .@"comptime" => "comptime", .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), - .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), + .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).fqn.toSlice(ip), .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), .memoized_state => null, }; @@ -5240,7 +5280,7 @@ pub const CodegenTaskPool = struct { /// memory on AIR/MIR, we see a limit of around 10 MiB of AIR in-flight. const max_air_bytes_in_flight = 10 * 1024 * 1024; - const max_funcs_in_flight = @import("link.zig").Queue.buffer_size; + const max_funcs_in_flight = link.Queue.buffer_size; available_air_bytes: u32, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 0e4962985a566aa611a326ea1aeb2b61e9928ae4..06bbdf9d3ddae622fe4aeffe118b06c8c03b942e 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -258,6 +258,8 @@ pub fn update( return; } + try comp.link_queue.enqueueZcu(comp, pt.tid, .files_ready); + if (comp.config.incremental) { const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0); defer update_zir_refs_node.end(); @@ -859,25 +861,63 @@ fn updateZirRefs(pt: Zcu.PerThread) (Io.Cancelable || Allocator.Error)!void { log.debug("tracking failed for %{d}", .{old_inst}); tracked_inst.inst = .lost; try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index }); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .lost_tracking = tracked_inst_index }); continue; }; - tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst); + tracked_inst.inst = .wrap(new_inst); const old_zir = file.prev_zir.?.*; - const new_zir = file.zir.?; const old_tag = old_zir.instructions.items(.tag)[@backingInt(old_inst)]; const old_data = old_zir.instructions.items(.data)[@backingInt(old_inst)]; - switch (old_tag) { - .declaration => { - const old_line = old_zir.getDeclaration(old_inst).src_line; - const new_line = new_zir.getDeclaration(new_inst).src_line; - if (old_line != new_line) { - comp.link_prog_node.increaseEstimatedTotalItems(1); - try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .debug_update_line_number = tracked_inst_index }); - } - }, - else => {}, + const new_zir = file.zir.?; + const new_data = new_zir.instructions.items(.data)[@backingInt(new_inst)]; + + debug_update_line_number: { + const old_line, const new_line = switch (old_tag) { + .declaration => .{ + old_zir.getDeclaration(old_inst).src_line, + new_zir.getDeclaration(new_inst).src_line, + }, + .extended => switch (old_data.extended.opcode) { + .struct_decl => .{ + old_zir.getStructDecl(old_inst).src_line, + new_zir.getStructDecl(new_inst).src_line, + }, + .union_decl => .{ + old_zir.getUnionDecl(old_inst).src_line, + new_zir.getUnionDecl(new_inst).src_line, + }, + .enum_decl => .{ + old_zir.getEnumDecl(old_inst).src_line, + new_zir.getEnumDecl(new_inst).src_line, + }, + .opaque_decl => .{ + old_zir.getOpaqueDecl(old_inst).src_line, + new_zir.getOpaqueDecl(new_inst).src_line, + }, + .reify_enum => .{ + old_zir.extraData(Zir.Inst.ReifyEnum, old_data.extended.operand).data.src_line, + new_zir.extraData(Zir.Inst.ReifyEnum, new_data.extended.operand).data.src_line, + }, + .reify_struct => .{ + old_zir.extraData(Zir.Inst.ReifyStruct, old_data.extended.operand).data.src_line, + new_zir.extraData(Zir.Inst.ReifyStruct, new_data.extended.operand).data.src_line, + }, + .reify_union => .{ + old_zir.extraData(Zir.Inst.ReifyUnion, old_data.extended.operand).data.src_line, + new_zir.extraData(Zir.Inst.ReifyUnion, new_data.extended.operand).data.src_line, + }, + else => break :debug_update_line_number, + }, + else => break :debug_update_line_number, + }; + if (old_line == new_line) break :debug_update_line_number; + comp.link_prog_node.increaseEstimatedTotalItems(1); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .debug_update_line_number = .{ + .inst = tracked_inst_index, + .line = new_line, + } }); } if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { @@ -979,7 +1019,7 @@ fn updateZirRefs(pt: Zcu.PerThread) (Io.Cancelable || Allocator.Error)!void { /// Ensures that `zcu.fileRootType` on this `file_index` is populated (not `.none`). This implies /// that the file's namespace is scanned, discovering declarations. /// -/// Typical Zig compilations begin by claling this function on the root source file of the standard +/// Typical Zig compilations begin by calling this function on the root source file of the standard /// library, `lib/std/std.zig`. The resulting namespace scan discovers a `comptime` declaration in /// that file, which is queued for analysis, and everything goes from there. pub fn ensureFilePopulated(pt: Zcu.PerThread, file_index: Zcu.File.Index) (Allocator.Error || Io.Cancelable)!void { @@ -1020,7 +1060,12 @@ pub fn ensureFilePopulated(pt: Zcu.PerThread, file_index: Zcu.File.Index) (Alloc }; errdefer wip.cancel(ip, pt.tid); - wip.setName(ip, try file.internFullyQualifiedName(pt), .none); + wip.setName( + ip, + try ip.getOrPutString(gpa, io, pt.tid, std.fs.path.stem(file.sub_file_path), .no_embedded_nulls), + try file.internFullyQualifiedName(pt), + .none, + ); const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ .parent = .none, .owner_type = wip.index, @@ -1261,6 +1306,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu // The comptime unit declares on the source of the corresponding `comptime` declaration. try sema.declareDependency(.{ .src_hash = comptime_unit.zir_index }); + const parent_ns = Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip); var block: Sema.Block = .{ .parent = null, .sema = &sema, @@ -1276,7 +1322,10 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu } }, .src_base_inst = comptime_unit.zir_index, .type_name_ctx = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.comptime", .{ - Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip).fmt(ip), + parent_ns.name.fmt(ip), + }, .no_embedded_nulls), + .type_fqn_ctx = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.comptime", .{ + parent_ns.fqn.fmt(ip), }, .no_embedded_nulls), }; defer block.instructions.deinit(gpa); @@ -1352,7 +1401,7 @@ pub fn ensureTypeLayoutUpToDate( info.deps.clearRetainingCapacity(); } - const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).toSlice(ip), null); + const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).fqn.toSlice(ip), null); defer unit_tracking.end(zcu); try zcu.analysis_in_progress.put(gpa, anal_unit, reason); @@ -1464,7 +1513,7 @@ pub fn ensureStructDefaultsUpToDate( info.deps.clearRetainingCapacity(); } - const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).toSlice(ip), null); + const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).fqn.toSlice(ip), null); defer unit_tracking.end(zcu); try zcu.analysis_in_progress.put(gpa, anal_unit, reason); @@ -1673,7 +1722,8 @@ fn analyzeNavVal( .inlining = null, .comptime_reason = undefined, // set below .src_base_inst = old_nav.analysis.?.zir_index, - .type_name_ctx = old_nav.fqn, + .type_name_ctx = old_nav.name, + .type_fqn_ctx = old_nav.fqn, }; defer block.instructions.deinit(gpa); @@ -2042,7 +2092,8 @@ fn analyzeNavType( .inlining = null, .comptime_reason = undefined, // set below .src_base_inst = old_nav.analysis.?.zir_index, - .type_name_ctx = old_nav.fqn, + .type_name_ctx = old_nav.name, + .type_fqn_ctx = old_nav.fqn, }; defer block.instructions.deinit(gpa); @@ -2983,11 +3034,11 @@ pub fn scanNamespace( const tracy_trace = trace(@src()); defer tracy_trace.end(); - tracy_trace.addText(Type.fromInterned(namespace.owner_type).containerTypeName(ip).toSlice(ip)); + tracy_trace.addText(Type.fromInterned(namespace.owner_type).containerTypeName(ip).fqn.toSlice(ip)); tracy_trace.addTextFmt("type_ip_index={d}", .{namespace.owner_type}); const tracked_unit = zcu.trackUnitSema( - Type.fromInterned(namespace.owner_type).containerTypeName(ip).toSlice(ip), + Type.fromInterned(namespace.owner_type).containerTypeName(ip).fqn.toSlice(ip), null, ); defer tracked_unit.end(zcu); @@ -3309,7 +3360,8 @@ fn analyzeFuncBodyInner( .inlining = null, .comptime_reason = null, .src_base_inst = decl_analysis.zir_index, - .type_name_ctx = func_nav.fqn, + .type_name_ctx = func_nav.name, + .type_fqn_ctx = func_nav.fqn, }; defer inner_block.instructions.deinit(gpa); @@ -4409,8 +4461,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) Ru comp.config.use_llvm, )) { else => unreachable, // assertion failure - .stage2_llvm, - => {}, + .stage2_llvm => {}, }, error.Canceled => |e| return e, } diff --git a/src/codegen.zig b/src/codegen.zig index b4efad778181ae69f35265f5e5edfe29e565921a..f1ddcab5750d0d311bcfbbefcf6cad82125b996a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -196,7 +196,7 @@ pub fn emitFunction( any_mir: *const AnyMir, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; @@ -228,7 +228,7 @@ pub fn generateLazyFunction( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const zcu = pt.zcu; const target = if (Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu)) |inst_index| &zcu.fileByIndex(inst_index.resolveFile(&zcu.intern_pool)).mod.?.resolved_target.result @@ -252,7 +252,7 @@ pub fn generateLazySymbol( w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, reloc_parent: link.File.RelocInfo.Parent, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const tracy = trace(@src()); defer tracy.end(); tracy.addTextFmt("{t}, {f}", .{ lazy_sym.kind, Type.fromInterned(lazy_sym.ty).fmt(pt) }); @@ -314,7 +314,7 @@ pub fn generateSymbol( val: Value, w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const tracy = trace(@src()); defer tracy.end(); @@ -665,7 +665,7 @@ fn lowerPtr( w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, prev_offset: u64, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const zcu = pt.zcu; const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; const offset: u64 = prev_offset + ptr.byte_offset; @@ -723,7 +723,7 @@ fn lowerUavRef( w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, offset: u64, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const comp = lf.comp; @@ -744,7 +744,6 @@ fn lowerUavRef( .c => unreachable, .spirv => unreachable, .wasm => { - dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; assert(reloc_parent == .none); try wasm.addUavReloc(w.end, uav.val, uav.orig_ty, @intCast(offset)); @@ -781,7 +780,7 @@ fn lowerNavRef( w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, offset: u64, -) (Error || std.Io.Writer.Error)!void { +) link.EmitError!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; @@ -797,7 +796,6 @@ fn lowerNavRef( .c => unreachable, .spirv => unreachable, .wasm => { - dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; assert(reloc_parent == .none); try wasm.addNavReloc(w.end, nav_index, nav_ty, @intCast(offset)); diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 3b8765e23a9f6ed943bbd73a95c812da364b2af1..81216ea24a0d5f05cecb1d1663cb4f6ff822be4a 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -896,7 +896,7 @@ pub fn finishAnalysis(isel: *Select) !void { } } -pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, AlreadyReported }!void { +pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void { const zcu = isel.pt.zcu; const ip = &zcu.intern_pool; const gpa = zcu.gpa; @@ -8024,7 +8024,7 @@ fn emitLiteral(isel: *Select, bytes: []const u8) !void { } } -fn fail(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +fn fail(isel: *Select, comptime format: []const u8, args: anytype) codegen.Error { @branchHint(.cold); return isel.pt.zcu.codegenFail(isel.nav_index, format, args); } @@ -10618,7 +10618,7 @@ pub const Value = struct { vi: Value.Index, ra: Register.Alias, - fn finish(mat: Value.Materialize, isel: *Select) error{ OutOfMemory, AlreadyReported }!void { + fn finish(mat: Value.Materialize, isel: *Select) codegen.Error!void { const live_vi = isel.live_registers.getPtr(mat.ra); assert(live_vi.* == .allocating); var vi = mat.vi; @@ -11659,7 +11659,7 @@ fn use(isel: *Select, air_ref: Air.Inst.Ref) !Value.Index { return vi; } -fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, AlreadyReported }!bool { +fn fill(isel: *Select, dst_ra: Register.Alias) codegen.Error!bool { switch (dst_ra) { else => {}, Register.Alias.fp, .zr, .sp, .pc, .fpcr, .fpsr, .ffr => return false, @@ -11692,7 +11692,7 @@ fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, AlreadyReport return true; } -fn fillMemory(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, AlreadyReported }!bool { +fn fillMemory(isel: *Select, dst_ra: Register.Alias) codegen.Error!bool { const dst_live_vi = isel.live_registers.getPtr(dst_ra); const dst_vi = switch (dst_live_vi.*) { _ => |dst_vi| dst_vi, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f4d724d4d6f766bb58818c0c33dbba915c7f2251..5b1b451d6ff1feeb8d5a4addd6e615cf6f240b99 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6,7 +6,7 @@ const log = std.log.scoped(.c); const Allocator = mem.Allocator; const Writer = std.Io.Writer; -const dev = @import("../dev.zig"); +const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const Zcu = @import("../Zcu.zig"); const Module = @import("../Module.zig"); @@ -24,7 +24,7 @@ const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { - return comptime switch (dev.env.supports(.legalize)) { + return comptime switch (@import("../dev.zig").env.supports(.legalize)) { inline false, true => |supports_legalize| &.init(.{ // we don't currently ask zig1 to use safe optimization modes .expand_bit_cast_safe = supports_legalize, @@ -86,7 +86,7 @@ pub const Mir = struct { } }; -pub const Error = Writer.Error || Allocator.Error || error{AlreadyReported}; +pub const Error = codegen.Error || Writer.Error; pub const CType = @import("c/type.zig").CType; @@ -2174,11 +2174,11 @@ pub fn genTagNameFn( } if (!zcu.comp.config.root_strip) try w.print("/* @tagName({f}) */\n", .{ - loaded_enum.name.fmt(ip), + loaded_enum.fqn.fmt(ip), }); try w.print("static {s} zig_tagName_{f}__{d}({s} tag) {{\n", .{ slice_const_u8_sentinel_0_type_name, - fmtIdentUnsolo(loaded_enum.name.toSlice(ip)), + fmtIdentUnsolo(loaded_enum.fqn.toSlice(ip)), @backingInt(enum_ty.toIntern()), enum_type_name, }); @@ -2251,7 +2251,7 @@ pub fn generate( func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, -) @import("../codegen.zig").Error!Mir { +) codegen.Error!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -6666,7 +6666,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(w, local, .other); try f.need_tag_name_funcs.put(gpa, enum_ty.toIntern(), {}); try w.print(" = zig_tagName_{f}__{d}(", .{ - fmtIdentUnsolo(enum_ty.containerTypeName(ip).toSlice(ip)), + fmtIdentUnsolo(enum_ty.containerTypeName(ip).fqn.toSlice(ip)), @backingInt(enum_ty.toIntern()), }); try f.writeCValue(w, operand, .other); diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 01b474dc9c7cfa2c995cae0f3e75b238b8447619..4b0abd42ccba9bd00cbb5804b5e8e8ed43b0f221 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1140,17 +1140,17 @@ pub const CType = union(enum) { try w.print("_{f}", .{fmtZigType(field_ty, zcu)}); } } else { - const name = ty.containerTypeName(ip).toSlice(ip); + const name = ty.containerTypeName(ip).fqn.toSlice(ip); try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)}); }, .@"opaque" => if (ty.toIntern() == .anyopaque_type) { try w.writeAll("anyopaque"); } else { - const name = ty.containerTypeName(ip).toSlice(ip); + const name = ty.containerTypeName(ip).fqn.toSlice(ip); try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)}); }, .@"union", .@"enum" => { - const name = ty.containerTypeName(ip).toSlice(ip); + const name = ty.containerTypeName(ip).fqn.toSlice(ip); try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)}); }, } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ec493f5540a0b55aa49cff29746dfd73c4859e29..186dab61293ae7ccd8d00fa17d328959c8b62cc6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -10,7 +10,6 @@ const build_options = @import("build_options"); const Air = @import("../Air.zig"); const codegen = @import("../codegen.zig"); const Compilation = @import("../Compilation.zig"); -const dev = @import("../dev.zig"); const InternPool = @import("../InternPool.zig"); const link = @import("../link.zig"); const Module = @import("../Module.zig"); @@ -155,12 +154,11 @@ pub const Object = struct { /// Values for `@llvm.used`. used: std.ArrayList(Builder.Constant), - pub const Ptr = if (dev.env.supports(.llvm_backend)) *Object else noreturn; + pub const Ptr = if (@import("../dev.zig").env.supports(.llvm_backend)) *Object else noreturn; const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); pub fn create(arena: Allocator, zcu: *Zcu) !Ptr { - dev.check(.llvm_backend); const comp = zcu.comp; const gpa = comp.gpa; const target = zcu.getTarget(); @@ -348,7 +346,7 @@ pub const Object = struct { lto: std.zig.LtoMode, }; - pub fn emit(o: *Object, pt: Zcu.PerThread, options: EmitOptions) error{ AlreadyReported, OutOfMemory }!void { + pub fn emit(o: *Object, pt: Zcu.PerThread, options: EmitOptions) link.Error!void { const zcu = o.zcu; const comp = zcu.comp; const io = comp.io; @@ -1141,7 +1139,7 @@ pub const Object = struct { } } - fn flushTypePool(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { + fn flushTypePool(o: *Object, pt: Zcu.PerThread) link.Error!void { try o.type_pool.flushPending(pt, .{ .llvm = o }); } @@ -1304,7 +1302,7 @@ pub const Object = struct { }, &o.builder); } - pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { + pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) link.Error!void { _ = o.type_map.remove(ty); try o.type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success); if (o.named_enum_map.get(ty)) |llvm_function| { @@ -1431,7 +1429,7 @@ pub const Object = struct { pub fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata { assert(!o.builder.strip); - const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); + const index = o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()) catch |err| return @errorCast(err); return o.debug_types.items[@backingInt(index)]; } @@ -2893,7 +2891,7 @@ pub const Object = struct { } } - const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).toSlice(ip))); + const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -2983,7 +2981,7 @@ pub const Object = struct { }; if (layout.tag_size == 0) { - const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).toSlice(ip))); + const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3011,7 +3009,7 @@ pub const Object = struct { llvm_fields_len += 1; } - const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).toSlice(ip))); + const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -4026,7 +4024,7 @@ pub const Object = struct { // Dummy function type; `updateEnumTagNameFunction` will replace it with the correct type. // TODO: change the builder API so we don't need to do this. try o.builder.fnType(.void, &.{}, .normal), - try o.builder.strtabStringFmt("__zig_tag_name_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), + try o.builder.strtabStringFmt("__zig_tag_name_{f}", .{enum_ty.containerTypeName(ip).fqn.fmt(ip)}), toLlvmAddressSpace(.generic, zcu.getTarget()), ); gop.value_ptr.* = llvm_function; @@ -4108,7 +4106,7 @@ pub const Object = struct { } pub fn lazyAbiAlignment(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Alignment.Lazy { - const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); + const index = o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()) catch |err| return @errorCast(err); return o.lazy_abi_aligns.items[@backingInt(index)]; } @@ -4123,7 +4121,7 @@ pub const Object = struct { // Dummy function type; `updateIsNamedEnumValue` will replace it with the correct type. // TODO: change the builder API so we don't need to do this. try o.builder.fnType(.void, &.{}, .normal), - try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), + try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_ty.containerTypeName(ip).fqn.fmt(ip)}), toLlvmAddressSpace(.generic, zcu.getTarget()), ); gop.value_ptr.* = llvm_function; diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index ba97c62105485c98a9044dd6c9a75c332c4e933e..49e9fb09702b345c28d0058f2a48b353414edebc 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -1020,7 +1020,7 @@ pub const Value = struct { /// Defines a value with a location. /// Returned location must be free-ed by caller. /// Extension unchanged. - fn def(vi: Value.Index, isel: *Select) error{ AlreadyReported, OutOfMemory }!?Location { + fn def(vi: Value.Index, isel: *Select) codegen.Error!?Location { try vi.collectDefs(isel); return vi.takeLocationMarkWritten(isel); } @@ -2046,7 +2046,7 @@ pub const Value = struct { if (!std.debug.runtime_safety) assert(@sizeOf(Mat) <= 32); } - const Error = error{ OutOfMemory, AlreadyReported }; + const Error = codegen.Error; pub fn ra(mat: Value.Mat) Register.Alias { return mat.location.register; @@ -2296,13 +2296,13 @@ pub const Value = struct { }; }; -fn fail(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +fn fail(isel: *Select, comptime format: []const u8, args: anytype) codegen.Error { @branchHint(.cold); wip_mir_log.debug("codegen error: " ++ format, args); return isel.pt.zcu.codegenFail(isel.nav_index, format, args); } -fn failUnimplemented(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported }!void { +fn failUnimplemented(isel: *Select, comptime format: []const u8, args: anytype) codegen.Error!void { @branchHint(.cold); if (debug_trap_unimplemented_code) { const gpa = isel.pt.zcu.gpa; @@ -2963,7 +2963,7 @@ pub fn verify(isel: *Select, check_values: bool) void { } } -pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, AlreadyReported }!void { +pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void { const zcu = isel.pt.zcu; const ip = &zcu.intern_pool; const gpa = zcu.gpa; @@ -5341,7 +5341,7 @@ fn forgetReg(isel: *Select, dst_reg: Register) error{ OutOfMemory, AlreadyReport /// Frees a register by moving it to another place. /// Returns true on success, false on failure (i.e. dst_reg is locked/allocated or unallocatable). -fn fillReg(isel: *Select, dst_reg: Register) error{ OutOfMemory, AlreadyReported }!bool { +fn fillReg(isel: *Select, dst_reg: Register) codegen.Error!bool { if (!isRegisterAllocatable(dst_reg)) return false; const dst_live_vi = isel.live_registers.getPtr(dst_reg); const dst_vi = switch (dst_live_vi.*) { @@ -5377,7 +5377,7 @@ fn fillReg(isel: *Select, dst_reg: Register) error{ OutOfMemory, AlreadyReported /// Frees a set of register. If locked is true, these registers are then locked. /// Requires all registers to be unlocked. /// Returns true on success. -fn fillRegsBatch(isel: *Select, regs: RegisterSet, locking: bool) error{ OutOfMemory, AlreadyReported }!void { +fn fillRegsBatch(isel: *Select, regs: RegisterSet, locking: bool) codegen.Error!void { tracking_log.debug("batch fill: {f}", .{fmtRegisterSet(regs)}); // lock free registers var regs_it = regs.iterator(); @@ -5419,7 +5419,7 @@ fn fillRegsBatch(isel: *Select, regs: RegisterSet, locking: bool) error{ OutOfMe /// Frees a register by moving it to stack. /// Returns true on success, false on failure (i.e. dst_reg is locked/allocated or unallocatable). -fn fillRegToMemory(isel: *Select, dst_reg: Register) error{ OutOfMemory, AlreadyReported }!bool { +fn fillRegToMemory(isel: *Select, dst_reg: Register) codegen.Error!bool { if (!isRegisterAllocatable(dst_reg)) return false; const dst_live_vi = isel.live_registers.getPtr(dst_reg); const dst_vi = switch (dst_live_vi.*) { diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 0b40d2fd34576e135160ca4cebd8f32d5a9fb6c5..9f971cddf23f8a15cf0abcbb17fae110c41066c2 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -856,7 +856,7 @@ pub fn generateLazy( atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.Error || std.Io.Writer.Error)!void { +) link.EmitError!void { _ = atom_index; const comp = bin_file.comp; const gpa = comp.gpa; @@ -8349,7 +8349,7 @@ fn wantSafety(func: *Func) bool { }; } -fn fail(func: *const Func, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +fn fail(func: *const Func, comptime format: []const u8, args: anytype) codegen.Error { @branchHint(.cold); const zcu = func.pt.zcu; switch (func.owner) { @@ -8359,7 +8359,7 @@ fn fail(func: *const Func, comptime format: []const u8, args: anytype) error{ Ou return error.AlreadyReported; } -fn failMsg(func: *const Func, msg: *ErrorMsg) error{ OutOfMemory, AlreadyReported } { +fn failMsg(func: *const Func, msg: *ErrorMsg) codegen.Error { @branchHint(.cold); const zcu = func.pt.zcu; switch (func.owner) { diff --git a/src/codegen/riscv64/Emit.zig b/src/codegen/riscv64/Emit.zig index f5a3f9584a5018fa88e0d2a7507d2fd73cbef645..29b57cd74427d1004b1dd179d19a6ffa50af8b73 100644 --- a/src/codegen/riscv64/Emit.zig +++ b/src/codegen/riscv64/Emit.zig @@ -13,7 +13,7 @@ prev_di_pc: usize, code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, relocs: std.ArrayList(Reloc) = .empty, -pub const Error = Lower.Error || std.Io.Writer.Error || error{ +pub const Error = Lower.Error || link.EmitError || error{ EmitFail, }; @@ -118,14 +118,14 @@ pub fn emitMir(emit: *Emit) Error!void { else => unreachable, .pseudo_dbg_prologue_end => { switch (emit.debug_output) { - .dwarf => |dw| { + inline .dwarf, .dwarf2 => |dw| { try dw.setPrologueEnd(); log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column, }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } }, .pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine( @@ -134,14 +134,14 @@ pub fn emitMir(emit: *Emit) Error!void { ), .pseudo_dbg_epilogue_begin => { switch (emit.debug_output) { - .dwarf => |dw| { + inline .dwarf, .dwarf2 => |dw| { try dw.setEpilogueBegin(); log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column, }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } }, .pseudo_dead => {}, @@ -190,15 +190,14 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void { const delta_pc: usize = emit.w.end - emit.prev_di_pc; log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); switch (emit.debug_output) { - .dwarf => |dw| { + inline .dwarf, .dwarf2 => |dw| { if (column != emit.prev_di_column) try dw.setColumn(column); - if (delta_line == 0) return; // TODO: fix these edge cases. - try dw.advancePCAndLine(delta_line, delta_pc); + try dw.advanceLineAndPc(delta_line, delta_pc, false); emit.prev_di_line = line; emit.prev_di_column = column; emit.prev_di_pc = emit.w.end; }, - .none => {}, + .eh_frame, .none => {}, } } @@ -209,6 +208,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { }; } +const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.emit); const mem = std.mem; diff --git a/src/codegen/riscv64/Mir.zig b/src/codegen/riscv64/Mir.zig index 52cadccd87bdcb7bb016dcf9a7f03ada3bd76b3a..64cd1d506e76ff057ce1f46f37edc6c0e9544dd3 100644 --- a/src/codegen/riscv64/Mir.zig +++ b/src/codegen/riscv64/Mir.zig @@ -111,7 +111,7 @@ pub fn emit( atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.Error || std.Io.Writer.Error)!void { +) link.EmitError!void { _ = atom_index; const zcu = pt.zcu; const comp = zcu.comp; diff --git a/src/codegen/sparc64/CodeGen.zig b/src/codegen/sparc64/CodeGen.zig index c8a4b9ab5f163a3dfdcd696717c62bdccb86737b..a35ceb4fcbe8e18abd11ebef7c5852bca56b6bc3 100644 --- a/src/codegen/sparc64/CodeGen.zig +++ b/src/codegen/sparc64/CodeGen.zig @@ -3450,7 +3450,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) } } -fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +fn fail(self: *Self, comptime format: []const u8, args: anytype) codegen.Error { @branchHint(.cold); const zcu = self.pt.zcu; const func = zcu.funcInfo(self.func_index); @@ -3458,7 +3458,7 @@ fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMem return zcu.codegenFailMsg(func.owner_nav, msg); } -fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, AlreadyReported } { +fn failMsg(self: *Self, msg: *ErrorMsg) codegen.Error { @branchHint(.cold); const zcu = self.pt.zcu; const func = zcu.funcInfo(self.func_index); diff --git a/src/codegen/sparc64/Emit.zig b/src/codegen/sparc64/Emit.zig index 1fd9b5c769e258c68dd3c50e994f032a159f5bcc..8545355fa30413de5db138a1da3f319ad488f48e 100644 --- a/src/codegen/sparc64/Emit.zig +++ b/src/codegen/sparc64/Emit.zig @@ -4,6 +4,7 @@ const std = @import("std"); const Endian = std.lang.Endian; const assert = std.debug.assert; +const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const Zcu = @import("../../Zcu.zig"); const ErrorMsg = Zcu.ErrorMsg; @@ -40,8 +41,7 @@ branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayList(M /// instruction code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, -const InnerError = std.Io.Writer.Error || error{ - OutOfMemory, +const InnerError = link.EmitError || error{ EmitFail, }; @@ -175,21 +175,21 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { + inline .dwarf, .dwarf2 => |dbg_out| { try dbg_out.setPrologueEnd(); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } } fn mirDebugEpilogueBegin(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { + inline .dwarf, .dwarf2 => |dbg_out| { try dbg_out.setEpilogueBegin(); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } } @@ -496,13 +496,13 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line)); const delta_pc: usize = emit.w.end - emit.prev_di_pc; switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.advancePCAndLine(delta_line, delta_pc); + inline .dwarf, .dwarf2 => |dbg_out| { + try dbg_out.advanceLineAndPc(delta_line, delta_pc, false); emit.prev_di_line = line; emit.prev_di_column = column; emit.prev_di_pc = emit.w.end; }, - else => {}, + .eh_frame, .none => {}, } } diff --git a/src/codegen/sparc64/Mir.zig b/src/codegen/sparc64/Mir.zig index 9b701e4e7654fd1fb36334e02f5bd3da7e3c9f14..ecac6f5f6746e3582195753717f745e2d8a80f4e 100644 --- a/src/codegen/sparc64/Mir.zig +++ b/src/codegen/sparc64/Mir.zig @@ -382,7 +382,7 @@ pub fn emit( atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.Error || std.Io.Writer.Error)!void { +) link.EmitError!void { _ = atom_index; const zcu = pt.zcu; const func = zcu.funcInfo(func_index); diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index db2ffcb30bcea0d4f48f912f6ba13bb991b85da8..57683c89553d73c83bc93a7145136f0730fd8f13 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -240,10 +240,7 @@ pub fn generate( }; defer cg.deinit(); - cg.genNav(true) catch |err| switch (err) { - error.AlreadyReported => return error.AlreadyReported, - error.OutOfMemory => return error.OutOfMemory, - }; + try cg.genNav(true); return cg.serializeToMir(gpa); } @@ -270,10 +267,7 @@ pub fn generateNav( }; defer cg.deinit(); - cg.genNav(false) catch |err| switch (err) { - error.AlreadyReported => return error.AlreadyReported, - error.OutOfMemory => return error.OutOfMemory, - }; + try cg.genNav(false); return cg.serializeToMir(gpa); } @@ -854,7 +848,7 @@ pub fn storageClass(cg: *const CodeGen, as: std.lang.AddressSpace) spec.StorageC }; } -const Error = error{ AlreadyReported, OutOfMemory }; +const Error = codegen.Error; pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { const gpa = cg.gpa; diff --git a/src/codegen/spork8/CodeGen.zig b/src/codegen/spork8/CodeGen.zig index f163d9347e239495f1d5217bf522a859b9c608f4..94ed016dc40b72983234b61a5889eedad9ef0088 100644 --- a/src/codegen/spork8/CodeGen.zig +++ b/src/codegen/spork8/CodeGen.zig @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const CodeGen = @This(); +const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const Spork8 = link.File.Spork8; const Zcu = @import("../../Zcu.zig"); @@ -133,37 +134,20 @@ pub fn generate( _ = bin_file; const zcu = pt.zcu; const gpa = zcu.gpa; - const cg = zcu.funcInfo(func_index); + const func = zcu.funcInfo(func_index); - var code_gen: CodeGen = .{ + var cg: CodeGen = .{ .gpa = gpa, .pt = pt, .air = air.*, .liveness = liveness.*.?, - .owner_nav = cg.owner_nav, + .owner_nav = func.owner_nav, .func_index = func_index, .mir_instructions = .empty, .mir_extra = .empty, }; - defer code_gen.deinit(); + defer cg.deinit(); - return generateInner(&code_gen) catch |err| switch (err) { - error.AlreadyReported, - error.OutOfMemory, - => |e| return e, - }; -} - -pub fn deinit(cg: *CodeGen) void { - cg.* = undefined; -} - -const InnerError = error{ - AlreadyReported, - OutOfMemory, -}; - -fn generateInner(cg: *CodeGen) InnerError!Mir { // Generate MIR for function body try cg.genBody(cg.air.getMainBody()); @@ -175,7 +159,11 @@ fn generateInner(cg: *CodeGen) InnerError!Mir { }; } -fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { +pub fn deinit(cg: *CodeGen) void { + cg.* = undefined; +} + +fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) codegen.Error!void { const zcu = cg.pt.zcu; const ip = &zcu.intern_pool; @@ -185,7 +173,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } } -fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { +fn genInst(cg: *CodeGen, inst: Air.Inst.Index) codegen.Error!void { const air_tags = cg.air.instructions.items(.tag); return switch (air_tags[@backingInt(inst)]) { .inferred_alloc, .inferred_alloc_comptime => unreachable, @@ -444,17 +432,17 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { }; } -fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { +fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) codegen.Error!void { _ = cg; _ = inst; } -fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { +fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) codegen.Error!void { _ = inst; try cg.addTag(.halt); } -fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { +fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) codegen.Error!void { const unwrapped_asm = cg.air.unwrapAsm(inst); const outputs = unwrapped_asm.outputs; // const inputs = unwrapped_asm.inputs; @@ -538,7 +526,7 @@ pub fn addTagImm8(cg: *CodeGen, tag: Mir.Inst.Tag, imm8: u8) error{OutOfMemory}! try cg.addInst(.{ .tag = tag, .data = .{ .imm8 = imm8 } }); } -fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) codegen.Error { const zcu = cg.pt.zcu; const func = zcu.funcInfo(cg.func_index); return zcu.codegenFail(func.owner_nav, fmt, args); diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index 343d089209acbb63da92b17dc8a90857a8f22e31..e3997d0b2e806cadb750dde7feac93fc05bab31c 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -332,8 +332,7 @@ const ValueTable = std.array_hash_map.Auto(Air.Inst.Ref, WValue); const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; -const InnerError = error{ - OutOfMemory, +const InnerError = Error || error{ /// An error occurred when trying to lower AIR to MIR. AlreadyReported, /// Compiler implementation could not handle a large integer. @@ -361,7 +360,7 @@ pub fn deinit(cg: *CodeGen) void { cg.* = undefined; } -pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error { const zcu = cg.pt.zcu; const func = zcu.funcInfo(cg.func_index); return zcu.codegenFail(func.owner_nav, fmt, args); @@ -760,11 +759,7 @@ fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { return .{ .local = .{ .value = initial_index, .references = 1 } }; } -pub const Error = error{ - OutOfMemory, - /// Indicates the error is already stored in Zcu `failed_codegen`. - AlreadyReported, -}; +pub const Error = codegen.Error; pub fn generate( bin_file: *link.File, diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 506a5a6a0ddfd66e30bec64676fef2ba266ec99e..e59e520c5cb4ee44ef14062bc030266a776711ae 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -1072,20 +1072,12 @@ pub fn generate( ); } - function.gen(&file.zir.?, func_zir.inst, func.comptime_args, call_info.air_arg_count) catch |err| switch (err) { + function.gen(&file.zir.?, func_zir.inst, &func, call_info.air_arg_count) catch |err| switch (err) { error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, }; - // Drop them off at the rbrace. - if (!mod.strip) _ = try function.addInst(.{ - .tag = .pseudo, - .ops = .pseudo_dbg_line_line_column, - .data = .{ .line_column = .{ - .line = func.rbrace_line, - .column = func.rbrace_column, - } }, - }); + if (!mod.strip) _ = try function.asmPseudo(.pseudo_dbg_end_none); try function.mir_extra.shrinkToLen(gpa); try function.mir_string_bytes.shrinkToLen(gpa); @@ -1120,7 +1112,7 @@ pub fn generateLazy( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.Error!void { +) link.EmitError!void { const gpa = pt.zcu.gpa; // This function is for generating global code, so we use the root module. const mod = pt.zcu.comp.root_mod; @@ -1228,18 +1220,18 @@ fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void { switch (mir_inst.ops) { else => unreachable, .pseudo_dbg_prologue_end_none, - .pseudo_dbg_epilogue_begin_none, .pseudo_dbg_enter_block_none, .pseudo_dbg_leave_block_none, + .pseudo_dbg_end_none, .pseudo_dbg_arg_none, .pseudo_dbg_var_args_none, .pseudo_dbg_var_none, .pseudo_dead_none, => {}, - .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try w.print( - " {[line]d}, {[column]d}", - mir_inst.data.line_column, - ), + .pseudo_dbg_line_stmt_line_column, + .pseudo_dbg_line_line_column, + .pseudo_dbg_epilogue_begin_line_column, + => try w.print(" {[line]d}, {[column]d}", mir_inst.data.line_column), .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try w.print(" {f}", .{ ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip), }), @@ -2069,7 +2061,7 @@ fn gen( self: *CodeGen, zir: *const std.zig.Zir, func_zir_inst: std.zig.Zir.Inst.Index, - comptime_args: InternPool.Index.Slice, + func: *const InternPool.Key.Func, air_arg_count: u32, ) InnerError!void { const pt = self.pt; @@ -2150,7 +2142,7 @@ fn gen( if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none); - try self.genMainBody(zir, func_zir_inst, comptime_args, air_arg_count); + try self.genMainBody(zir, func_zir_inst, func.comptime_args, air_arg_count); const epilogue = if (self.epilogue_relocs.items.len > 0) epilogue: { var last_inst: Mir.Inst.Index = @intCast(self.mir_instructions.len - 1); @@ -2165,7 +2157,14 @@ fn gen( } for (self.epilogue_relocs.items) |epilogue_reloc| self.performReloc(epilogue_reloc); - if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); + if (!self.mod.strip) _ = try self.addInst(.{ + .tag = .pseudo, + .ops = .pseudo_dbg_epilogue_begin_line_column, + .data = .{ .line_column = .{ + .line = func.rbrace_line, + .column = func.rbrace_column, + } }, + }); const backpatch_stack_dealloc = try self.asmPlaceholder(); const backpatch_pop_callee_preserved_regs = try self.asmPlaceholder(); try self.asmRegister(.{ ._, .pop }, .rbp); @@ -2283,11 +2282,7 @@ fn gen( .data = .{ .reg_list = frame_layout.save_reg_list }, }); } - } else { - if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none); - try self.genMainBody(zir, func_zir_inst, comptime_args, air_arg_count); - if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); - } + } else try self.genMainBody(zir, func_zir_inst, func.comptime_args, air_arg_count); } fn genMainBody( @@ -182177,7 +182172,7 @@ fn resolveCallingConventionValues( return result; } -fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { +fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) codegen.Error { @branchHint(.cold); const zcu = cg.pt.zcu; return switch (cg.owner) { diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig index 0852a6d5ce77f5d40e20149e5cac7b15d64ea3b3..7196aca6ef02eee3b07bcdf58d84822ee550877d 100644 --- a/src/codegen/x86_64/Emit.zig +++ b/src/codegen/x86_64/Emit.zig @@ -16,10 +16,8 @@ code_offset_mapping: std.ArrayList(u32), relocs: std.ArrayList(Reloc), table_relocs: std.ArrayList(TableReloc), -pub const Error = Lower.Error || error{ - AlreadyReported, +pub const Error = Lower.Error || codegen.Error || std.Io.Writer.Error || error{ EmitFail, - NotFile, } || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError; pub fn emitMir(emit: *Emit) Error!void { @@ -38,7 +36,7 @@ pub fn emitMir(emit: *Emit) Error!void { if (lowered_inst.prefix == .directive) { const start_offset: u32 = @intCast(emit.w.end); switch (emit.debug_output) { - .dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) { + inline .dwarf, .dwarf2, .eh_frame => |dwarf| switch (lowered_inst.encoding.mnemonic) { .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{ .reg = lowered_inst.ops[0].reg.dwarfNum(), .off = lowered_inst.ops[1].imm.signed, @@ -460,204 +458,222 @@ pub fn emitMir(emit: *Emit) Error!void { if (lowered.insts.len == 0) { const mir_inst = emit.lower.mir.instructions.get(mir_index); - switch (mir_inst.tag) { + assert(mir_inst.tag == .pseudo); + switch (mir_inst.ops) { else => unreachable, - .pseudo => switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_prologue_end_none => switch (emit.debug_output) { - .dwarf => |dwarf| try dwarf.setPrologueEnd(), - .none => {}, + .pseudo_dbg_prologue_end_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + try dwarf.setPrologueEnd(); + log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); }, - .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvancePCAndLine(.{ - .line = mir_inst.data.line_column.line, - .column = mir_inst.data.line_column.column, - .is_stmt = true, - }), - .pseudo_dbg_line_line_column => try emit.dbgAdvancePCAndLine(.{ - .line = mir_inst.data.line_column.line, - .column = mir_inst.data.line_column.column, - .is_stmt = false, - }), - .pseudo_dbg_epilogue_begin_none => switch (emit.debug_output) { - .dwarf => |dwarf| { + .eh_frame, .none => {}, + }, + .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvanceLineAndPc(.{ + .line = mir_inst.data.line_column.line, + .column = mir_inst.data.line_column.column, + .is_stmt = true, + }), + .pseudo_dbg_line_line_column => try emit.dbgAdvanceLineAndPc(.{ + .line = mir_inst.data.line_column.line, + .column = mir_inst.data.line_column.column, + .is_stmt = false, + }), + .pseudo_dbg_epilogue_begin_line_column => { + switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { try dwarf.setEpilogueBegin(); log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ emit.prev_di_loc.line, emit.prev_di_loc.column, }); - try emit.dbgAdvancePCAndLine(emit.prev_di_loc); }, - .none => {}, + .eh_frame, .none => {}, + } + try emit.dbgAdvanceLineAndPc(.{ + .line = mir_inst.data.line_column.line, + .column = mir_inst.data.line_column.column, + }); + }, + .pseudo_dbg_enter_block_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgEnterBlock (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.enterBlock(emit.w.end); }, - .pseudo_dbg_enter_block_none => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgEnterBlock (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.enterBlock(emit.w.end); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_leave_block_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.leaveBlock(emit.w.end); }, - .pseudo_dbg_leave_block_none => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.leaveBlock(emit.w.end); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_enter_inline_func => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgEnterInline (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column); }, - .pseudo_dbg_enter_inline_func => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgEnterInline (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column); - }, - .none => {}, - }, - .pseudo_dbg_leave_inline_func => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_leave_inline_func => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end); }, - .pseudo_dbg_arg_none, - .pseudo_dbg_arg_i_s, - .pseudo_dbg_arg_i_u, - .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_ro, - .pseudo_dbg_arg_fa, - .pseudo_dbg_arg_m, - .pseudo_dbg_var_none, - .pseudo_dbg_var_i_s, - .pseudo_dbg_var_i_u, - .pseudo_dbg_var_i_64, - .pseudo_dbg_var_ro, - .pseudo_dbg_var_fa, - .pseudo_dbg_var_m, - => switch (emit.debug_output) { - .dwarf => |dwarf| { - var loc_buf: [2]link.File.Dwarf.Loc = undefined; - const loc: link.File.Dwarf.Loc = loc: switch (mir_inst.ops) { + .eh_frame, .none => {}, + }, + .pseudo_dbg_end_none => try emit.dbgAdvanceLineAndPc(.{ + .line = emit.prev_di_loc.line, + .column = emit.prev_di_loc.column, + .end = true, + }), + .pseudo_dbg_arg_none, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_var_none, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, + => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf, tag| { + const DwarfLoc = switch (tag) { + .dwarf => link.File.Dwarf.Loc, + .dwarf2 => link.File.Dwarf2.Loc, + .eh_frame, .none => comptime unreachable, + }; + var loc_buf: [2]DwarfLoc = undefined; + const loc: DwarfLoc = loc: switch (mir_inst.ops) { + else => unreachable, + .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + => .{ .stack_value = stack_value: { + loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) { + .signed => |s| .{ .consts = s }, + .unsigned => |u| .{ .constu = u }, + }; + break :stack_value &loc_buf[0]; + } }, + .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: { + loc_buf[0] = .{ .constu = mir_inst.data.i64 }; + break :stack_value &loc_buf[0]; + } }, + .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { + const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); + break :loc .{ .plus = .{ + reg: { + loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; + break :reg &loc_buf[0]; + }, + off: { + loc_buf[1] = .{ .consts = reg_off.off }; + break :off &loc_buf[1]; + }, + } }; + }, + .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { + const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); + break :loc .{ .plus = .{ + base: { + loc_buf[0] = switch (mem.base()) { + .none => .{ .constu = 0 }, + .reg => |reg| .{ .breg = reg.dwarfNum() }, + .frame, .table, .rip_inst => unreachable, + .nav => |nav| .{ .addr_reloc = try codegen.genNavRef( + emit.bin_file, + emit.pt, + nav, + ) }, + .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav( + emit.pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), + ) }, + .lazy_sym, .extern_func => unreachable, + }; + break :base &loc_buf[0]; + }, + disp: { + loc_buf[1] = switch (mem.disp()) { + .signed => |s| .{ .consts = s }, + .unsigned => |u| .{ .constu = u }, + }; + break :disp &loc_buf[1]; + }, + } }; + }, + }; + + const local = &emit.lower.mir.locals[local_index]; + local_index += 1; + try dwarf.genLocalVarDebugInfo( + switch (mir_inst.ops) { else => unreachable, - .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, + .pseudo_dbg_arg_none, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_arg_val, + => .arg, + .pseudo_dbg_var_none, .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, - => .{ .stack_value = stack_value: { - loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) { - .signed => |s| .{ .consts = s }, - .unsigned => |u| .{ .constu = u }, - }; - break :stack_value &loc_buf[0]; - } }, - .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: { - loc_buf[0] = .{ .constu = mir_inst.data.i64 }; - break :stack_value &loc_buf[0]; - } }, - .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { - const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); - break :loc .{ .plus = .{ - reg: { - loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; - break :reg &loc_buf[0]; - }, - off: { - loc_buf[1] = .{ .consts = reg_off.off }; - break :off &loc_buf[1]; - }, - } }; - }, - .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { - const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); - break :loc .{ .plus = .{ - base: { - loc_buf[0] = switch (mem.base()) { - .none => .{ .constu = 0 }, - .reg => |reg| .{ .breg = reg.dwarfNum() }, - .frame, .table, .rip_inst => unreachable, - .nav => |nav| .{ .addr_reloc = try codegen.genNavRef( - emit.bin_file, - emit.pt, - nav, - ) }, - .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav( - emit.pt, - uav.val, - Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), - ) }, - .lazy_sym, .extern_func => unreachable, - }; - break :base &loc_buf[0]; - }, - disp: { - loc_buf[1] = switch (mem.disp()) { - .signed => |s| .{ .consts = s }, - .unsigned => |u| .{ .constu = u }, - }; - break :disp &loc_buf[1]; - }, - } }; - }, - }; - - const local = &emit.lower.mir.locals[local_index]; - local_index += 1; - try dwarf.genLocalVarDebugInfo( - switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_arg_none, - .pseudo_dbg_arg_i_s, - .pseudo_dbg_arg_i_u, - .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_ro, - .pseudo_dbg_arg_fa, - .pseudo_dbg_arg_m, - .pseudo_dbg_arg_val, - => .arg, - .pseudo_dbg_var_none, - .pseudo_dbg_var_i_s, - .pseudo_dbg_var_i_u, - .pseudo_dbg_var_i_64, - .pseudo_dbg_var_ro, - .pseudo_dbg_var_fa, - .pseudo_dbg_var_m, - .pseudo_dbg_var_val, - => .local_var, - }, - local.name.toSlice(&emit.lower.mir), - .fromInterned(local.type), - loc, - ); - }, - .none => local_index += 1, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, + .pseudo_dbg_var_val, + => .local_var, + }, + local.name.toSlice(&emit.lower.mir), + .fromInterned(local.type), + loc, + ); }, - .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { - .dwarf => |dwarf| { - const local = &emit.lower.mir.locals[local_index]; - local_index += 1; - try dwarf.genLocalConstDebugInfo( - switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_arg_val => .comptime_arg, - .pseudo_dbg_var_val => .local_const, - }, - local.name.toSlice(&emit.lower.mir), - .fromInterned(mir_inst.data.ip_index), - ); - }, - .none => local_index += 1, - }, - .pseudo_dbg_var_args_none => switch (emit.debug_output) { - .dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(), - .none => {}, + .eh_frame, .none => local_index += 1, + }, + .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + const local = &emit.lower.mir.locals[local_index]; + local_index += 1; + try dwarf.genLocalConstDebugInfo( + switch (mir_inst.ops) { + else => unreachable, + .pseudo_dbg_arg_val => .comptime_arg, + .pseudo_dbg_var_val => .local_const, + }, + local.name.toSlice(&emit.lower.mir), + .fromInterned(mir_inst.data.ip_index), + ); }, - .pseudo_dead_none => {}, + .eh_frame, .none => local_index += 1, + }, + .pseudo_dbg_var_args_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| try dwarf.genVarArgsDebugInfo(), + .eh_frame, .none => {}, }, + .pseudo_dead_none => {}, } } } @@ -971,22 +987,23 @@ const TableReloc = struct { const Loc = struct { line: u32, column: u32, - is_stmt: bool, + is_stmt: ?bool = null, + end: bool = false, }; -fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void { - const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line); - const delta_pc: usize = emit.w.end - emit.prev_di_pc; - log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); +fn dbgAdvanceLineAndPc(emit: *Emit, loc: Loc) Error!void { switch (emit.debug_output) { - .dwarf => |dwarf| { - if (loc.is_stmt != emit.prev_di_loc.is_stmt) try dwarf.negateStmt(); + inline .dwarf, .dwarf2 => |dwarf| { + const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line); + const delta_pc: usize = emit.w.end - emit.prev_di_pc; + log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); + if (loc.is_stmt) |is_stmt| if (is_stmt != emit.prev_di_loc.is_stmt) try dwarf.negateStmt(); if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column); - try dwarf.advancePCAndLine(delta_line, delta_pc); + try dwarf.advanceLineAndPc(delta_line, delta_pc, loc.end); emit.prev_di_loc = loc; emit.prev_di_pc = emit.w.end; }, - .none => {}, + .eh_frame, .none => {}, } } diff --git a/src/codegen/x86_64/Lower.zig b/src/codegen/x86_64/Lower.zig index 389d57f62d6540f7f5dc096d65c59402f72ab06b..db93e40a1dd04cf6a2076b06fda4cd64fc2fe017 100644 --- a/src/codegen/x86_64/Lower.zig +++ b/src/codegen/x86_64/Lower.zig @@ -314,11 +314,12 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_prologue_end_none, .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column, - .pseudo_dbg_epilogue_begin_none, + .pseudo_dbg_epilogue_begin_line_column, .pseudo_dbg_enter_block_none, .pseudo_dbg_leave_block_none, .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func, + .pseudo_dbg_end_none, .pseudo_dbg_arg_none, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, diff --git a/src/codegen/x86_64/Mir.zig b/src/codegen/x86_64/Mir.zig index 274437d54ccf55ce4fb47470e7a82b607c4bf0d2..763378dd4bb41acafb46c7302286f12aa8dde08b 100644 --- a/src/codegen/x86_64/Mir.zig +++ b/src/codegen/x86_64/Mir.zig @@ -1519,30 +1519,33 @@ pub const Inst = struct { /// Uses `bytes` payload. pseudo_cfi_escape_bytes, - /// End of prologue + /// End of prologue. /// Uses `none` payload. pseudo_dbg_prologue_end_none, - /// Update debug line with is_stmt register set + /// Update debug line with is_stmt register set. /// Uses `line_column` payload. pseudo_dbg_line_stmt_line_column, - /// Update debug line with is_stmt register clear + /// Update debug line with is_stmt register clear. /// Uses `line_column` payload. pseudo_dbg_line_line_column, - /// Start of epilogue - /// Uses `none` payload. - pseudo_dbg_epilogue_begin_none, - /// Start of lexical block + /// Start of epilogue. + /// Uses `line_column` payload. + pseudo_dbg_epilogue_begin_line_column, + /// Start of lexical block. /// Uses `none` payload. pseudo_dbg_enter_block_none, - /// End of lexical block + /// End of lexical block. /// Uses `none` payload. pseudo_dbg_leave_block_none, - /// Start of inline function + /// Start of inline function. /// Uses `ip_index` payload. pseudo_dbg_enter_inline_func, - /// End of inline function + /// End of inline function. /// Uses `ip_index` payload. pseudo_dbg_leave_inline_func, + /// End of function. + /// Uses `none` payload. + pseudo_dbg_end_none, /// Local argument. /// Uses `none` payload. pseudo_dbg_arg_none, @@ -1978,7 +1981,7 @@ pub fn emit( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.Error!void { +) link.EmitError!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; @@ -1986,7 +1989,7 @@ pub fn emit( const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?; const nav = func.owner_nav; const mod = zcu.navFileScope(nav).mod.?; - var e: Emit = .{ + var em: Emit = .{ .lower = .{ .target = &mod.resolved_target.result, .allocator = gpa, @@ -2006,7 +2009,8 @@ pub fn emit( .column = func.lbrace_column, .is_stmt = switch (debug_output) { .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt, - .none => undefined, + .dwarf2 => |dwarf| dwarf.wip_nav.dwarf.debug_line.header.default_is_stmt, + .eh_frame, .none => undefined, }, }, .prev_di_pc = 0, @@ -2015,11 +2019,12 @@ pub fn emit( .relocs = .empty, .table_relocs = .empty, }; - defer e.deinit(); - e.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?), + defer em.deinit(); + em.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, em.lower.err_msg.?), error.InvalidInstruction, error.CannotEncode => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), else => return zcu.codegenFail(nav, "emit MIR failed: {s}", .{@errorName(err)}), + error.AlreadyReported, error.Canceled, error.WriteFailed => |e| return e, }; } @@ -2031,12 +2036,12 @@ pub fn emitLazy( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.Error!void { +) link.EmitError!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; const mod = comp.root_mod; - var e: Emit = .{ + var em: Emit = .{ .lower = .{ .target = &mod.resolved_target.result, .allocator = gpa, @@ -2058,11 +2063,12 @@ pub fn emitLazy( .relocs = .empty, .table_relocs = .empty, }; - defer e.deinit(); - e.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, e.lower.err_msg.?), + defer em.deinit(); + em.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, em.lower.err_msg.?), error.InvalidInstruction, error.CannotEncode => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), else => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s}", .{@errorName(err)}), + error.AlreadyReported, error.Canceled, error.WriteFailed => |e| return e, }; } diff --git a/src/crash_report.zig b/src/crash_report.zig index 63370d0557d6799fe584794a84026963077b7a71..8ca3c2f836a5dbd25c81645f18f5bb88e88340ab 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -215,7 +215,6 @@ const Sema = @import("Sema.zig"); const Zcu = @import("Zcu.zig"); const link = @import("link.zig"); const InternPool = @import("InternPool.zig"); -const dev = @import("dev.zig"); const print_zir = @import("print_zir.zig"); const build_options = @import("build_options"); diff --git a/src/dev.zig b/src/dev.zig index 318a689f6db7fa0708105e252195ef6bcb0d61b7..e8b62bd420f5d6e9472252cd021db4f122971f1d 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -227,7 +227,6 @@ pub const Env = enum { else => Env.sema.supports(feature), }, .@"x86_64-windows" => switch (feature) { - .build_command, .stdio_listen, .incremental, .legalize, diff --git a/src/link.zig b/src/link.zig index 9e200d1d1c850062ccfc5e95cfa10e74fcd98411..056251dcd9431a90d35720769f71ff08f9db21ec 100644 --- a/src/link.zig +++ b/src/link.zig @@ -26,9 +26,10 @@ const target_util = @import("target.zig"); const codegen = @import("codegen.zig"); const crash_report = @import("crash_report.zig"); +pub const ConstPool = @import("link/ConstPool.zig"); pub const LdScript = @import("link/LdScript.zig"); +pub const MappedFile = @import("link/MappedFile.zig"); pub const Queue = @import("link/Queue.zig"); -pub const ConstPool = @import("link/ConstPool.zig"); pub const aarch64 = @import("link/aarch64.zig"); pub const loongarch = @import("link/loongarch.zig"); @@ -38,6 +39,7 @@ pub const Error = Allocator.Error || Io.Cancelable || error{ /// instance in `Compilation.link_diags`. AlreadyReported, }; +pub const EmitError = Error || Io.Writer.Error; pub const Diags = struct { /// Stored here so that function definitions can distinguish between @@ -95,7 +97,6 @@ pub const Diags = struct { return switch (msg.source_location) { .none => try bundle.addString(msg.msg), .wasm => |sl| { - dev.check(.wasm_linker); const wasm = base.?.cast(.wasm).?; return sl.string(msg.msg, bundle, wasm); }, @@ -394,8 +395,6 @@ pub const Diags = struct { } }; -pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version; - pub const File = struct { tag: Tag, @@ -653,7 +652,6 @@ pub const File = struct { base.file = try emit.root_dir.handle.openFile(io, emit.sub_path, .{ .mode = .read_write }); }, .elf2, .coff2 => if (base.file == null) { - dev.checkAny(&.{ .elf2_linker, .coff2_linker }); const mf = if (base.cast(.elf2)) |elf| &elf.mf else if (base.cast(.coff2)) |coff| @@ -757,6 +755,8 @@ pub const File = struct { pub const DebugInfoOutput = union(enum) { dwarf: *Dwarf.WipNav, + eh_frame: *Dwarf2.WipNav, + dwarf2: *Dwarf2.WipNav.Debug, none, }; pub const UpdateDebugInfoError = Dwarf.UpdateError; @@ -791,9 +791,31 @@ pub const File = struct { } } + /// When there is a ZCU, this is called exactly once per update, to indicate that all per-file + /// state (e.g. `Zcu.alive_files`) has been populated by the frontend, so can now be safely + /// accessed by the linker. + /// + /// This call occurs before any call to any of these functions: + /// * `updateNav` + /// * `updateFunc` + /// * `updateContainerType` + /// * `updateLineNumber` + /// + /// Asserts that the ZCU is not using the LLVM backend. + fn zcuFilesReady(base: *File, zcu: *Zcu) Error!void { + assert(zcu.llvm_object == null); + switch (base.tag) { + else => {}, + inline .elf2 => |tag| { + dev.check(tag.devFeature()); + return @as(*tag.Type(), @fieldParentPtr("base", base)).zcuFilesReady(zcu); + }, + } + } + /// Asserts that the ZCU is not using the LLVM backend. fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { - assert(base.comp.zcu.?.llvm_object == null); + assert(pt.zcu.llvm_object == null); const nav = pt.zcu.intern_pool.getNav(nav_index); assert(nav.resolved.?.value != .none); @@ -809,30 +831,17 @@ pub const File = struct { /// Never called when LLVM is codegenning the ZCU. fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Error!void { - assert(base.comp.zcu.?.llvm_object == null); + assert(pt.zcu.llvm_object == null); switch (base.tag) { .lld => unreachable, else => {}, - inline .elf, .c => |tag| { + inline .elf, .elf2, .c, .coff2 => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success); }, } } - /// Never called when LLVM is codegenning the ZCU. - fn clearContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) Error!void { - assert(base.comp.zcu.?.llvm_object == null); - switch (base.tag) { - .lld => unreachable, - else => {}, - inline .elf => |tag| { - dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).clearContainerType(pt, ty); - }, - } - } - /// The active tag of `mir` is determined by the backend used for the module this function is in. /// Never called when LLVM is codegenning the ZCU. fn updateFunc( @@ -844,7 +853,7 @@ pub const File = struct { /// take ownership of an embedded slice and replace it with `&.{}` in `mir`. mir: *codegen.AnyMir, ) Error!void { - assert(base.comp.zcu.?.llvm_object == null); + assert(pt.zcu.llvm_object == null); switch (base.tag) { .lld => unreachable, .plan9 => unreachable, @@ -858,23 +867,49 @@ pub const File = struct { /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`. /// Never called when LLVM is codegenning the ZCU. - fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) Error!void { - assert(base.comp.zcu.?.llvm_object == null); + fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) Error!void { + assert(pt.zcu.llvm_object == null); { const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; const file = pt.zcu.fileByIndex(ti.file); const inst = file.zir.?.instructions.get(@backingInt(ti.inst)); - assert(inst.tag == .declaration); + switch (inst.tag) { + .declaration => {}, + .extended => switch (inst.data.extended.opcode) { + .struct_decl, + .union_decl, + .enum_decl, + .opaque_decl, + .reify_enum, + .reify_struct, + .reify_union, + => {}, + else => unreachable, + }, + else => unreachable, + } } - switch (base.tag) { .lld => unreachable, + .plan9 => unreachable, .spirv => {}, - .plan9 => unreachable, - .elf2, .coff2 => {}, + .coff2 => {}, inline else => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id); + return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line); + }, + } + } + + fn lostTracking(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) Error!void { + assert(base.comp.zcu.?.llvm_object == null); + switch (base.tag) { + .lld => unreachable, + .plan9 => unreachable, + else => {}, + inline .elf2 => |tag| { + dev.check(tag.devFeature()); + return @as(*tag.Type(), @fieldParentPtr("base", base)).lostTracking(pt, ti_id); }, } } @@ -984,7 +1019,7 @@ pub const File = struct { pt: Zcu.PerThread, export_indices: []const Zcu.Export.Index, ) Error!void { - assert(base.comp.zcu.?.llvm_object == null); + assert(pt.zcu.llvm_object == null); crash_report.LinkerOp.start(base, pt.tid); defer crash_report.LinkerOp.stop(base, pt.tid); @@ -1019,7 +1054,7 @@ pub const File = struct { /// the block/atom. /// Never called when LLVM is codegenning the ZCU. pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 { - assert(base.comp.zcu.?.llvm_object == null); + assert(pt.zcu.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -1042,7 +1077,7 @@ pub const File = struct { decl_val: InternPool.Index, decl_align: InternPool.Alignment, ) Error!SymbolId { - assert(base.comp.zcu.?.llvm_object == null); + assert(pt.zcu.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -1308,7 +1343,7 @@ pub const File = struct { }; } - pub fn devFeature(tag: Tag) dev.Feature { + fn devFeature(tag: Tag) dev.Feature { return @field(dev.Feature, @tagName(tag) ++ "_linker"); } }; @@ -1391,6 +1426,7 @@ pub const File = struct { pub const SpirV = @import("link/SpirV.zig"); pub const Wasm = @import("link/Wasm.zig"); pub const Dwarf = @import("link/Dwarf.zig"); + pub const Dwarf2 = @import("link/Dwarf2.zig"); }; pub const PrelinkTask = union(enum) { @@ -1413,6 +1449,9 @@ pub const PrelinkTask = union(enum) { load_dso: Path, }; pub const ZcuTask = union(enum) { + /// Sent once per update, as the very first `ZcuTask` in the update. Indicates that all per-file + /// state (e.g. `Zcu.alive_files`) is populated so can now be safely accessed by the linker. + files_ready, /// Write the constant value for a Decl to the output file. link_nav: InternPool.Nav.Index, /// Write the machine code for a function to the output file. @@ -1424,7 +1463,11 @@ pub const ZcuTask = union(enum) { ty: InternPool.Index, success: bool, }, - debug_update_line_number: InternPool.TrackedInst.Index, + debug_update_line_number: struct { + inst: InternPool.TrackedInst.Index, + line: u32, + }, + lost_tracking: InternPool.TrackedInst.Index, }; pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { @@ -1616,6 +1659,16 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void var timer = comp.startTimer(); const maybe_nav: ?InternPool.Nav.Index = switch (task) { + .files_ready => { + if (zcu.llvm_object != null) return; + const lf = comp.bin_file orelse return; + lf.zcuFilesReady(zcu) catch |err| switch (err) { + error.Canceled => io.recancel(), + error.AlreadyReported => return, + error.OutOfMemory => return diags.setAllocFailure(), + }; + return; + }, .link_nav => |nav_index| nav: { const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0); @@ -1661,30 +1714,25 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void break :nav ip.indexToKey(func).func.owner_nav; }, .debug_update_container_type => |container_update| nav: { - const name = Type.fromInterned(container_update.ty).containerTypeName(ip).toSlice(ip); - const ty_prog_node = comp.link_prog_node.start(name, 0); + const fqn = Type.fromInterned(container_update.ty).containerTypeName(ip).fqn.toSlice(ip); + const ty_prog_node = comp.link_prog_node.start(fqn, 0); defer ty_prog_node.end(); - if (zcu.llvm_object) |llvm_object| { - llvm_object.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - }; - } else { - if (comp.bin_file) |lf| { - lf.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { - error.OutOfMemory => diags.setAllocFailure(), - error.Canceled => io.recancel(), - error.AlreadyReported => {}, - }; - } - } + (if (zcu.llvm_object) |llvm_object| + llvm_object.updateContainerType(pt, container_update.ty, container_update.success) + else if (comp.bin_file) |lf| + lf.updateContainerType(pt, container_update.ty, container_update.success)) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + error.Canceled => io.recancel(), + error.AlreadyReported => {}, + }; break :nav null; }, - .debug_update_line_number => |ti| nav: { + .debug_update_line_number => |line_update| nav: { const nav_prog_node = comp.link_prog_node.start("Update line number", 0); defer nav_prog_node.end(); if (pt.zcu.llvm_object == null) { if (comp.bin_file) |lf| { - lf.updateLineNumber(pt, ti) catch |err| switch (err) { + lf.updateLineNumber(pt, line_update.inst, line_update.line) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), else => |e| log.err("update line number failed: {s}", .{@errorName(e)}), }; @@ -1692,6 +1740,19 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void } break :nav null; }, + .lost_tracking => |ti| nav: { + const nav_prog_node = comp.link_prog_node.start("Lost tracking", 0); + defer nav_prog_node.end(); + if (pt.zcu.llvm_object == null) { + if (comp.bin_file) |lf| { + lf.lostTracking(pt, ti) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + else => |e| log.err("lost tracking failed: {s}", .{@errorName(e)}), + }; + } + } + break :nav null; + }, }; if (timer.finish(io)) |ns_link| report_time: { diff --git a/src/link/C.zig b/src/link/C.zig index 94a53479639a156a88365cfac5d9d14ee4dfec42..e2245881def411213663310963e39ca2519492d6 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -209,7 +209,7 @@ pub fn addConst( pt: Zcu.PerThread, pool_index: link.ConstPool.Index, val: InternPool.Index, -) Allocator.Error!void { +) link.Error!void { const zcu = pt.zcu; const gpa = zcu.comp.gpa; assert(zcu.intern_pool.typeOf(val) == .type_type); @@ -310,7 +310,7 @@ pub fn updateConst( pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index, -) Allocator.Error!void { +) link.Error!void { const zcu = pt.zcu; const gpa = zcu.comp.gpa; @@ -498,7 +498,7 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *AnyMir, -) Allocator.Error!void { +) link.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const nav = zcu.funcInfo(func_index).owner_nav; @@ -536,11 +536,7 @@ pub fn updateFunc( try c.type_pool.flushPending(pt, .{ .c = c }); } -pub fn updateNav( - c: *C, - pt: Zcu.PerThread, - nav_index: InternPool.Nav.Index, -) Allocator.Error!void { +pub fn updateNav(c: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -603,7 +599,8 @@ pub fn updateNav( const start = aw.written().len; codegen.genDeclFwd(&dg, &aw.writer) catch |err| switch (err) { error.AlreadyReported => return, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + error.WriteFailed => return error.OutOfMemory, + error.Canceled, error.OutOfMemory => |e| return e, }; break :fwd_decl .{ .start = @intCast(start), @@ -617,7 +614,8 @@ pub fn updateNav( const start = aw.written().len; codegen.genDecl(&dg, &aw.writer) catch |err| switch (err) { error.AlreadyReported => return, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + error.WriteFailed => return error.OutOfMemory, + error.Canceled, error.OutOfMemory => |e| return e, }; break :code .{ .start = @intCast(start), @@ -655,7 +653,7 @@ fn updateUav( pt: Zcu.PerThread, val: Value, rendered_decl: *RenderedDecl, -) Allocator.Error!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -691,7 +689,8 @@ fn updateUav( .init_val = val, }) catch |err| switch (err) { error.AlreadyReported => return, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + error.WriteFailed => return error.OutOfMemory, + error.Canceled, error.OutOfMemory => |e| return e, }; break :fwd_decl .{ .start = @intCast(start), @@ -710,7 +709,8 @@ fn updateUav( .init_val = val, }) catch |err| switch (err) { error.AlreadyReported => return, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + error.WriteFailed => return error.OutOfMemory, + error.Canceled, error.OutOfMemory => |e| return e, }; break :code .{ .start = @intCast(start), @@ -721,12 +721,13 @@ fn updateUav( rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps); } -pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) error{}!void { +pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) error{}!void { // The C backend does not currently emit "#line" directives. Even if it did, it would not be // capable of updating those line numbers without re-generating the entire declaration. _ = c; _ = pt; _ = ti_id; + _ = line; } pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.Error!void { @@ -1144,14 +1145,14 @@ pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Prog for (need_never_tail_funcs.keys()) |fn_nav| { codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_tail, &lazy_decls_aw.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, - error.OutOfMemory => |e| return e, + error.Canceled, error.OutOfMemory => |e| return e, error.AlreadyReported => unreachable, }; } for (need_never_inline_funcs.keys()) |fn_nav| { codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_inline, &lazy_decls_aw.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, - error.OutOfMemory => |e| return e, + error.Canceled, error.OutOfMemory => |e| return e, error.AlreadyReported => unreachable, }; } @@ -1343,7 +1344,7 @@ fn addCTypeDependencies( c: *C, pt: Zcu.PerThread, deps: *const codegen.CType.Dependencies, -) Allocator.Error!CTypeDependencies { +) link.Error!CTypeDependencies { const gpa = pt.zcu.comp.gpa; try c.bigint_types.ensureUnusedCapacity(gpa, deps.bigint.count()); @@ -1399,7 +1400,7 @@ fn addCTypeDependencies( }; } -fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) Allocator.Error!void { +fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) link.Error!void { const gpa = pt.zcu.comp.gpa; var index = old_uavs_len; while (index < c.uavs.count()) : (index += 1) { diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 91ee0ab5bda97079f456ac74bf68c3d9d4868493..1a553d8dc8a2f3ee69a6720ecb2439c974e317c1 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -13,7 +13,7 @@ const codegen = @import("../codegen.zig"); const Compilation = @import("../Compilation.zig"); const InternPool = @import("../InternPool.zig"); const link = @import("../link.zig"); -const MappedFile = @import("MappedFile.zig"); +const MappedFile = link.MappedFile; const target_util = @import("../target.zig"); const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); @@ -536,7 +536,7 @@ pub const Member = struct { const new_size = Alignment.@"4".forward(old_size + name.len + 1); assert(new_size < comptime try std.math.powi(u64, 10, max_name_len - 1)); - try Node.known.longnames_member.resizeLeaf(&coff.mf, gpa, new_size); + try Node.known.longnames_member.resizeLeaf(gpa, &coff.mf, new_size); const name_table_slice = Node.known.longnames_member.slice(&coff.mf); const name_slice = name_table_slice[@intCast(old_size)..][0 .. name.len + 1]; @memcpy(name_slice[0..name.len], name); @@ -1666,7 +1666,7 @@ fn create( .global_pending_index = 0, .navs = .empty, .uavs = .empty, - .lazy = .initFill(.{ + .lazy = comptime .initFill(.{ .map = .empty, .pending_index = 0, }), @@ -1840,13 +1840,13 @@ fn initHeaders( coff.nodes.appendAssumeCapacity(.file); const header_ni = Node.known.header; - assert(header_ni == try Node.known.file.addOnlyHeaderChild(&coff.mf, gpa, .{ + assert(header_ni == try Node.known.file.addOnlyHeaderChild(gpa, &coff.mf, .{ .alignment = coff.mf.flags.block_size, })); coff.nodes.appendAssumeCapacity(.header); const coff_parent_ni: MappedFile.Node.Index = if (is_archive) parent: { - assert(try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .wrap(header_ni), .{ + assert(try Node.known.file.addHeaderChildAfter(gpa, &coff.mf, .wrap(header_ni), .{ .size = std.coff.archive_signature.len, .alignment = .@"4", }) == Node.known.signature); @@ -1879,7 +1879,7 @@ fn initHeaders( const zcu_member = zcu_mi.get(coff); try zcu_member.initHeader(coff, zcu.main_mod.fully_qualified_name, timestamp); - assert(try zcu_member.content_ni.addOnlyHeaderChild(&coff.mf, gpa, .{ + assert(try zcu_member.content_ni.addOnlyHeaderChild(gpa, &coff.mf, .{ .size = @sizeOf(std.coff.Header), .alignment = .@"4", }) == Node.known.coff_header); @@ -1894,13 +1894,13 @@ fn initHeaders( // no other members then the last linker member (longnames) needs to expand // to fill the padding at the end of the file. while (coff.nodes.len < Node.known_count) { - _ = try Node.known.header.addHeaderChildAfter(&coff.mf, gpa, .none, .{}); + _ = try Node.known.header.addHeaderChildAfter(gpa, &coff.mf, .none, .{}); coff.nodes.appendAssumeCapacity(.placeholder); } return; } else parent: { - assert(try header_ni.addOnlyHeaderChild(&coff.mf, gpa, .{ + assert(try header_ni.addOnlyHeaderChild(gpa, &coff.mf, .{ .size = if (is_image) msdos_stub.len + std.coff.pe_signature.len else 0, .alignment = .@"4", }) == Node.known.signature); @@ -1913,12 +1913,12 @@ fn initHeaders( // TODO: Not ideal to have this many placeholder nodes - use two distinct `Node.known` types? while (true) { - const placeholder_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .none, .{}); + const placeholder_ni = try Node.known.file.addHeaderChildAfter(gpa, &coff.mf, .none, .{}); coff.nodes.appendAssumeCapacity(.placeholder); if (placeholder_ni == Node.known.zcu_member) break; } - assert(try header_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(Node.known.signature), .{ + assert(try header_ni.addHeaderChildAfter(gpa, &coff.mf, .wrap(Node.known.signature), .{ .size = @sizeOf(std.coff.Header), .alignment = .@"4", }) == Node.known.coff_header); @@ -1949,7 +1949,7 @@ fn initHeaders( } const optional_header_ni = Node.known.optional_header; - assert(optional_header_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(Node.known.coff_header), .{ + assert(optional_header_ni == try coff_parent_ni.addHeaderChildAfter(gpa, &coff.mf, .wrap(Node.known.coff_header), .{ .size = optional_header_size, .alignment = .@"4", })); @@ -2060,7 +2060,7 @@ fn initHeaders( } const data_directories_ni = Node.known.data_directories; - assert(data_directories_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(optional_header_ni), .{ + assert(data_directories_ni == try coff_parent_ni.addHeaderChildAfter(gpa, &coff.mf, .wrap(optional_header_ni), .{ .size = data_directories_size, .alignment = .@"4", })); @@ -2075,7 +2075,7 @@ fn initHeaders( } const section_table_ni = Node.known.section_table; - assert(section_table_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(data_directories_ni), .{ + assert(section_table_ni == try coff_parent_ni.addHeaderChildAfter(gpa, &coff.mf, .wrap(data_directories_ni), .{ .alignment = .@"4", })); coff.nodes.appendAssumeCapacity(.section_table); @@ -2084,13 +2084,13 @@ fn initHeaders( if (!is_image) { // TODO: These two nodes could be inside one movable node? - coff.symbol_table.ni = try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(section_table_ni), .{ + coff.symbol_table.ni = try coff_parent_ni.addHeaderChildAfter(gpa, &coff.mf, .wrap(section_table_ni), .{ .alignment = .@"2", .moved = true, }); coff.nodes.appendAssumeCapacity(.symbol_table); - coff.symbol_table.strings_ni = try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(coff.symbol_table.ni), .{ + coff.symbol_table.strings_ni = try coff_parent_ni.addHeaderChildAfter(gpa, &coff.mf, .wrap(coff.symbol_table.ni), .{ .size = @sizeOf(u32), .resized = true, }); @@ -2143,7 +2143,7 @@ fn initHeaders( coff.mf.flags.block_size, .{ .read = true, .initialized = true }, )).symbol(coff).node(coff); - coff.import_table.ni = try import_table_parent_ni.addFloatingChild(&coff.mf, gpa, .{ + coff.import_table.ni = try import_table_parent_ni.addFloatingChild(gpa, &coff.mf, .{ .alignment = .@"4", }); coff.nodes.appendAssumeCapacity(.import_directory_table); @@ -2154,7 +2154,7 @@ fn initHeaders( .{ .read = true, .initialized = true }, )).symbol(coff).node(coff); - coff.export_table.export_directory_table_ni = try coff.export_table.ni.addHeaderChildAfter(&coff.mf, gpa, coff.export_table.ni.last(&coff.mf), .{ + coff.export_table.export_directory_table_ni = try coff.export_table.ni.addHeaderChildAfter(gpa, &coff.mf, coff.export_table.ni.last(&coff.mf), .{ .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1, .moved = true, }); @@ -2165,7 +2165,7 @@ fn initHeaders( @memcpy(table_slice[name_index..][0..file_name.len], file_name[0..file_name.len]); @memset(table_slice[name_index + file_name.len ..], 0); - const export_address_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{ + const export_address_table_ni = try coff.export_table.ni.addFloatingChild(gpa, &coff.mf, .{ .alignment = .of(std.coff.ExportAddressTableEntry), .moved = true, }); @@ -2181,19 +2181,19 @@ fn initHeaders( export_address_table_sym.section_number = coff.getNode(coff.export_table.ni).pseudo_section.symbol(coff).get(coff).section_number; - coff.export_table.name_pointer_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{ + coff.export_table.name_pointer_table_ni = try coff.export_table.ni.addFloatingChild(gpa, &coff.mf, .{ .alignment = .of(std.coff.ExportNamePointerTableEntry), .moved = true, }); coff.nodes.appendAssumeCapacity(.export_name_pointer_table); - coff.export_table.ordinal_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{ + coff.export_table.ordinal_table_ni = try coff.export_table.ni.addFloatingChild(gpa, &coff.mf, .{ .alignment = .of(std.coff.ExportOrdinalTableEntry), .moved = true, }); coff.nodes.appendAssumeCapacity(.export_ordinal_table); - coff.export_table.name_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{ + coff.export_table.name_table_ni = try coff.export_table.ni.addFloatingChild(gpa, &coff.mf, .{ .alignment = .of(u8), .moved = true, }); @@ -2286,7 +2286,7 @@ pub fn initBuiltins(coff: *Coff) !void { const list_len_si = try coff.globalSymbol(.{ .name = list.global, .type = .data }); const list_len_sym = list_len_si.get(coff); list_len_sym.setExtra(.{ .size = addr_info.size }); - list_len_sym.ni = .wrap(try start_sym.ni.unwrap().?.addHeaderChildAfter(&coff.mf, gpa, .none, .{ + list_len_sym.ni = .wrap(try start_sym.ni.unwrap().?.addHeaderChildAfter(gpa, &coff.mf, .none, .{ .size = addr_info.size, })); coff.nodes.appendAssumeCapacity(.{ .builtin = list_len_si }); @@ -2307,7 +2307,7 @@ pub fn initBuiltins(coff: *Coff) !void { const list_end_si = coff.addSymbolAssumeCapacity(); const list_end_sym = list_end_si.get(coff); list_end_sym.setExtra(.{ .size = addr_info.size }); - list_end_sym.ni = .wrap(try end_sym.ni.unwrap().?.addHeaderChildAfter(&coff.mf, gpa, .none, .{ + list_end_sym.ni = .wrap(try end_sym.ni.unwrap().?.addHeaderChildAfter(gpa, &coff.mf, .none, .{ .size = addr_info.size, })); coff.nodes.appendAssumeCapacity(.{ .builtin = list_end_si }); @@ -2723,7 +2723,7 @@ fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !Symbo const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1]; string_gop.value_ptr.* = @fromBackingInt(@intCast(string_index)); - try coff.symbol_table.strings_ni.resizeLeaf(&coff.mf, gpa, string_index + name.len + 1); + try coff.symbol_table.strings_ni.resizeLeaf(gpa, &coff.mf, string_index + name.len + 1); const slice = coff.symbol_table.strings_ni.slice(&coff.mf); @memcpy(slice[@intCast(string_index)..][0..name.len], name); slice[@intCast(string_index + name.len)] = 0; @@ -2948,7 +2948,7 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind, const comp = coff.base.comp; const gpa = comp.gpa; - const header_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, Node.known.file.last(&coff.mf), .{ + const header_ni = try Node.known.file.addHeaderChildAfter(gpa, &coff.mf, Node.known.file.last(&coff.mf), .{ .size = @sizeOf(std.coff.ArchiveMemberHeader), .alignment = .@"2", .moved = true, @@ -2960,7 +2960,7 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind, .first_linker, .second_linker, .longnames, .coff => .@"4", else => .@"2", }; - const content_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .wrap(header_ni), .{ + const content_ni = try Node.known.file.addHeaderChildAfter(gpa, &coff.mf, .wrap(header_ni), .{ .alignment = content_align, .size = content_align.forward(size), .resized = size > 0, @@ -2989,7 +2989,7 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind, const old_size = Node.known.second_linker_member.location(&coff.mf).resolve(&coff.mf)[1]; const old_header_size = new_num_members * @sizeOf(u32); const trailing_size: usize = @intCast(old_size - old_header_size); - try Node.known.second_linker_member.resizeLeaf(&coff.mf, gpa, old_size + @sizeOf(u32)); + try Node.known.second_linker_member.resizeLeaf(gpa, &coff.mf, old_size + @sizeOf(u32)); const slice = Node.known.second_linker_member.slice(&coff.mf); @memmove( @@ -3060,7 +3060,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void { { const old_header_size: usize = @intCast(@sizeOf(u32) + @backingInt(mfli) * @sizeOf(u32)); const new_header_size: usize = @intCast(old_header_size + @sizeOf(u32)); - try Node.known.first_linker_member.resizeLeaf(&coff.mf, gpa, Alignment.@"4".forward(new_header_size + new_string_table_size)); + try Node.known.first_linker_member.resizeLeaf(gpa, &coff.mf, Alignment.@"4".forward(new_header_size + new_string_table_size)); const slice = Node.known.first_linker_member.slice(&coff.mf); @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]); @@ -3074,7 +3074,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void { const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr()); const old_header_size = 2 * @sizeOf(u32) + num_members * @sizeOf(u32) + @backingInt(mfli) * @sizeOf(u16); const new_header_size = old_header_size + @sizeOf(u16); - try Node.known.second_linker_member.resizeLeaf(&coff.mf, gpa, Alignment.@"4".forward(new_header_size + new_string_table_size)); + try Node.known.second_linker_member.resizeLeaf(gpa, &coff.mf, Alignment.@"4".forward(new_header_size + new_string_table_size)); const old_needs_sort = coff.pending_members.get(Member.Index.second) != null; const needs_sort = old_needs_sort or (if (coff.lib_string_table.items.len > 0) @@ -3108,7 +3108,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void { coff.member_prog_node.increaseEstimatedTotalItems(1); } -fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void { +fn flushSymbolTableEntry(coff: *Coff, index: u32) !void { assert(!coff.isImage()); const gpa = coff.base.comp.gpa; @@ -3119,7 +3119,6 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void { assert(sym.ni != .none or sym.gmi != .none); const entry = coff.symbolTableEntryPtr(sti.*) orelse entry: { - var buf: [15]u8 = undefined; const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType = if (sym.gmi != .none) blk: { const name = sym.gmi.name(coff); @@ -3148,21 +3147,22 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void { }; }, .uav => |umi| { - var w = Io.Writer.fixed(&buf); - w.print("__anon_{x}", .{umi.uavValue(coff)}) catch unreachable; + var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined; + const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable; break :blk .{ - try coff.getOrPutSymbolName(w.buffered(), null), + try coff.getOrPutSymbolName(name, null), 0, .NULL, }; }, inline .lazy_code, .lazy_const_data => |mi, tag| { const lazy_sym = mi.lazySymbol(coff); - const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{ - @tagName(lazy_sym.kind), - Type.fromInterned(lazy_sym.ty).fmt(pt), - }); - defer gpa.free(name); + var name_buf: [ + std.fmt.count("__lazy_const_data_{d}", .{std.math.maxInt(u32)}) + ]u8 = undefined; + const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ + lazy_sym.kind, mi, + }) catch unreachable; const string = try coff.getOrPutString(name); break :blk .{ @@ -3181,7 +3181,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void { const new_num_symbols = old_num_symbols + 1 + num_aux_symbols; coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols); - try coff.symbol_table.ni.resizeLeaf(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf()); + try coff.symbol_table.ni.resizeLeaf(gpa, &coff.mf, new_num_symbols * std.coff.Symbol.sizeOf()); sti.* = .wrap(old_num_symbols); si.flushSymbolTableIndex(coff); @@ -3317,7 +3317,7 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void { try fr.seekTo(file_loc.offset); var nw: MappedFile.Node.Writer = undefined; const si = isi.symbol(coff); - si.node(coff).writer(&coff.mf, gpa, &nw); + si.node(coff).writer(gpa, &coff.mf, &nw); defer nw.deinit(); log.debug("flushInputSection({f}{f}, {s}, {d}, n{d})", .{ path, @@ -3345,12 +3345,12 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S const section_table_len = section_index + 1; coff.targetStore(&coff_header.number_of_sections, section_table_len); try Node.known.section_table.resizeLeaf( + gpa, &coff.mf, - gpa, @sizeOf(std.coff.SectionHeader) * section_table_len, ); - const ni = try coff.sectionParent().addFloatingChild(&coff.mf, gpa, .{ + const ni = try coff.sectionParent().addFloatingChild(gpa, &coff.mf, .{ .alignment = coff.mf.flags.block_size, .moved = true, .bubbles_moved = false, @@ -3486,7 +3486,7 @@ fn pseudoSectionMapIndex( try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.symbols.ensureUnusedCapacity(gpa, 1); - const ni = try parent.node(coff).addFloatingChild(&coff.mf, gpa, .{ .alignment = alignment }); + const ni = try parent.node(coff).addFloatingChild(gpa, &coff.mf, .{ .alignment = alignment }); const si = coff.addSymbolAssumeCapacity(); pseudo_section_gop.value_ptr.* = si; const sym = si.get(coff); @@ -3560,7 +3560,7 @@ fn objectSectionMapIndex( } } } - const ni = try parent_ni.addHeaderChildAfter(&coff.mf, gpa, prev_oni, .{ + const ni = try parent_ni.addHeaderChildAfter(gpa, &coff.mf, prev_oni, .{ .alignment = alignment, }); const si = coff.addSymbolAssumeCapacity(); @@ -3579,13 +3579,13 @@ fn objectSectionMapIndex( const parent_alignment = parent_ni.alignment(&coff.mf); if (alignment.compare(.gt, parent_alignment)) { log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment }); - try parent_ni.realign(&coff.mf, gpa, alignment); + try parent_ni.realign(gpa, &coff.mf, alignment); } const old_alignment = sym.ni.unwrap().?.alignment(&coff.mf); if (alignment.compare(.gt, old_alignment)) { log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment }); - try sym.ni.unwrap().?.realign(&coff.mf, gpa, alignment); + try sym.ni.unwrap().?.realign(gpa, &coff.mf, alignment); } try coff.verifyParentSectionAttributes( @@ -3742,9 +3742,9 @@ fn addRelocAssumeCapacity( coff.targetStore(&aux_ptr.number_of_relocations, new_num_relocations); if (section.relocation_table_ni.unwrap()) |relocation_table_ni| { - try relocation_table_ni.resizeLeaf(&coff.mf, gpa, new_size); + try relocation_table_ni.resizeLeaf(gpa, &coff.mf, new_size); } else { - section.relocation_table_ni = .wrap(try coff.sectionParent().addFloatingChild(&coff.mf, gpa, .{ + section.relocation_table_ni = .wrap(try coff.sectionParent().addFloatingChild(gpa, &coff.mf, .{ .size = new_size, .alignment = .@"2", .moved = true, @@ -4094,7 +4094,7 @@ fn loadObject( { // TODO: This should be deferred to an idle task (but resize it here!) var nw: MappedFile.Node.Writer = undefined; - member.content_ni.writer(&coff.mf, gpa, &nw); + member.content_ni.writer(gpa, &coff.mf, &nw); defer nw.deinit(); try fr.seekTo(fl.offset); @@ -4653,7 +4653,7 @@ fn loadObject( if (section.parent_si == .null) continue; const alignment: Alignment = .fromByteUnits(section.header.flags.ALIGN.toByteUnits() orelse 1); - const ni = try section.parent_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ + const ni = try section.parent_si.node(coff).addFloatingChild(gpa, &coff.mf, .{ .size = alignment.forward(section.header.size_of_raw_data), .alignment = alignment, .moved = true, @@ -5064,7 +5064,9 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) Loa offset: u32, iami: ?InputArchive.Member.Index, }) = .empty; + defer members.deinit(gpa); var symbol_member_indices: std.ArrayList(u32) = .empty; + defer symbol_member_indices.deinit(gpa); const iai: InputArchive.Index = @fromBackingInt(@intCast(coff.input_archives.items.len)); (try coff.input_archives.addOne(gpa)).* = .{ @@ -5447,7 +5449,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde const sec_si = try coff.navSection(zcu, nav.resolved.?); try coff.nodes.ensureUnusedCapacity(gpa, 1); if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); - const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ + const ni = try sec_si.node(coff).addFloatingChild(gpa, &coff.mf, .{ .alignment = .fromIp(zcu.navAlignment(nav_index)), .moved = true, }); @@ -5469,7 +5471,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde { var nw: MappedFile.Node.Writer = undefined; - ni.writer(&coff.mf, gpa, &nw); + ni.writer(gpa, &coff.mf, &nw); defer nw.deinit(); codegen.generateSymbol( &coff.base, @@ -5486,8 +5488,26 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde } if (nav.resolved.?.@"linksection".unwrap()) |_| { - try ni.resizeLeaf(&coff.mf, gpa, si.get(coff).extra.size); + try ni.resizeLeaf(gpa, &coff.mf, si.get(coff).extra.size); } + + // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs. + try coff.genPending(pt); +} + +pub fn updateContainerType( + coff: *Coff, + pt: Zcu.PerThread, + ty: InternPool.Index, + success: bool, +) link.Error!void { + if (!success) return; + var lazy_it = coff.lazy.iterator(); + while (lazy_it.next()) |lazy| if (lazy.value.map.getIndex(ty)) |lmi| { + if (lazy.value.pending_index <= lmi) continue; + // This type has changed on this incremental update, so update the lazy code/data. + try coff.genLazy(pt, .{ .kind = lazy.key, .index = @intCast(lmi) }); + }; } pub fn lowerUav( @@ -5558,7 +5578,7 @@ fn updateFuncInner( if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); const mod = zcu.navFileScope(func.owner_nav).mod.?; const target = &mod.resolved_target.result; - const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ + const ni = try sec_si.node(coff).addFloatingChild(gpa, &coff.mf, .{ .alignment = switch (nav.resolved.?.@"align") { .none => switch (mod.optimize_mode) { .debug, @@ -5587,7 +5607,7 @@ fn updateFuncInner( }; var nw: MappedFile.Node.Writer = undefined; - ni.writer(&coff.mf, gpa, &nw); + ni.writer(gpa, &coff.mf, &nw); defer nw.deinit(); codegen.emitFunction( &coff.base, @@ -5603,10 +5623,13 @@ fn updateFuncInner( }; si.get(coff).extra.size = @intCast(nw.interface.end); try si.applyLocationRelocs(coff); + + // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs. + try coff.genPending(pt); } pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void { - coff.flushLazy(pt, .{ + coff.genLazyInner(pt, .{ .kind = .const_data, .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return), }) catch |err| switch (err) { @@ -5863,8 +5886,8 @@ pub fn flush( const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols); coff.symbol_table.ni.resizeLeaf( - &coff.mf, comp.gpa, + &coff.mf, number_of_symbols * std.coff.Symbol.sizeOf(), ) catch |err| switch (err) { else => |e| return e, @@ -5919,22 +5942,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool { }; break :task; } - while (coff.pending_uavs.pop()) |pending_uav| { - const sub_prog_node = coff.idleProgNode(tid, coff.const_prog_node, .{ .uav = pending_uav.key }); - defer sub_prog_node.end(); - coff.flushUav( - .{ .zcu = comp.zcu.?, .tid = tid }, - pending_uav.key, - pending_uav.value.alignment, - ) catch |err| switch (err) { - else => |e| return e, - error.MappedFileIo => return comp.link_diags.fail( - "linker failed to lower constant: {t}", - .{coff.mf.io_err.?}, - ), - }; - break :task; - } if (coff.pending_input) |pending_iami| { const name_slice = pending_iami.member(coff).name.toSlice(coff); const sub_prog_node = coff.input_prog_node.start( @@ -5983,33 +5990,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool { }; break :task; } - var lazy_it = coff.lazy.iterator(); - while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) { - const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid }; - const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index }; - lazy.value.pending_index += 1; - const kind = switch (lmr.kind) { - .code => "code", - .const_data => "data", - }; - var name: [std.Progress.Node.max_name_len]u8 = undefined; - const sub_prog_node = coff.synth_prog_node.start( - std.mem.print(&name, "lazy {s} for {f}", .{ - kind, - Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt), - }) catch &name, - 0, - ); - defer sub_prog_node.end(); - coff.flushLazy(pt, lmr) catch |err| switch (err) { - else => |e| return e, - error.MappedFileIo => return comp.link_diags.fail( - "linker failed to lower lazy {s}: {t}", - .{ kind, coff.mf.io_err.? }, - ), - }; - break :task; - }; if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) { defer coff.symbol_table.pending_symbol_index += 1; const si = coff.symbol_table.symbols.keys()[coff.symbol_table.pending_symbol_index]; @@ -6025,7 +6005,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool { defer sub_prog_node.end(); coff.flushSymbolTableEntry( coff.symbol_table.pending_symbol_index, - .{ .zcu = comp.zcu.?, .tid = tid }, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => |e| return comp.link_diags.fail( @@ -6038,12 +6017,10 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool { } if (coff.section_merge_pending_index < coff.section_merges.count()) return true; - if (coff.pending_uavs.count() > 0) return true; if (coff.pending_input != null) return true; if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true; assert(!coff.exports_complete or coff.inputs_complete); if (coff.exports_complete and coff.pending_special_symbol != .none) return true; - for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true; if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) return true; return false; } @@ -6077,17 +6054,18 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { }; break :task; } - while (coff.mf.updates.pop()) |ni| { + while (coff.mf.updates.pop()) |ni| : (coff.mf.update_prog_node.completeOne()) { + if (ni.pendingDelete(&coff.mf)) continue; const clean_moved = ni.cleanMoved(&coff.mf); const clean_resized = ni.cleanResized(&coff.mf); - if (clean_moved or clean_resized) { - const sub_prog_node = - coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni)); - defer sub_prog_node.end(); - if (clean_moved) try coff.flushMoved(ni); - if (clean_resized) try coff.flushResized(ni); - break :task; - } else coff.mf.update_prog_node.completeOne(); + const clean_next_moved = ni.cleanNextMoved(&coff.mf); + if (!clean_moved and !clean_resized and !clean_next_moved) continue; + const sub_prog_node = + coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni)); + defer sub_prog_node.end(); + if (clean_moved) try coff.flushMoved(ni); + if (clean_resized) try coff.flushResized(ni); + break :task; } while (coff.pending_members.pop()) |pending_mi| { const sub_prog_node = coff.idleProgNode( @@ -6153,7 +6131,27 @@ fn idleProgNode( }, 0); } -fn flushUav( +fn genPending(coff: *Coff, pt: Zcu.PerThread) Error!void { + const comp = pt.zcu.comp; + while (coff.pending_uavs.pop()) |pending_uav| { + const sub_prog_node = coff.idleProgNode(pt.tid, coff.const_prog_node, .{ .uav = pending_uav.key }); + defer sub_prog_node.end(); + coff.genUav(pt, pending_uav.key, pending_uav.value.alignment) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail( + "linker failed to lower constant: {t}", + .{coff.mf.io_err.?}, + ), + }; + } + var lazy_it = coff.lazy.iterator(); + while (lazy_it.next()) |lazy| while (lazy.value.pending_index < lazy.value.map.count()) { + try coff.genLazy(pt, .{ .kind = lazy.key, .index = lazy.value.pending_index }); + lazy.value.pending_index += 1; + }; +} + +fn genUav( coff: *Coff, pt: Zcu.PerThread, umi: Node.UavMapIndex, @@ -6175,7 +6173,7 @@ fn flushUav( try coff.nodes.ensureUnusedCapacity(gpa, 1); if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); const sym = si.get(coff); - const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ + const ni = try sec_si.node(coff).addFloatingChild(gpa, &coff.mf, .{ .alignment = .fromIp(uav_align), .moved = true, }); @@ -6204,7 +6202,7 @@ fn flushUav( }; var nw: MappedFile.Node.Writer = undefined; - ni.writer(&coff.mf, gpa, &nw); + ni.writer(gpa, &coff.mf, &nw); defer nw.deinit(); codegen.generateSymbol( &coff.base, @@ -6311,7 +6309,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { .{ name, imp_match } else name: { try coff.ensureUnusedStringCapacity(imp_prefix.len + name_slice.len); - const imp_name = try std.fmt.allocPrint(gpa, imp_prefix ++ "{s}", .{name_slice}); + const imp_name = try gpa.print(imp_prefix ++ "{s}", .{name_slice}); defer gpa.free(imp_name); break :name .{ coff.getOrPutStringAssumeCapacity(imp_name), true }; }; @@ -6468,19 +6466,19 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { if (!gop.found_existing) { errdefer _ = coff.import_table.entries.pop(); try coff.import_table.ni.resizeLeaf( - &coff.mf, gpa, + &coff.mf, @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2), ); const import_hint_name_table_len = import_hint_name_align.forward(lib_name.len + ".dll".len + 1); const idata_section_ni = coff.import_table.ni.parent(&coff.mf).unwrap().?; - const import_lookup_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{ + const import_lookup_table_ni = try idata_section_ni.addFloatingChild(gpa, &coff.mf, .{ .size = addr_info.size * 2, .alignment = addr_info.alignment, .moved = true, }); - const import_address_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{ + const import_address_table_ni = try idata_section_ni.addFloatingChild(gpa, &coff.mf, .{ .size = addr_info.size * 2, .alignment = addr_info.alignment, .moved = true, @@ -6494,7 +6492,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { import_address_table_sym.section_number = coff.getNode(idata_section_ni).object_section.symbol(coff).get(coff).section_number; } - const import_hint_name_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{ + const import_hint_name_table_ni = try idata_section_ni.addFloatingChild(gpa, &coff.mf, .{ .size = import_hint_name_table_len, .alignment = import_hint_name_align, .moved = true, @@ -6550,9 +6548,9 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { gop.value_ptr.len = import_symbol_index + 1; const new_symbol_table_size = addr_info.size * (import_symbol_index + 2); - try gop.value_ptr.import_lookup_table_ni.resizeLeaf(&coff.mf, gpa, new_symbol_table_size); + try gop.value_ptr.import_lookup_table_ni.resizeLeaf(gpa, &coff.mf, new_symbol_table_size); const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff); - try import_address_table_ni.resizeLeaf(&coff.mf, gpa, new_symbol_table_size); + try import_address_table_ni.resizeLeaf(gpa, &coff.mf, new_symbol_table_size); const opt_imp_name = import.name.toSlice(coff); const opt_import_hint_name_index = if (opt_imp_name) |imp_name| blk: { @@ -6560,7 +6558,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { gop.value_ptr.hint_name_len = @intCast( import_hint_name_align.forward(import_hint_name_index + 2 + imp_name.len + 1), ); - try gop.value_ptr.import_hint_name_table_ni.resizeLeaf(&coff.mf, gpa, gop.value_ptr.hint_name_len); + try gop.value_ptr.import_hint_name_table_ni.resizeLeaf(gpa, &coff.mf, gop.value_ptr.hint_name_len); break :blk import_hint_name_index; } else null; @@ -6635,7 +6633,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { else => |tag| @panic(@tagName(tag)), .AMD64 => { const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }; - const ni = try parent_sym.ni.unwrap().?.addFloatingChild(&coff.mf, gpa, .{ + const ni = try parent_sym.ni.unwrap().?.addFloatingChild(gpa, &coff.mf, .{ .alignment = alignment, .size = alignment.forward(init.len), }); @@ -6773,7 +6771,31 @@ fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol { }; } -fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { +fn genLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { + const lazy = lmr.lazySymbol(coff); + if (lazy.ty == .anyerror_type) return; + const kind = switch (lmr.kind) { + .code => "code", + .const_data => "data", + }; + var name: [std.Progress.Node.max_name_len]u8 = undefined; + const sub_prog_node = coff.synth_prog_node.start( + std.mem.print(&name, "lazy {s} for {f}", .{ + kind, + Type.fromInterned(lazy.ty).fmt(pt), + }) catch &name, + 0, + ); + defer sub_prog_node.end(); + coff.genLazyInner(pt, lmr) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return coff.base.comp.link_diags.fail( + "linker failed to lower lazy {s}: {t}", + .{ kind, coff.mf.io_err.? }, + ), + }; +} +fn genLazyInner(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -6788,7 +6810,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { .code => .text, .const_data => .rdata, }; - const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ .moved = true }); + const ni = try sec_si.node(coff).addFloatingChild(gpa, &coff.mf, .{ .moved = true }); coff.nodes.appendAssumeCapacity(switch (lazy.kind) { .code => .{ .lazy_code = @fromBackingInt(@intCast(lmr.index)) }, .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(lmr.index)) }, @@ -6808,7 +6830,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { var required_alignment: InternPool.Alignment = .none; var nw: MappedFile.Node.Writer = undefined; - ni.writer(&coff.mf, gpa, &nw); + ni.writer(gpa, &coff.mf, &nw); defer nw.deinit(); codegen.generateLazySymbol( &coff.base, @@ -7356,7 +7378,7 @@ fn updateExportInner( pt: Zcu.PerThread, export_index: Zcu.Export.Index, alias_syms: *std.array_hash_map.Auto(Symbol.Index, Symbol.Index), -) !void { +) Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -7380,6 +7402,8 @@ fn updateExportInner( exported_si, }), } + + try coff.genPending(pt); while (try coff.resolve(pt.tid)) {} while (try coff.idle(pt.tid)) {} @@ -7444,7 +7468,7 @@ fn updateExportInner( if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index"))) return coff.base.comp.link_diags.fail("exports name table limit reached", .{}); - try coff.export_table.name_table_ni.resizeLeaf(&coff.mf, gpa, new_name_table_size); + try coff.export_table.name_table_ni.resizeLeaf(gpa, &coff.mf, new_name_table_size); const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf); @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]); @@ -7468,20 +7492,20 @@ fn updateExportInner( // TODO: These should all be resized ahead of time to fit all exports // after https://github.com/ziglang/zig/issues/23616 try coff.export_table.export_address_table_si.node(coff).resizeLeaf( - &coff.mf, gpa, + &coff.mf, export_count * @sizeOf(std.coff.ExportAddressTableEntry), ); try coff.export_table.name_pointer_table_ni.resizeLeaf( - &coff.mf, gpa, + &coff.mf, export_count * @sizeOf(std.coff.ExportNamePointerTableEntry), ); try coff.export_table.ordinal_table_ni.resizeLeaf( - &coff.mf, gpa, + &coff.mf, export_count * @sizeOf(std.coff.ExportOrdinalTableEntry), ); @@ -7519,17 +7543,19 @@ fn updateExportInner( } } -fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void { +fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void { const comp = coff.base.comp; const io = comp.io; var buffer: [512]u8 = undefined; const stderr = try io.lockStderr(&buffer, null); defer io.unlockStderr(); const w = &stderr.file_writer.interface; - _ = try coff.dump(w, tid); + _ = coff.dump(w, tid) catch |err| switch (err) { + error.WriteFailed => return stderr.file_writer.err.?, + }; } -pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult { +pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult { if (coff.options.enable_link_snapshots) { try coff.printNode(tid, w, .root, 0); try w.writeAll("Section table:\n"); @@ -7544,7 +7570,7 @@ pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpRe return .disabled; } -fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !void { +fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) Io.Writer.Error!void { const sym = si.get(coff); try w.print("{d:0>6}@{d:0>2} {x:08} n{d:0>8} | {s}\n", .{ si, @@ -7560,7 +7586,7 @@ fn printSymbol( w: *Io.Writer, tid: Zcu.PerThread.Id, si: Symbol.Index, -) !void { +) Io.Writer.Error!void { const sym = si.get(coff); try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} {s} n{d:0>8}+{x:08}:{s: <26} | {x:08} ", .{ si, @@ -7622,7 +7648,7 @@ fn printNodeName( w: *std.Io.Writer, tid: Zcu.PerThread.Id, node: Node, -) !void { +) Io.Writer.Error!void { switch (node) { else => {}, .image_section => |si| try w.print("({s})", .{ @@ -7702,7 +7728,7 @@ pub fn printNode( w: *Io.Writer, ni: MappedFile.Node.Index, indent: usize, -) !void { +) Io.Writer.Error!void { const node = coff.getNode(ni); try w.splatByteAll(' ', indent); try w.writeAll(@tagName(node)); @@ -7710,12 +7736,13 @@ pub fn printNode( { const mf_node = &coff.mf.nodes.items[@backingInt(ni)]; const off, const size = mf_node.location().resolve(&coff.mf); - try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}\n", .{ + try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}\n", .{ @backingInt(ni), off, size, mf_node.flags.alignment.toByteUnits(), mf_node.flags.position, + if (mf_node.flags.bubbles_moved) " bubbles_moved" else "", if (mf_node.flags.moved) " moved" else "", if (mf_node.flags.resized) " resized" else "", if (mf_node.flags.has_content) " has_content" else "", @@ -7730,22 +7757,30 @@ pub fn printNode( } return; } - const file_loc = ni.fileLocation(&coff.mf, false); - if (file_loc.size == 0) return; - var address = file_loc.offset; + const start_address: usize, const end_address: usize = file_loc: { + const file_loc = ni.fileLocation(&coff.mf, false); + break :file_loc .{ @intCast(file_loc.offset), @intCast(file_loc.offset + file_loc.size) }; + }; + var address = start_address; const line_len = 0x10; - var line_it = std.mem.window( - u8, - coff.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)], - line_len, - line_len, - ); - while (line_it.next()) |line_bytes| : (address += line_len) { + while (true) : (address = @min(std.mem.alignForward(usize, address + 1, line_len), end_address)) { try w.splatByteAll(' ', indent + 1); - try w.print("{x:0>8} ", .{address}); - for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte}); - try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1); - for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.'); + try w.print("{x:0>8}", .{address}); + if (address == end_address) break try w.writeByte('\n'); + try w.splatByteAll(' ', 2); + const start_byte_address = std.mem.alignBackward(usize, address, line_len); + const end_byte_address = start_byte_address + line_len; + for (start_byte_address..end_byte_address) |byte_address| + if (byte_address < start_address or byte_address >= end_address) + try w.splatByteAll(' ', 3) + else + try w.print("{x:0>2} ", .{coff.mf.memory_map.memory[byte_address]}); + try w.writeByte(' '); + for (start_byte_address..@min(end_address, end_byte_address)) |byte_address| + try w.writeByte(if (byte_address < start_address or byte_address >= end_address) ' ' else char: { + const byte = coff.mf.memory_map.memory[byte_address]; + break :char if (std.ascii.isPrint(byte)) byte else '.'; + }); try w.writeByte('\n'); } } diff --git a/src/link/ConstPool.zig b/src/link/ConstPool.zig index 9cf25cd1cb690fe58acbc592e5bfe86a7f3ebd32..ab90bb9df52132f29dde6610da5e1fb65ac50abf 100644 --- a/src/link/ConstPool.zig +++ b/src/link/ConstPool.zig @@ -45,10 +45,22 @@ pub const Index = enum(u32) { }; pub const User = union(enum) { - dwarf: *@import("Dwarf.zig"), + elf: *@import("Dwarf.zig"), + elf2: *@import("Elf2.zig"), + macho: *@import("Dwarf.zig"), c: *@import("C.zig"), llvm: @import("../codegen/llvm.zig").Object.Ptr, + fn devFeature(tag: @typeInfo(User).@"union".tag_type.?) dev.Feature { + return switch (tag) { + .elf => .elf_linker, + .elf2 => .elf2_linker, + .macho => .macho_linker, + .c => .c_linker, + .llvm => .llvm_backend, + }; + } + /// Inform the debug info implementation that the new constant `val` was added to the pool at /// the given index (which equals the current pool length) due to a `get` call. It is guaranteed /// that there will eventually be a call to either `updateConst` or `updateConstIncomplete` @@ -58,9 +70,12 @@ pub const User = union(enum) { pt: Zcu.PerThread, index: Index, val: InternPool.Index, - ) Allocator.Error!void { + ) link.Error!void { switch (user) { - inline else => |impl| return impl.addConst(pt, index, val), + inline else => |impl, tag| { + dev.check(devFeature(tag)); + return impl.addConst(pt, index, val); + }, } } @@ -73,9 +88,12 @@ pub const User = union(enum) { pt: Zcu.PerThread, index: Index, val: InternPool.Index, - ) Allocator.Error!void { + ) link.Error!void { switch (user) { - inline else => |impl| return impl.updateConst(pt, index, val), + inline else => |impl, tag| { + dev.check(devFeature(tag)); + return impl.updateConst(pt, index, val); + }, } } @@ -89,9 +107,12 @@ pub const User = union(enum) { pt: Zcu.PerThread, index: Index, val: InternPool.Index, - ) Allocator.Error!void { + ) link.Error!void { switch (user) { - inline else => |impl| return impl.updateConstIncomplete(pt, index, val), + inline else => |impl, tag| { + dev.check(devFeature(tag)); + return impl.updateConstIncomplete(pt, index, val); + }, } } }; @@ -128,12 +149,12 @@ pub fn updateContainerType( user: User, container_ty: InternPool.Index, success: bool, -) Allocator.Error!void { +) link.Error!void { if (success) { const gpa = pt.zcu.comp.gpa; try pool.complete_containers.put(gpa, container_ty, {}); } else { - _ = pool.complete_containers.fetchSwapRemove(container_ty); + _ = pool.complete_containers.swapRemove(container_ty); } var opt_dep = pool.container_deps.get(container_ty); while (opt_dep) |dep| : (opt_dep = dep.ptr(pool).next.unwrap()) { @@ -143,7 +164,7 @@ pub fn updateContainerType( /// After this is called, there may be a constant for which debug information (complete or not) has /// not yet been emitted, so the user must call `flushPending` at some point after this call. -pub fn get(pool: *ConstPool, pt: Zcu.PerThread, user: User, val: InternPool.Index) Allocator.Error!ConstPool.Index { +pub fn get(pool: *ConstPool, pt: Zcu.PerThread, user: User, val: InternPool.Index) link.Error!ConstPool.Index { const zcu = pt.zcu; const ip = &zcu.intern_pool; const gpa = zcu.comp.gpa; @@ -160,13 +181,16 @@ pub fn get(pool: *ConstPool, pt: Zcu.PerThread, user: User, val: InternPool.Inde } return index; } -pub fn flushPending(pool: *ConstPool, pt: Zcu.PerThread, user: User) Allocator.Error!void { +pub fn getIfExists(pool: *ConstPool, val: InternPool.Index) ?ConstPool.Index { + return @fromBackingInt(@intCast(pool.values.getIndex(val) orelse return null)); +} +pub fn flushPending(pool: *ConstPool, pt: Zcu.PerThread, user: User) link.Error!void { while (pool.pending.pop()) |pending_ty| { try pool.update(pt, user, pending_ty); } } -fn update(pool: *ConstPool, pt: Zcu.PerThread, user: User, index: ConstPool.Index) Allocator.Error!void { +fn update(pool: *ConstPool, pt: Zcu.PerThread, user: User, index: ConstPool.Index) link.Error!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const val = index.val(pool); @@ -285,6 +309,8 @@ fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Al const std = @import("std"); const Allocator = std.mem.Allocator; +const dev = @import("../dev.zig"); const InternPool = @import("../InternPool.zig"); +const link = @import("../link.zig"); const Type = @import("../Type.zig"); const Zcu = @import("../Zcu.zig"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 78f3287d8e014e3fb37f51fd7fc2355b2ccbbeef..8c4d2b669998e4a83d876185279822c1fcb8c862 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -122,7 +122,7 @@ const DebugFrame = struct { uleb128Bytes(1) + 1, } + switch (target.cpu.arch) { .x86_64 => len: { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const Register = @import("../codegen/x86_64/bits.zig").Register; break :len uleb128Bytes(1) + sleb128Bytes(-8) + uleb128Bytes(Register.rip.dwarfNum()) + 1 + uleb128Bytes(Register.rsp.dwarfNum()) + sleb128Bytes(-1) + @@ -1626,15 +1626,25 @@ pub const WipNav = struct { wip_nav.any_children = true; } - pub fn advancePCAndLine(wip_nav: *WipNav, delta_line: i33, delta_pc: u64) Allocator.Error!void { - return wip_nav.advancePCAndLineWriterError(delta_line, delta_pc) catch |err| switch (err) { + pub fn advanceLineAndPc( + wip_nav: *WipNav, + delta_line: i33, + delta_pc: u64, + end: bool, + ) Allocator.Error!void { + return wip_nav.advanceLineAndPcWriterError( + delta_line, + delta_pc, + end, + ) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, }; } - fn advancePCAndLineWriterError( + fn advanceLineAndPcWriterError( wip_nav: *WipNav, delta_line: i33, delta_pc: u64, + end: bool, ) Writer.Error!void { const dlw = &wip_nav.debug_line.writer; @@ -1654,20 +1664,30 @@ pub const WipNav = struct { const op_advance = @divExact(delta_pc, header.minimum_instruction_length) * header.maximum_operations_per_instruction + delta_op; const max_op_advance: u9 = (std.math.maxInt(u8) - header.opcode_base) / header.line_range; - const remaining_op_advance: u8 = @intCast(if (op_advance >= 2 * max_op_advance) remaining: { - try dlw.writeByte(DW.LNS.advance_pc); - try dlw.writeUleb128(op_advance); + const remaining_op_advance: u8 = @intCast(if (end or + op_advance >= 2 * max_op_advance) + remaining: { + if (op_advance == max_op_advance) { + try dlw.writeByte(DW.LNS.const_add_pc); + } else if (op_advance != 0) { + try dlw.writeByte(DW.LNS.advance_pc); + try dlw.writeUleb128(op_advance); + } else assert(end); break :remaining 0; } else if (op_advance >= max_op_advance) remaining: { try dlw.writeByte(DW.LNS.const_add_pc); break :remaining op_advance - max_op_advance; } else op_advance); - if (remaining_delta_line == 0 and remaining_op_advance == 0) - try dlw.writeByte(DW.LNS.copy) - else + if (remaining_delta_line != 0 or remaining_op_advance != 0) { + assert(!end); try dlw.writeByte(@intCast((remaining_delta_line - header.line_base) + (header.line_range * remaining_op_advance) + header.opcode_base)); + } else if (end) { + try dlw.writeByte(DW.LNS.extended_op); + try dlw.writeUleb128(1); + try dlw.writeByte(DW.LNE.end_sequence); + } else try dlw.writeByte(DW.LNS.copy); } pub fn setColumn(wip_nav: *WipNav, column: u32) Allocator.Error!void { @@ -1990,7 +2010,7 @@ pub const WipNav = struct { try ctx.wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } } = .{ .wip_nav = wip_nav }; - try adapter.writer().writeUleb128(counter.dw.count + counter.dw.writer.end); + try adapter.writer().writeUleb128(counter.dw.fullCount()); try loc.write(adapter); } @@ -2032,7 +2052,7 @@ pub const WipNav = struct { try ctx.wip_nav.sectionOffset(.debug_frame, .debug_info, unit, entry, 0); } } = .{ .wip_nav = wip_nav }; - try adapter.writer().writeUleb128(counter.dw.count + counter.dw.writer.end); + try adapter.writer().writeUleb128(counter.dw.fullCount()); try loc.write(adapter); } @@ -2072,7 +2092,7 @@ pub const WipNav = struct { assert(value.typeOf(wip_nav.pt.zcu).comptimeOnly(wip_nav.pt.zcu)); } const dwarf = wip_nav.dwarf; - const index = try dwarf.const_pool.get(wip_nav.pt, .{ .dwarf = dwarf }, value.toIntern()); + const index = try dwarf.const_pool.get(wip_nav.pt, dwarf.constPoolUser(), value.toIntern()); return dwarf.values.items[@backingInt(index)]; } @@ -2105,20 +2125,20 @@ pub const WipNav = struct { const size = ty.abiSize(wip_nav.pt.zcu); try diw.writeUleb128(size); if (size == 0) return; - const old_end = wip_nav.debug_info.writer.end; + const old_end = diw.end; try codegen.generateSymbol( wip_nav.dwarf.bin_file, wip_nav.pt, val, - &wip_nav.debug_info.writer, + diw, .{ .debug_output = .{ .dwarf = wip_nav } }, ); - if (old_end + size != wip_nav.debug_info.writer.end) { + if (old_end + size != diw.end) { std.debug.print("{f} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), size, - wip_nav.debug_info.writer.end - old_end, + diw.end - old_end, }); unreachable; } @@ -2182,7 +2202,7 @@ pub const WipNav = struct { wip_nav: *WipNav, abbrev_code: struct { decl: AbbrevCode, - generic_decl: AbbrevCode, + decl_specification: AbbrevCode, decl_instance: AbbrevCode, }, nav: *const InternPool.Nav, @@ -2196,11 +2216,11 @@ pub const WipNav = struct { const orig_entry = wip_nav.entry; defer wip_nav.entry = orig_entry; - const parent_type, const is_generic_decl = if (nav.analysis) |analysis| parent_info: { + const parent_type, const is_specification = if (nav.analysis) |analysis| parent_info: { const parent_type: Type = .fromInterned(zcu.namespacePtr(analysis.namespace).owner_type); const decl_gop = try dwarf.decls.getOrPut(dwarf.gpa, analysis.zir_index); errdefer _ = if (!decl_gop.found_existing) dwarf.decls.pop(); - const was_generic_decl = decl_gop.found_existing and + const was_specification = decl_gop.found_existing and switch (try dwarf.debug_info.declAbbrevCode(wip_nav.unit, decl_gop.value_ptr.*)) { .null, .decl_alias, @@ -2222,9 +2242,9 @@ pub const WipNav = struct { .decl_extern_nullary_func, .decl_extern_func, => false, - .generic_decl_var, - .generic_decl_const, - .generic_decl_func, + .decl_specification_var, + .decl_specification_const, + .decl_specification_func, => true, // This comes from a decl which was previously generated as an incomplete value @@ -2235,11 +2255,11 @@ pub const WipNav = struct { else => |t| std.debug.panic("bad decl abbrev code: {t}", .{t}), }; if (parent_type.getCaptures(zcu).len == 0) { - if (was_generic_decl) try dwarf.freeCommonEntry(wip_nav.unit, decl_gop.value_ptr.*); + if (was_specification) try dwarf.freeCommonEntry(wip_nav.unit, decl_gop.value_ptr.*); decl_gop.value_ptr.* = orig_entry; break :parent_info .{ parent_type, false }; } else { - if (was_generic_decl) + if (was_specification) dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(decl_gop.value_ptr.*).clear() else decl_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); @@ -2248,8 +2268,8 @@ pub const WipNav = struct { } } else .{ null, false }; - try wip_nav.abbrevCode(if (is_generic_decl) abbrev_code.generic_decl else abbrev_code.decl); - try wip_nav.refType((if (is_generic_decl) null else parent_type) orelse + try wip_nav.abbrevCode(if (is_specification) abbrev_code.decl_specification else abbrev_code.decl); + try wip_nav.refType((if (is_specification) null else parent_type) orelse .fromInterned(zcu.fileRootType(file))); assert(diw.end == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, decl.src_line + 1, dwarf.endian); @@ -2257,14 +2277,14 @@ pub const WipNav = struct { try diw.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); try wip_nav.strp(nav.name.toSlice(ip)); - if (!is_generic_decl) return; - const generic_decl_entry = wip_nav.entry; - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, generic_decl_entry, dwarf, wip_nav.debug_info.written()); + if (!is_specification) return; + const specification_entry = wip_nav.entry; + try dwarf.debug_info.section.replaceEntry(wip_nav.unit, specification_entry, dwarf, wip_nav.debug_info.written()); wip_nav.debug_info.clearRetainingCapacity(); wip_nav.entry = orig_entry; try wip_nav.abbrevCode(abbrev_code.decl_instance); try wip_nav.refType(parent_type.?); - try wip_nav.infoSectionOffset(.debug_info, wip_nav.unit, generic_decl_entry, 0); + try wip_nav.infoSectionOffset(.debug_info, wip_nav.unit, specification_entry, 0); } }; @@ -2278,10 +2298,9 @@ fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { pub fn init(lf: *link.File, format: DW.Format) Dwarf { const comp = lf.comp; - const gpa = comp.gpa; const target = &comp.root_mod.resolved_target.result; return .{ - .gpa = gpa, + .gpa = comp.gpa, .bin_file = lf, .format = format, .address_size = switch (target.ptrBitWidth()) { @@ -2566,7 +2585,7 @@ pub fn initWipNav( pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, sym_index: link.File.SymbolId, -) error{ OutOfMemory, AlreadyReported }!WipNav { +) link.Error!WipNav { return initWipNavInner(dwarf, pt, nav_index, sym_index) catch |err| switch (err) { error.OutOfMemory => error.OutOfMemory, else => |e| pt.zcu.codegenFail(nav_index, "failed to init dwarf: {s}", .{@errorName(e)}), @@ -2661,11 +2680,11 @@ fn initWipNavInner( const diw = &wip_nav.debug_info.writer; try wip_nav.declCommon(if (func_type.param_types.len > 0 or func_type.is_var_args) .{ .decl = .decl_extern_func, - .generic_decl = .generic_decl_func, + .decl_specification = .decl_specification_func, .decl_instance = .decl_instance_extern_func, } else .{ .decl = .decl_extern_nullary_func, - .generic_decl = .generic_decl_func, + .decl_specification = .decl_specification_func, .decl_instance = .decl_instance_extern_nullary_func, }, &nav, inst_info.file, &decl); try wip_nav.strp(@"extern".name.toSlice(ip)); @@ -2686,7 +2705,7 @@ fn initWipNavInner( .func => |func| if (func.owner_nav != nav_index) { try wip_nav.declCommon(.{ .decl = .decl_alias, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_alias, }, &nav, inst_info.file, &decl); try wip_nav.refNav(func.owner_nav); @@ -2739,7 +2758,7 @@ fn initWipNavInner( const diw = &wip_nav.debug_info.writer; try wip_nav.declCommon(.{ .decl = .decl_func, - .generic_decl = .generic_decl_func, + .decl_specification = .decl_specification_func, .decl_instance = .decl_instance_func, }, &nav, inst_info.file, &decl); try wip_nav.strp(switch (decl.linkage) { @@ -2774,7 +2793,7 @@ fn initWipNavInner( try dlw.writeByte(DW.LNS.set_column); try dlw.writeUleb128(func.lbrace_column + 1); - try wip_nav.advancePCAndLine(func.lbrace_line, 0); + try wip_nav.advanceLineAndPc(func.lbrace_line, 0, false); } else { try dlw.writeUleb128(1 + @backingInt(dwarf.address_size)); try dlw.writeByte(DW.LNE.set_address); @@ -2791,17 +2810,17 @@ fn initWipNavInner( try dlw.writeByte(DW.LNS.set_column); try dlw.writeUleb128(func.lbrace_column + 1); - try wip_nav.advancePCAndLine(@intCast(decl.src_line + func.lbrace_line), 0); + try wip_nav.advanceLineAndPc(decl.src_line + func.lbrace_line, 0, false); } }, else => { const diw = &wip_nav.debug_info.writer; try wip_nav.declCommon(.{ .decl = .decl_var, - .generic_decl = switch (decl.kind) { + .decl_specification = switch (decl.kind) { .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable, - .@"const" => .generic_decl_const, - .@"var" => .generic_decl_var, + .@"const" => .decl_specification_const, + .@"var" => .decl_specification_var, }, .decl_instance = .decl_instance_var, }, &nav, inst_info.file, &decl); @@ -2983,19 +3002,13 @@ fn finishWipNavWriterError( log.debug("finishWipNav({f})", .{nav.fqn.fmt(ip)}); try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - const dlw = &wip_nav.debug_line.writer; - if (dlw.end > 0) { - try dlw.writeByte(DW.LNS.extended_op); - try dlw.writeUleb128(1); - try dlw.writeByte(DW.LNE.end_sequence); - try dwarf.debug_line.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_line.written()); - } + try dwarf.debug_line.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_line.written()); try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); } -pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, AlreadyReported }!void { +pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { return updateComptimeNavInner(dwarf, pt, nav_index) catch |err| switch (err) { error.OutOfMemory => error.OutOfMemory, else => |e| pt.zcu.codegenFail(nav_index, "failed to update dwarf: {s}", .{@errorName(e)}), @@ -3054,8 +3067,8 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const loaded_struct = ip.loadStructType(nav_val.toIntern()); if (nav_index.toOptional() == loaded_struct.name_nav) { // This Nav's entry is populated by the type, not the actual Nav. - _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); return; } break :tag .alias; @@ -3064,8 +3077,8 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const loaded_enum = ip.loadEnumType(nav_val.toIntern()); if (nav_index.toOptional() == loaded_enum.name_nav) { // This Nav's entry is populated by the type, not the actual Nav. - _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); return; } break :tag .alias; @@ -3074,8 +3087,8 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const loaded_union = ip.loadUnionType(nav_val.toIntern()); if (nav_index.toOptional() == loaded_union.name_nav) { // This Nav's entry is populated by the type, not the actual Nav. - _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); return; } break :tag .alias; @@ -3084,8 +3097,8 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const loaded_opaque = ip.loadOpaqueType(nav_val.toIntern()); if (nav_index.toOptional() == loaded_opaque.name_nav) { // This Nav's entry is populated by the type, not the actual Nav. - _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); return; } break :tag .alias; @@ -3168,7 +3181,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .alias => { try wip_nav.declCommon(.{ .decl = .decl_alias, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_alias, }, &nav, inst_info.file, &decl); try wip_nav.refType(nav_val.toType()); @@ -3176,7 +3189,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .@"var" => { try wip_nav.declCommon(.{ .decl = .decl_var, - .generic_decl = .generic_decl_var, + .decl_specification = .decl_specification_var, .decl_instance = .decl_instance_var, }, &nav, inst_info.file, &decl); try wip_nav.strp(switch (decl.linkage) { @@ -3196,19 +3209,19 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo const has_comptime_state = nav_ty.comptimeOnly(zcu); try wip_nav.declCommon(if (has_runtime_bits and has_comptime_state) .{ .decl = .decl_const_runtime_bits_comptime_state, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_const_runtime_bits_comptime_state, } else if (has_comptime_state) .{ .decl = .decl_const_comptime_state, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_const_comptime_state, } else if (has_runtime_bits) .{ .decl = .decl_const_runtime_bits, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_const_runtime_bits, } else .{ .decl = .decl_const, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_const, }, &nav, inst_info.file, &decl); try wip_nav.strp(switch (decl.linkage) { @@ -3232,11 +3245,11 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo } else true; try wip_nav.declCommon(if (is_nullary) .{ .decl = .decl_nullary_func_generic, - .generic_decl = .generic_decl_func, + .decl_specification = .decl_specification_func, .decl_instance = .decl_instance_nullary_func_generic, } else .{ .decl = .decl_func_generic, - .generic_decl = .generic_decl_func, + .decl_specification = .decl_specification_func, .decl_instance = .decl_instance_func_generic, }, &nav, inst_info.file, &decl); try wip_nav.refType(.fromInterned(func_type.return_type)); @@ -3254,14 +3267,14 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .func_alias => |owner_nav| { try wip_nav.declCommon(.{ .decl = .decl_alias, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_alias, }, &nav, inst_info.file, &decl); try wip_nav.refNav(owner_nav); }, } try dwarf.debug_info.section.replaceEntry(unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); } pub fn updateContainerType( @@ -3270,7 +3283,7 @@ pub fn updateContainerType( ty: InternPool.Index, success: bool, ) !void { - try dwarf.const_pool.updateContainerType(pt, .{ .dwarf = dwarf }, ty, success); + try dwarf.const_pool.updateContainerType(pt, dwarf.constPoolUser(), ty, success); } /// Should only be called by the `link.ConstPool` implementation. pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { @@ -3374,12 +3387,12 @@ fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_inde const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file_index); try wip_nav.abbrevCode(.empty_file); try wip_nav.debug_info.writer.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_struct.name.toSlice(ip)); + try wip_nav.strp(loaded_struct.fqn.toSlice(ip)); } else { try dwarf.emitIncompleteContainerType( &wip_nav, loaded_struct.zir_index, - loaded_struct.name, + loaded_struct.fqn, loaded_struct.name_nav, ); } @@ -3389,7 +3402,7 @@ fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_inde try dwarf.emitIncompleteContainerType( &wip_nav, loaded_union.zir_index, - loaded_union.name, + loaded_union.fqn, loaded_union.name_nav, ); }, @@ -3399,12 +3412,12 @@ fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_inde try dwarf.emitIncompleteContainerType( &wip_nav, zir_index, - loaded_enum.name, + loaded_enum.fqn, loaded_enum.name_nav, ); } else { try wip_nav.abbrevCode(.generated_empty_struct_type); - try wip_nav.strp(loaded_enum.name.toSlice(ip)); + try wip_nav.strp(loaded_enum.fqn.toSlice(ip)); try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); } }, @@ -3413,7 +3426,7 @@ fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_inde try dwarf.emitIncompleteContainerType( &wip_nav, loaded_opaque.zir_index, - loaded_opaque.name, + loaded_opaque.fqn, loaded_opaque.name_nav, ); }, @@ -3438,7 +3451,7 @@ fn emitIncompleteContainerType( dwarf: *Dwarf, wip_nav: *WipNav, zir_index: InternPool.TrackedInst.Index, - name: InternPool.NullTerminatedString, + fqn: InternPool.NullTerminatedString, name_nav: InternPool.Nav.Index.Optional, ) !void { const zcu = wip_nav.pt.zcu; @@ -3450,7 +3463,7 @@ fn emitIncompleteContainerType( const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(.{ .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_namespace_struct, }, &nav, file, &decl); try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); @@ -3459,7 +3472,7 @@ fn emitIncompleteContainerType( const file_gop = try dwarf.getModInfo(wip_nav.unit).files.getOrPut(dwarf.gpa, file); try wip_nav.abbrevCode(.empty_struct_type); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name.toSlice(ip)); + try wip_nav.strp(fqn.toSlice(ip)); try diw.writeByte(@intFromBool(true)); } } @@ -3868,11 +3881,11 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(if (loaded_struct.field_types.len == 0) .{ .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_namespace_struct, } else .{ .decl = .decl_struct, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_struct, }, &nav, file, &decl); } else { @@ -3882,7 +3895,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co else => if (struct_is_file) .file else .struct_type, }); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_struct.name.toSlice(ip)); + try wip_nav.strp(loaded_struct.fqn.toSlice(ip)); } if (loaded_struct.field_types.len == 0) { if (!struct_is_file) try diw.writeByte(@intFromBool(false)); @@ -3947,7 +3960,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(.{ .decl = .decl_packed_struct, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_packed_struct, }, &nav, file, &decl); break :t true; @@ -3955,7 +3968,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); try wip_nav.abbrevCode(if (loaded_struct.field_types.len > 0) .packed_struct_type else .empty_packed_struct_type); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_struct.name.toSlice(ip)); + try wip_nav.strp(loaded_struct.fqn.toSlice(ip)); break :t loaded_struct.field_types.len > 0; }; try wip_nav.refType(.fromInterned(loaded_struct.packed_backing_int_type)); @@ -3984,7 +3997,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(.{ .decl = .decl_union, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_union, }, &nav, file, &decl); break :t true; @@ -3992,7 +4005,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_union.name.toSlice(ip)); + try wip_nav.strp(loaded_union.fqn.toSlice(ip)); break :t loaded_union.field_types.len > 0; }; const union_layout = Type.getUnionLayout(loaded_union, zcu); @@ -4046,7 +4059,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(.{ .decl = .decl_packed_union, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_packed_union, }, &nav, file, &decl); break :t true; @@ -4054,7 +4067,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .packed_union_type else .empty_packed_union_type); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_union.name.toSlice(ip)); + try wip_nav.strp(loaded_union.fqn.toSlice(ip)); break :t loaded_union.field_types.len > 0; }; try wip_nav.refType(.fromInterned(loaded_union.packed_backing_int_type)); @@ -4079,18 +4092,18 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(if (loaded_enum.field_names.len > 0) .{ .decl = .decl_enum, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_enum, } else .{ .decl = .decl_empty_enum, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_empty_enum, }, &nav, file, &decl); } else { const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); try wip_nav.abbrevCode(if (loaded_enum.field_names.len > 0) .enum_type else .empty_enum_type); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_enum.name.toSlice(ip)); + try wip_nav.strp(loaded_enum.fqn.toSlice(ip)); } try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); for (0..loaded_enum.field_names.len) |field_index| { @@ -4102,7 +4115,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co } else { assert(loaded_enum.owner_union != .none); try wip_nav.abbrevCode(if (loaded_enum.field_names.len == 0) .generated_empty_enum_type else .generated_enum_type); - try wip_nav.strp(loaded_enum.name.toSlice(ip)); + try wip_nav.strp(loaded_enum.fqn.toSlice(ip)); try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); for (0..loaded_enum.field_names.len) |field_index| { try wip_nav.abbrevCode(.enum_field); @@ -4121,14 +4134,14 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); try wip_nav.declCommon(.{ .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, + .decl_specification = .decl_specification_const, .decl_instance = .decl_instance_namespace_struct, }, &nav, file, &decl); } else { const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); try wip_nav.abbrevCode(.empty_struct_type); try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_opaque.name.toSlice(ip)); + try wip_nav.strp(loaded_opaque.fqn.toSlice(ip)); } try diw.writeByte(@intFromBool(true)); }, @@ -4614,6 +4627,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co } try diw.writeUleb128(@backingInt(AbbrevCode.null)); }, + .memoized_call => unreachable, // not a value } try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written()); @@ -4632,25 +4646,17 @@ fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum { unpacked, opv_null, err }; } -pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedInst.Index) UpdateError!void { +pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedInst.Index, line: u32) UpdateError!void { const comp = dwarf.bin_file.comp; const io = comp.io; const ip = &zcu.intern_pool; const inst_info = zir_index.resolveFull(ip).?; - assert(inst_info.inst != .main_struct_inst); + if (inst_info.inst == .main_struct_inst) return; const file = zcu.fileByIndex(inst_info.file); - const decl = file.zir.?.getDeclaration(inst_info.inst); - log.debug("updateLineNumber({s}:{d}:{d} %{d} = {s})", .{ - file.sub_file_path, - decl.src_line + 1, - decl.src_column + 1, - @backingInt(inst_info.inst), - file.zir.?.nullTerminatedString(decl.name), - }); var line_buf: [4]u8 = undefined; - std.mem.writeInt(u32, &line_buf, decl.src_line + 1, dwarf.endian); + std.mem.writeInt(u32, &line_buf, line + 1, dwarf.endian); const unit = dwarf.debug_info.section.getUnit(dwarf.getUnitIfExists(file.mod.?) orelse return); const entry = unit.getEntry(dwarf.decls.get(zir_index) orelse return); @@ -4696,7 +4702,7 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (UpdateError || Writer.Err // Update `anyerror` based on the finished global error set. { - const index = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, .anyerror_type); + const index = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), .anyerror_type); const unit, const entry = dwarf.values.items[@backingInt(index)]; var wip_nav: WipNav = .{ .dwarf = dwarf, @@ -4731,7 +4737,7 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (UpdateError || Writer.Err } if (global_error_set_names.len > 0) try diw.writeUleb128(@backingInt(AbbrevCode.null)); try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); } for (dwarf.mods.keys(), dwarf.mods.values()) |mod, *mod_info| { @@ -4783,7 +4789,7 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (UpdateError || Writer.Err .debug_frame => unreachable, .eh_frame => switch (target.cpu.arch) { .x86_64 => { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const Register = @import("../codegen/x86_64/bits.zig").Register; for (dwarf.debug_frame.section.units.items) |*unit| { header_aw.clearRetainingCapacity(); @@ -5131,9 +5137,9 @@ const AbbrevCode = enum { decl_func_generic, decl_extern_nullary_func, decl_extern_func, - generic_decl_var, - generic_decl_const, - generic_decl_func, + decl_specification_var, + decl_specification_const, + decl_specification_func, decl_instance_alias, decl_instance_empty_enum, decl_instance_enum, @@ -5257,7 +5263,7 @@ const AbbrevCode = enum { .{ .accessibility, .data1 }, .{ .name, .strp }, }; - const generic_decl_abbrev_common_attrs = decl_abbrev_common_attrs ++ &[_]Attr{ + const decl_specification_abbrev_common_attrs = decl_abbrev_common_attrs ++ &[_]Attr{ .{ .declaration, .flag_present }, }; const decl_instance_abbrev_common_attrs = &[_]Attr{ @@ -5442,17 +5448,17 @@ const AbbrevCode = enum { .{ .noreturn, .flag }, }, }, - .generic_decl_var = .{ + .decl_specification_var = .{ .tag = .variable, - .attrs = generic_decl_abbrev_common_attrs, + .attrs = decl_specification_abbrev_common_attrs, }, - .generic_decl_const = .{ + .decl_specification_const = .{ .tag = .constant, - .attrs = generic_decl_abbrev_common_attrs, + .attrs = decl_specification_abbrev_common_attrs, }, - .generic_decl_func = .{ + .decl_specification_func = .{ .tag = .subprogram, - .attrs = generic_decl_abbrev_common_attrs, + .attrs = decl_specification_abbrev_common_attrs, }, .decl_instance_alias = .{ .tag = .imported_declaration, @@ -6301,6 +6307,14 @@ fn getFile(dwarf: *Dwarf) ?Io.File { return dwarf.bin_file.file; } +fn constPoolUser(dwarf: *Dwarf) link.ConstPool.User { + return switch (dwarf.bin_file.tag) { + else => unreachable, + .elf => .{ .elf = dwarf }, + .macho => .{ .macho = dwarf }, + }; +} + fn addCommonEntry(dwarf: *Dwarf, unit: Unit.Index) UpdateError!Entry.Index { const entry = try dwarf.debug_aranges.section.getUnit(unit).addEntry(dwarf.gpa); assert(try dwarf.debug_frame.section.getUnit(unit).addEntry(dwarf.gpa) == entry); @@ -6362,14 +6376,14 @@ fn uleb128Bytes(value: anytype) u32 { var buf: [64]u8 = undefined; var dw: Writer.Discarding = .init(&buf); dw.writer.writeUleb128(value) catch unreachable; - return @intCast(dw.count + dw.writer.end); + return @intCast(dw.fullCount()); } fn sleb128Bytes(value: anytype) u32 { var buf: [64]u8 = undefined; var dw: Writer.Discarding = .init(&buf); dw.writer.writeSleb128(value) catch unreachable; - return @intCast(dw.count + dw.writer.end); + return @intCast(dw.fullCount()); } /// overrides `-fno-incremental` for testing incremental debug info until `-fincremental` is functional diff --git a/src/link/Dwarf2.zig b/src/link/Dwarf2.zig new file mode 100644 index 0000000000000000000000000000000000000000..20ab9a14877402921eb62c09c2b0f2a68dbcc541 --- /dev/null +++ b/src/link/Dwarf2.zig @@ -0,0 +1,5247 @@ +lf: *link.File, +format: DW.Format, +endian: std.lang.Endian, +address_size: AddressSize, +const_pool: link.ConstPool, + +units: []Unit, +/// Indices are `link.ConstPool.Index`. +consts: std.ArrayList(Const), +globals: std.array_hash_map.Auto(InternPool.Nav.Index, Global), +funcs: std.array_hash_map.Auto(InternPool.Nav.Index, Func), +decls: std.array_hash_map.Auto(InternPool.TrackedInst.Index, Decl), +pending_decl: struct { di: Decl.Index, instance_val: InternPool.Index }, + +debug_abbrev: Abbrev, +frame: Frame, +debug_info: Info, +debug_line: Line, +debug_line_str: Str, +debug_rnglists: Rnglists, +debug_str: Str, +debug_str_offsets: StrOffsets, + +pub const AddressSize = enum(u8) { @"32" = 4, @"64" = 8, _ }; + +pub const Unit = struct { + alive: bool, + dirs: std.array_hash_map.Auto(Unit.Index, void), + files: std.array_hash_map.Auto(Zcu.File.Index, void), + frame_ni: link.MappedFile.Node.Index.Optional, + cie_ni: link.MappedFile.Node.Index.Optional, + debug_info_ni: link.MappedFile.Node.Index.Optional, + debug_info_header_ni: link.MappedFile.Node.Index.Optional, + debug_info_footer_ni: link.MappedFile.Node.Index.Optional, + debug_line_ni: link.MappedFile.Node.Index.Optional, + debug_line_header_ni: link.MappedFile.Node.Index.Optional, + debug_line_header_changed: bool, + debug_rnglists_ni: link.MappedFile.Node.Index.Optional, + debug_rnglists_offsets_table_offset: usize, + debug_rnglists_end: usize, + + pub const Index = enum(u32) { + _, + + pub fn mod(ui: Unit.Index, dwarf: *Dwarf) *Module { + return dwarf.lf.comp.zcu.?.module_roots.keys()[@backingInt(ui)]; + } + + pub fn get(ui: Unit.Index, dwarf: *Dwarf) *Unit { + return &dwarf.units[@backingInt(ui)]; + } + }; + + pub const DirIndex = enum(u32) { + root = 0, + _, + + fn get(di: DirIndex, unit: *Unit) Unit.Index { + return unit.dirs.keys()[@backingInt(di)]; + } + }; + + pub const FileIndex = enum(u32) { + root = 0, + _, + + fn get(fi: FileIndex, unit: *Unit) Zcu.File.Index { + return unit.files.keys()[@backingInt(fi)]; + } + }; + + fn deinit(unit: *Unit, gpa: std.mem.Allocator) void { + unit.dirs.deinit(gpa); + unit.files.deinit(gpa); + unit.* = undefined; + } + + fn getFile( + unit: *Unit, + gpa: std.mem.Allocator, + ui: Unit.Index, + zfi: Zcu.File.Index, + ) std.mem.Allocator.Error!struct { DirIndex, FileIndex } { + try unit.dirs.ensureUnusedCapacity(gpa, 1); + try unit.files.ensureUnusedCapacity(gpa, 1); + const dir_gop = unit.dirs.getOrPutAssumeCapacity(ui); + const file_gop = unit.files.getOrPutAssumeCapacity(zfi); + if (!dir_gop.found_existing or !file_gop.found_existing) unit.debug_line_header_changed = true; + return .{ @fromBackingInt(@intCast(dir_gop.index)), @fromBackingInt(@intCast(file_gop.index)) }; + } + + pub fn cleanDebugLineHeaderChanged(unit: *Unit) bool { + defer unit.debug_line_header_changed = false; + return unit.debug_line_header_changed; + } +}; + +pub const Const = struct { + debug_info_ni: link.MappedFile.Node.Index.Optional, + + pub fn get(cpi: link.ConstPool.Index, dwarf: *Dwarf) *Const { + return &dwarf.consts.items[@backingInt(cpi)]; + } +}; + +pub const Global = struct { + debug_info_ni: link.MappedFile.Node.Index.Optional, + + pub const Index = enum(u32) { + _, + + pub fn nav(gi: Global.Index, dwarf: *Dwarf) InternPool.Nav.Index { + return dwarf.globals.keys()[@backingInt(gi)]; + } + + pub fn get(gi: Global.Index, dwarf: *Dwarf) *Global { + return &dwarf.globals.values()[@backingInt(gi)]; + } + }; +}; + +pub const Func = struct { + state: State, + fde_ni: link.MappedFile.Node.Index.Optional, + debug_info_ni: link.MappedFile.Node.Index.Optional, + debug_line_ni: link.MappedFile.Node.Index.Optional, + + pub const State = enum { unresolved, resolved }; + + pub const Index = enum(u32) { + _, + + pub fn nav(fi: Func.Index, dwarf: *Dwarf) InternPool.Nav.Index { + return dwarf.funcs.keys()[@backingInt(fi)]; + } + + pub fn get(fi: Func.Index, dwarf: *Dwarf) *Func { + return &dwarf.funcs.values()[@backingInt(fi)]; + } + }; +}; + +pub const Decl = struct { + debug_info_ni: link.MappedFile.Node.Index.Optional, + + pub const Index = enum(u32) { + _, + + pub fn srcInst(di: Decl.Index, dwarf: *Dwarf) InternPool.TrackedInst.Index { + return dwarf.decls.keys()[@backingInt(di)]; + } + + pub fn get(di: Decl.Index, dwarf: *Dwarf) *Decl { + return &dwarf.decls.values()[@backingInt(di)]; + } + }; +}; + +pub const Frame = struct { + header: Header, + + pub const Header = struct { + code_alignment_factor: u32, + data_alignment_factor: i32, + return_address_register: u32, + initial_instructions: []const Cfa, + }; + + pub const Format = std.debug.Dwarf.Unwind.Section; +}; + +pub const Abbrev = struct { + ni: link.MappedFile.Node.Index.Optional, + end: usize, + set: std.enums.EnumSet(AbbrevCode), +}; + +pub const Info = struct {}; + +pub const Line = struct { + header: Header, + + pub const Header = struct { + minimum_instruction_length: u8, + maximum_operations_per_instruction: u8, + default_is_stmt: bool, + line_base: i8, + line_range: u8, + opcode_base: u8, + }; +}; + +pub const Str = struct { + ni: link.MappedFile.Node.Index.Optional, + offset: usize, + map: std.HashMapUnmanaged(usize, void, Context, std.hash_map.default_max_load_percentage), + + fn get( + s: *Str, + gpa: std.mem.Allocator, + mf: *link.MappedFile, + str: []const u8, + ) link.MappedFile.Error!usize { + const ni = s.ni.unwrap().?; + const slice = ni.sliceConst(mf); + const gop = try s.map.getOrPutContextAdapted( + gpa, + str, + Adapter{ .slice = slice }, + .{ .slice = slice }, + ); + if (!gop.found_existing) { + gop.key_ptr.* = s.offset; + try ni.ensureMinimumSize(gpa, mf, s.offset + str.len + 1); + const slice_mut = ni.slice(mf); + @memcpy(slice_mut[s.offset..][0..str.len], str); + s.offset += str.len; + slice_mut[s.offset] = 0; + s.offset += 1; + } + return gop.key_ptr.*; + } + + const Context = struct { + slice: []const u8, + pub fn hash(context: Context, offset: usize) u64 { + return std.hash.Wyhash.hash(0, std.mem.sliceTo(context.slice[offset..], 0)); + } + pub fn eql(_: Context, lhs_offset: usize, rhs_offset: usize) bool { + return lhs_offset == rhs_offset; + } + }; + + const Adapter = struct { + slice: []const u8, + pub fn hash(_: Adapter, key: []const u8) u64 { + return std.hash.Wyhash.hash(0, key); + } + pub fn eql(adapter: Adapter, key: []const u8, rhs_offset: usize) bool { + return std.mem.startsWith(u8, adapter.slice[rhs_offset..], key) and + adapter.slice[rhs_offset + key.len] == 0; + } + }; +}; + +pub const Rnglists = struct { + fn offsetsTableOffset(dwarf: *Dwarf) usize { + return dwarf.unitLengthSize() + 2 + 1 + 1 + 4; + } +}; + +pub const StrOffsets = struct { + ni: link.MappedFile.Node.Index.Optional, + offset: usize, +}; + +pub const SharedSection = enum { debug_abbrev, debug_line_str, debug_str, debug_str_offsets }; + +pub const Loc = union(enum) { + empty, + addr_reloc: link.File.SymbolId, + deref: *const Loc, + constu: u64, + consts: i64, + plus: Bin, + reg: u32, + breg: u32, + push_object_address, + call: struct { + args: []const Loc = &.{}, + node: link.MappedFile.Node.Index, + }, + form_tls_address: *const Loc, + implicit_value: []const u8, + stack_value: *const Loc, + implicit_pointer: struct { + node: link.MappedFile.Node.Index, + offset: i65 = 0, + }, + wasm_ext: union(enum) { + local: u32, + global: u32, + operand_stack: u32, + }, + + pub const Bin = struct { *const Loc, *const Loc }; + + fn getConst(loc: Loc, comptime Int: type) ?Int { + return switch (loc) { + .constu => |constu| std.math.cast(Int, constu), + .consts => |consts| std.math.cast(Int, consts), + else => null, + }; + } + + fn getBaseReg(loc: Loc) ?u32 { + return switch (loc) { + .breg => |breg| breg, + else => null, + }; + } + + fn writeReg(reg: u32, op0: u8, opx: u8, writer: *std.Io.Writer) std.Io.Writer.Error!void { + if (std.math.cast(u5, reg)) |small_reg| { + try writer.writeByte(op0 + small_reg); + } else { + try writer.writeByte(opx); + try writer.writeUleb128(reg); + } + } + + fn write(loc: Loc, writer: union(enum) { + io: *std.Io.Writer, + mf: *link.MappedFile.Node.Writer, + }, dwarf: *Dwarf) link.EmitError!void { + const w = switch (writer) { + .io => |w| w, + .mf => |nw| &nw.interface, + }; + switch (loc) { + .empty => {}, + .addr_reloc => |si| { + try w.writeByte(DW.OP.addr); + switch (writer) { + .io => try dwarf.addrPlaceholder(w), + .mf => |nw| try dwarf.addrSym(nw, si, 0), + } + }, + .deref => |addr| { + try addr.write(writer, dwarf); + try w.writeByte(DW.OP.deref); + }, + .constu => |constu| if (std.math.cast(u5, constu)) |lit| { + try w.writeByte(@as(u8, DW.OP.lit0) + lit); + } else if (std.math.cast(u8, constu)) |const1u| { + try w.writeAll(&.{ DW.OP.const1u, const1u }); + } else if (std.math.cast(u16, constu)) |const2u| { + try w.writeByte(DW.OP.const2u); + try w.writeInt(u16, const2u, dwarf.endian); + } else if (std.math.cast(u21, constu)) |const3u| { + try w.writeByte(DW.OP.constu); + try w.writeUleb128(const3u); + } else if (std.math.cast(u32, constu)) |const4u| { + try w.writeByte(DW.OP.const4u); + try w.writeInt(u32, const4u, dwarf.endian); + } else if (std.math.cast(u49, constu)) |const7u| { + try w.writeByte(DW.OP.constu); + try w.writeUleb128(const7u); + } else { + try w.writeByte(DW.OP.const8u); + try w.writeInt(u64, constu, dwarf.endian); + }, + .consts => |consts| if (std.math.cast(i8, consts)) |const1s| { + try w.writeAll(&.{ DW.OP.const1s, @bitCast(const1s) }); + } else if (std.math.cast(i16, consts)) |const2s| { + try w.writeByte(DW.OP.const2s); + try w.writeInt(i16, const2s, dwarf.endian); + } else if (std.math.cast(i21, consts)) |const3s| { + try w.writeByte(DW.OP.consts); + try w.writeSleb128(const3s); + } else if (std.math.cast(i32, consts)) |const4s| { + try w.writeByte(DW.OP.const4s); + try w.writeInt(i32, const4s, dwarf.endian); + } else if (std.math.cast(i49, consts)) |const7s| { + try w.writeByte(DW.OP.consts); + try w.writeSleb128(const7s); + } else { + try w.writeByte(DW.OP.const8s); + try w.writeInt(i64, consts, dwarf.endian); + }, + .plus => |plus| done: { + if (plus[0].getConst(u0)) |_| { + try plus[1].write(writer, dwarf); + break :done; + } + if (plus[1].getConst(u0)) |_| { + try plus[0].write(writer, dwarf); + break :done; + } + if (plus[0].getBaseReg()) |breg| { + if (plus[1].getConst(i65)) |offset| { + try writeReg(breg, DW.OP.breg0, DW.OP.bregx, w); + try w.writeSleb128(offset); + break :done; + } + } + if (plus[1].getBaseReg()) |breg| { + if (plus[0].getConst(i65)) |offset| { + try writeReg(breg, DW.OP.breg0, DW.OP.bregx, w); + try w.writeSleb128(offset); + break :done; + } + } + if (plus[0].getConst(u64)) |uconst| { + try plus[1].write(writer, dwarf); + try w.writeByte(DW.OP.plus_uconst); + try w.writeUleb128(uconst); + break :done; + } + if (plus[1].getConst(u64)) |uconst| { + try plus[0].write(writer, dwarf); + try w.writeByte(DW.OP.plus_uconst); + try w.writeUleb128(uconst); + break :done; + } + try plus[0].write(writer, dwarf); + try plus[1].write(writer, dwarf); + try w.writeByte(DW.OP.plus); + }, + .reg => |reg| try writeReg(reg, DW.OP.reg0, DW.OP.regx, w), + .breg => |breg| { + try writeReg(breg, DW.OP.breg0, DW.OP.bregx, w); + try w.writeSleb128(0); + }, + .push_object_address => try w.writeByte(DW.OP.push_object_address), + .call => |call| { + for (call.args) |arg| try arg.write(writer, dwarf); + try w.writeByte(DW.OP.call_ref); + switch (writer) { + .io => try dwarf.secOffsetPlaceholder(w), + .mf => |nw| try dwarf.secOffset(nw, call.node, 0), + } + }, + .form_tls_address => |addr| { + try addr.write(writer, dwarf); + try w.writeByte(DW.OP.form_tls_address); + }, + .implicit_value => |value| { + try w.writeByte(DW.OP.implicit_value); + try w.writeUleb128(value.len); + try w.writeAll(value); + }, + .stack_value => |value| { + try value.write(writer, dwarf); + try w.writeByte(DW.OP.stack_value); + }, + .implicit_pointer => |implicit_pointer| { + try w.writeByte(DW.OP.implicit_pointer); + switch (writer) { + .io => try dwarf.secOffsetPlaceholder(w), + .mf => |nw| try dwarf.secOffset(nw, implicit_pointer.node, 0), + } + try w.writeSleb128(implicit_pointer.offset); + }, + .wasm_ext => |wasm_ext| { + try w.writeByte(DW.OP.WASM_location); + switch (wasm_ext) { + .local => |local| { + try w.writeByte(DW.OP.WASM_local); + try w.writeUleb128(local); + }, + .global => |global| if (std.math.cast(u21, global)) |global_u21| { + try w.writeByte(DW.OP.WASM_global); + try w.writeUleb128(global_u21); + } else { + try w.writeByte(DW.OP.WASM_global_u32); + try w.writeInt(u32, global, dwarf.endian); + }, + .operand_stack => |operand_stack| { + try w.writeByte(DW.OP.WASM_operand_stack); + try w.writeUleb128(operand_stack); + }, + } + }, + } + } +}; + +pub const Cfa = union(enum) { + nop, + advance_loc: u32, + offset: RegOff, + rel_offset: RegOff, + restore: u32, + undefined: u32, + same_value: u32, + register: [2]u32, + remember_state, + restore_state, + def_cfa: RegOff, + def_cfa_register: u32, + def_cfa_offset: i64, + adjust_cfa_offset: i64, + def_cfa_expression: Loc, + expression: RegExpr, + val_offset: RegOff, + val_expression: RegExpr, + escape: []const u8, + + const RegOff = struct { reg: u32, off: i64 }; + const RegExpr = struct { reg: u32, expr: Loc }; + + fn write(cfa: Cfa, wip_nav: *WipNav) link.EmitError!void { + const df_nw = &wip_nav.fde_writer; + const df_w = &df_nw.interface; + switch (cfa) { + .nop => try df_w.writeByte(DW.CFA.nop), + .advance_loc => |loc| { + const delta = + @divExact(loc - wip_nav.cfi.loc, wip_nav.dwarf.frame.header.code_alignment_factor); + if (delta == 0) {} else if (std.math.cast(u6, delta)) |small_delta| + try df_w.writeByte(@as(u8, DW.CFA.advance_loc) + small_delta) + else if (std.math.cast(u8, delta)) |ubyte_delta| + try df_w.writeAll(&.{ DW.CFA.advance_loc1, ubyte_delta }) + else if (std.math.cast(u16, delta)) |uhalf_delta| { + try df_w.writeByte(DW.CFA.advance_loc2); + try df_w.writeInt(u16, uhalf_delta, wip_nav.dwarf.endian); + } else if (std.math.cast(u32, delta)) |uword_delta| { + try df_w.writeByte(DW.CFA.advance_loc4); + try df_w.writeInt(u32, uword_delta, wip_nav.dwarf.endian); + } + wip_nav.cfi.loc = loc; + }, + .offset, .rel_offset => |reg_off| { + const factored_off = @divExact(reg_off.off - switch (cfa) { + else => unreachable, + .offset => 0, + .rel_offset => wip_nav.cfi.cfa.off, + }, wip_nav.dwarf.frame.header.data_alignment_factor); + if (std.math.cast(u63, factored_off)) |unsigned_off| { + if (std.math.cast(u6, reg_off.reg)) |small_reg| { + try df_w.writeByte(@as(u8, DW.CFA.offset) + small_reg); + } else { + try df_w.writeByte(DW.CFA.offset_extended); + try df_w.writeUleb128(reg_off.reg); + } + try df_w.writeUleb128(unsigned_off); + } else { + try df_w.writeByte(DW.CFA.offset_extended_sf); + try df_w.writeUleb128(reg_off.reg); + try df_w.writeSleb128(factored_off); + } + }, + .restore => |reg| if (std.math.cast(u6, reg)) |small_reg| + try df_w.writeByte(@as(u8, DW.CFA.restore) + small_reg) + else { + try df_w.writeByte(DW.CFA.restore_extended); + try df_w.writeUleb128(reg); + }, + .undefined => |reg| { + try df_w.writeByte(DW.CFA.undefined); + try df_w.writeUleb128(reg); + }, + .same_value => |reg| { + try df_w.writeByte(DW.CFA.same_value); + try df_w.writeUleb128(reg); + }, + .register => |regs| if (regs[0] != regs[1]) { + try df_w.writeByte(DW.CFA.register); + for (regs) |reg| try df_w.writeUleb128(reg); + } else { + try df_w.writeByte(DW.CFA.same_value); + try df_w.writeUleb128(regs[0]); + }, + .remember_state => try df_w.writeByte(DW.CFA.remember_state), + .restore_state => try df_w.writeByte(DW.CFA.restore_state), + .def_cfa, .def_cfa_register, .def_cfa_offset, .adjust_cfa_offset => { + const reg_off: RegOff = switch (cfa) { + else => unreachable, + .def_cfa => |reg_off| reg_off, + .def_cfa_register => |reg| .{ .reg = reg, .off = wip_nav.cfi.cfa.off }, + .def_cfa_offset => |off| .{ .reg = wip_nav.cfi.cfa.reg, .off = off }, + .adjust_cfa_offset => |off| .{ + .reg = wip_nav.cfi.cfa.reg, + .off = wip_nav.cfi.cfa.off + off, + }, + }; + const changed_reg = reg_off.reg != wip_nav.cfi.cfa.reg; + const unsigned_off = std.math.cast(u63, reg_off.off); + if (reg_off.off == wip_nav.cfi.cfa.off) { + if (changed_reg) { + try df_w.writeByte(DW.CFA.def_cfa_register); + try df_w.writeUleb128(reg_off.reg); + } + } else if (switch (wip_nav.dwarf.frame.header.data_alignment_factor) { + 0 => unreachable, + 1 => unsigned_off != null, + else => |data_alignment_factor| @rem(reg_off.off, data_alignment_factor) != 0, + }) { + try df_w.writeByte(if (changed_reg) DW.CFA.def_cfa else DW.CFA.def_cfa_offset); + if (changed_reg) try df_w.writeUleb128(reg_off.reg); + try df_w.writeUleb128(unsigned_off.?); + } else { + try df_w.writeByte(if (changed_reg) DW.CFA.def_cfa_sf else DW.CFA.def_cfa_offset_sf); + if (changed_reg) try df_w.writeUleb128(reg_off.reg); + try df_w.writeSleb128( + @divExact(reg_off.off, wip_nav.dwarf.frame.header.data_alignment_factor), + ); + } + wip_nav.cfi.cfa = reg_off; + }, + .def_cfa_expression => |expr| { + try df_w.writeByte(DW.CFA.def_cfa_expression); + try wip_nav.dwarf.exprLoc(df_nw, expr); + }, + .expression => |reg_expr| { + try df_w.writeByte(DW.CFA.expression); + try df_w.writeUleb128(reg_expr.reg); + try wip_nav.dwarf.exprLoc(df_nw, reg_expr.expr); + }, + .val_offset => |reg_off| { + const factored_off = + @divExact(reg_off.off, wip_nav.dwarf.frame.header.data_alignment_factor); + if (std.math.cast(u63, factored_off)) |unsigned_off| { + try df_w.writeByte(DW.CFA.val_offset); + try df_w.writeUleb128(reg_off.reg); + try df_w.writeUleb128(unsigned_off); + } else { + try df_w.writeByte(DW.CFA.val_offset_sf); + try df_w.writeUleb128(reg_off.reg); + try df_w.writeSleb128(factored_off); + } + }, + .val_expression => |reg_expr| { + try df_w.writeByte(DW.CFA.val_expression); + try df_w.writeUleb128(reg_expr.reg); + try wip_nav.dwarf.exprLoc(df_nw, reg_expr.expr); + }, + .escape => |bytes| try df_w.writeAll(bytes), + } + } +}; + +pub const WipNav = struct { + dwarf: *Dwarf, + unit: Unit.Index, + func: InternPool.Index, + func_si: link.File.SymbolId, + cfi: struct { + loc: u32, + cfa: Cfa.RegOff, + }, + frame_format: Frame.Format, + fde_writer: link.MappedFile.Node.Writer, + frame_func_length: struct { offset: usize, size: AddressSize }, + + pub const Debug = struct { + wip_nav: WipNav, + pt: Zcu.PerThread, + any_children: bool, + blocks: std.ArrayList(struct { + abbrev_code: u32, + low_pc_off: usize, + high_pc: u32, + }), + info_writer: link.MappedFile.Node.Writer, + info_func_length_offset: usize, + line_writer: link.MappedFile.Node.Writer, + + pub fn deinit(debug: *Debug) void { + const gpa = debug.pt.zcu.gpa; + debug.line_writer.deinit(); + debug.info_writer.deinit(); + debug.blocks.deinit(gpa); + debug.wip_nav.deinit(); + debug.* = undefined; + } + + pub fn genDebugFrame(debug: *Debug, loc: u32, cfa: Cfa) link.Error!void { + return debug.wip_nav.genDebugFrame(loc, cfa); + } + + pub fn startFuncDebugInfo(debug: *Debug) link.Error!void { + assert(debug.wip_nav.func != .none); + debug.startFuncDebugInfoInner() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| return e, + }; + } + fn startFuncDebugInfoInner(debug: *Debug) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const pt = debug.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const func = zcu.funcInfo(debug.wip_nav.func); + const nav = ip.getNav(func.owner_nav); + const func_type = ip.indexToKey(func.ty).func_type; + const inst_info = nav.srcInst(ip).resolveFull(ip).?; + const zf = zcu.fileByIndex(inst_info.file); + const target = &zf.mod.?.resolved_target.result; + const decl = zf.zir.?.getDeclaration(inst_info.inst); + const di_nw = &debug.info_writer; + const di_w = &di_nw.interface; + try dwarf.abbrevCode(di_nw, .decl_func); + try dwarf.refType(pt, di_nw, .fromInterned(ip.namespacePtr(switch (func.generic_owner) { + .none => nav, + else => |generic_owner| ip.getNav(zcu.funcInfo(generic_owner).owner_nav), + }.analysis.?.namespace).owner_type)); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, nav.name.toSlice(ip)); + try dwarf.strp(&dwarf.debug_str, di_nw, switch (decl.linkage) { + .normal => nav.fqn, + .@"extern", .@"export" => nav.name, + }.toSlice(ip)); + try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type)); + try dwarf.addrSym(di_nw, debug.wip_nav.func_si, 0); + debug.info_func_length_offset = di_w.end; + try di_w.writeInt(u32, undefined, dwarf.endian); + try di_w.writeUleb128( + target_info.minFunctionAlignment(target).max(nav.resolved.?.@"align").toByteUnits().?, + ); + try di_w.writeByte(@intFromBool(decl.linkage != .normal)); + try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu))); + } + + pub fn startDebugLine(debug: *Debug) link.Error!void { + assert(debug.wip_nav.func != .none); + debug.startDebugLineInner() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + else => |e| return e, + }; + } + fn startDebugLineInner(debug: *Debug) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const zcu = debug.pt.zcu; + const ip = &zcu.intern_pool; + const func = zcu.funcInfo(debug.wip_nav.func); + const inst_info = ip.getNav(func.owner_nav).srcInst(ip).resolveFull(ip).?; + const zf = zcu.fileByIndex(inst_info.file); + const decl = zf.zir.?.getDeclaration(inst_info.inst); + const dl_nw = &debug.line_writer; + const dl_w = &dl_nw.interface; + try dl_w.writeByte(DW.LNS.extended_op); + if (zcu.comp.config.incremental) { + try dl_w.writeUleb128(1 + dwarf.secOffsetSize()); + try dl_w.writeByte(DW.LNE.ZIG_set_decl); + try dwarf.secOffset(dl_nw, debug.info_writer.ni, 0); + + try dl_w.writeByte(DW.LNS.set_column); + try dl_w.writeUleb128(func.lbrace_column + 1); + + try debug.advanceLineAndPc(func.lbrace_line, 0, false); + } else { + try dl_w.writeUleb128(1 + @backingInt(dwarf.address_size)); + try dl_w.writeByte(DW.LNE.set_address); + try dwarf.addrSym(dl_nw, debug.wip_nav.func_si, 0); + + const unit = dwarf.getUnit(zf.mod.?); + _, const fi = try unit.get(dwarf).getFile(zcu.gpa, unit, inst_info.file); + try dl_w.writeByte(DW.LNS.set_file); + try dl_w.writeUleb128(@backingInt(fi)); + + try dl_w.writeByte(DW.LNS.set_column); + try dl_w.writeUleb128(func.lbrace_column + 1); + + try debug.advanceLineAndPc(decl.src_line + func.lbrace_line, 0, false); + } + } + + pub fn finishFunc(debug: *Debug, func_length: u64) link.Error!void { + assert(debug.wip_nav.func != .none); + const di_nw = &debug.info_writer; + debug.finishDebugInfo(func_length) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(di_nw), + else => |e| return e, + }; + debug.finishDebugLine() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(di_nw), + else => |e| return e, + }; + } + fn finishDebugInfo(debug: *Debug, func_length: u64) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const di_w = &debug.info_writer.interface; + std.mem.writeInt( + u32, + di_w.buffered()[debug.info_func_length_offset..][0..4], + @intCast(func_length), + dwarf.endian, + ); + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + try dwarf.genDebugInfoPadding(di_w, di_w.unusedCapacityLen()); + } + fn finishDebugLine(debug: *Debug) link.EmitError!void { + const dl_w = &debug.line_writer.interface; + try genDebugLinePadding(dl_w, dl_w.unusedCapacityLen()); + } + + pub const LocalVarTag = enum { arg, local_var }; + pub fn genLocalVarDebugInfo( + debug: *Debug, + tag: LocalVarTag, + opt_name: ?[]const u8, + ty: Type, + loc: Loc, + ) link.Error!void { + return debug.genLocalVarDebugInfoInner(tag, opt_name, ty, loc) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn genLocalVarDebugInfoInner( + debug: *Debug, + tag: LocalVarTag, + opt_name: ?[]const u8, + ty: Type, + loc: Loc, + ) link.EmitError!void { + assert(debug.wip_nav.func != .none); + const dwarf = debug.wip_nav.dwarf; + const di_nw = &debug.info_writer; + try dwarf.abbrevCode(di_nw, switch (tag) { + .arg => if (opt_name) |_| .arg else .unnamed_arg, + .local_var => if (opt_name) |_| .local_var else unreachable, + }); + if (opt_name) |name| try dwarf.strp(&dwarf.debug_str, di_nw, name); + try dwarf.refType(debug.pt, di_nw, ty); + try dwarf.exprLoc(di_nw, loc); + debug.any_children = true; + } + + pub const LocalConstTag = enum { comptime_arg, local_const }; + pub fn genLocalConstDebugInfo( + debug: *Debug, + tag: LocalConstTag, + opt_name: ?[]const u8, + val: Value, + ) link.Error!void { + return debug.genLocalConstDebugInfoInner(tag, opt_name, val) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn genLocalConstDebugInfoInner( + debug: *Debug, + tag: LocalConstTag, + opt_name: ?[]const u8, + val: Value, + ) link.EmitError!void { + assert(debug.wip_nav.func != .none); + const dwarf = debug.wip_nav.dwarf; + const pt = debug.pt; + const zcu = debug.pt.zcu; + const ty = val.typeOf(zcu); + const ty_class = ty.classify(zcu); + const di_nw = &debug.info_writer; + try dwarf.abbrevCode(di_nw, switch (tag) { + .comptime_arg => if (opt_name) |_| switch (ty_class) { + .no_possible_value => unreachable, + .one_possible_value => .comptime_arg, + .runtime => .comptime_arg_fully_runtime, + .partially_comptime => .comptime_arg_partially_comptime, + .fully_comptime => .comptime_arg_fully_comptime, + } else switch (ty_class) { + .no_possible_value => unreachable, + .one_possible_value => .unnamed_comptime_arg, + .runtime => .unnamed_comptime_arg_fully_runtime, + .partially_comptime => .unnamed_comptime_arg_partially_comptime, + .fully_comptime => .unnamed_comptime_arg_fully_comptime, + }, + .local_const => if (opt_name) |_| switch (ty_class) { + .no_possible_value => unreachable, + .one_possible_value => .local_const, + .runtime => .local_const_fully_runtime, + .partially_comptime => .local_const_partially_comptime, + .fully_comptime => .local_const_fully_comptime, + } else unreachable, + }); + if (opt_name) |name| try dwarf.strp(&dwarf.debug_str, di_nw, name); + try dwarf.refType(pt, di_nw, ty); + if (ty_class.hasRuntimeBits()) try dwarf.blockConst(pt, di_nw, val); + if (ty_class.comptimeOnly()) try dwarf.refConst(pt, di_nw, val); + debug.any_children = true; + } + + pub fn genVarArgsDebugInfo(debug: *Debug) link.Error!void { + return debug.genVarArgsDebugInfoInner() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn genVarArgsDebugInfoInner(debug: *Debug) link.EmitError!void { + assert(debug.wip_nav.func != .none); + try debug.wip_nav.dwarf.abbrevCode(&debug.info_writer, .is_var_args); + debug.any_children = true; + } + + pub fn advanceLineAndPc( + debug: *Debug, + delta_line: i33, + delta_pc: u64, + end: bool, + ) link.Error!void { + return debug.advanceLineAndPcInner(delta_line, delta_pc, end) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + }; + } + fn advanceLineAndPcInner( + debug: *Debug, + delta_line: i33, + delta_pc: u64, + end: bool, + ) std.Io.Writer.Error!void { + const dl_w = &debug.line_writer.interface; + + const header = debug.wip_nav.dwarf.debug_line.header; + assert(header.maximum_operations_per_instruction == 1); + const delta_op: u64 = 0; + + const remaining_delta_line: i9 = @intCast(if (delta_line < header.line_base or + delta_line - header.line_base >= header.line_range) + remaining: { + assert(delta_line != 0); + try dl_w.writeByte(DW.LNS.advance_line); + try dl_w.writeSleb128(delta_line); + break :remaining 0; + } else delta_line); + + const op_advance = @divExact(delta_pc, header.minimum_instruction_length) * + header.maximum_operations_per_instruction + delta_op; + const max_op_advance: u9 = (std.math.maxInt(u8) - header.opcode_base) / header.line_range; + const remaining_op_advance: u8 = @intCast(if (end or + op_advance >= 2 * max_op_advance) + remaining: { + if (op_advance == max_op_advance) { + try dl_w.writeByte(DW.LNS.const_add_pc); + } else if (op_advance != 0) { + try dl_w.writeByte(DW.LNS.advance_pc); + try dl_w.writeUleb128(op_advance); + } else assert(end); + break :remaining 0; + } else if (op_advance >= max_op_advance) remaining: { + try dl_w.writeByte(DW.LNS.const_add_pc); + break :remaining op_advance - max_op_advance; + } else op_advance); + + if (remaining_delta_line != 0 or remaining_op_advance != 0) { + assert(!end); + try dl_w.writeByte(@intCast((remaining_delta_line - header.line_base) + + (header.line_range * remaining_op_advance) + header.opcode_base)); + } else if (end) { + try dl_w.writeByte(DW.LNS.extended_op); + try dl_w.writeUleb128(1); + try dl_w.writeByte(DW.LNE.end_sequence); + } else try dl_w.writeByte(DW.LNS.copy); + } + + pub fn setColumn(debug: *Debug, column: u32) link.Error!void { + return debug.setColumnInner(column) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + }; + } + fn setColumnInner(debug: *Debug, column: u32) std.Io.Writer.Error!void { + const dl_w = &debug.line_writer.interface; + try dl_w.writeByte(DW.LNS.set_column); + try dl_w.writeUleb128(column + 1); + } + + pub fn negateStmt(debug: *Debug) link.Error!void { + return debug.negateStmtInner() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + }; + } + fn negateStmtInner(debug: *Debug) std.Io.Writer.Error!void { + try debug.line_writer.interface.writeByte(DW.LNS.negate_stmt); + } + + pub fn setPrologueEnd(debug: *Debug) link.Error!void { + return debug.setPrologueEndInner() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + }; + } + fn setPrologueEndInner(debug: *Debug) std.Io.Writer.Error!void { + try debug.line_writer.interface.writeByte(DW.LNS.set_prologue_end); + } + + pub fn setEpilogueBegin(debug: *Debug) link.Error!void { + return debug.setEpilogueBeginInner() catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + }; + } + fn setEpilogueBeginInner(debug: *Debug) std.Io.Writer.Error!void { + try debug.line_writer.interface.writeByte(DW.LNS.set_epilogue_begin); + } + + pub fn enterBlock(debug: *Debug, code_off: usize) link.Error!void { + return debug.enterBlockInner(code_off) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn enterBlockInner(debug: *Debug, code_off: usize) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const block = try debug.blocks.addOne(dwarf.lf.comp.gpa); + + const di_nw = &debug.info_writer; + const di_w = &di_nw.interface; + block.abbrev_code = @intCast(di_w.end); + try dwarf.abbrevCode(di_nw, .block); + block.low_pc_off = code_off; + try dwarf.addrSym(di_nw, debug.wip_nav.func_si, code_off); + block.high_pc = @intCast(di_w.end); + try di_w.writeInt(u32, 0, dwarf.endian); + debug.any_children = false; + } + + pub fn leaveBlock(debug: *Debug, code_off: usize) link.Error!void { + return debug.leaveBlockInner(code_off) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn leaveBlockInner(debug: *Debug, code_off: usize) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const block_size = comptime uleb128Size(@backingInt(AbbrevCode.block)); + const block = debug.blocks.pop().?; + + const di_nw = &debug.info_writer; + const di_w = &di_nw.interface; + if (debug.any_children) + try di_w.writeUleb128(@backingInt(AbbrevCode.null)) + else + std.leb.writeUnsignedFixed( + block_size, + di_w.buffered()[block.abbrev_code..][0..block_size], + @intCast(try dwarf.refAbbrevCode(di_nw.mf, .empty_block)), + ); + std.mem.writeInt( + u32, + di_nw.interface.buffered()[block.high_pc..][0..4], + @intCast(code_off - block.low_pc_off), + dwarf.endian, + ); + debug.any_children = true; + } + + pub fn enterInlineFunc( + debug: *Debug, + func: InternPool.Index, + code_off: usize, + line: u32, + column: u32, + ) link.Error!void { + return debug.enterInlineFuncInner(func, code_off, line, column) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn enterInlineFuncInner( + debug: *Debug, + func: InternPool.Index, + code_off: usize, + line: u32, + column: u32, + ) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const zcu = debug.pt.zcu; + const block = try debug.blocks.addOne(zcu.gpa); + + const di_nw = &debug.info_writer; + const di_w = &di_nw.interface; + block.abbrev_code = @intCast(di_w.end); + try dwarf.abbrevCode(di_nw, .inlined_func); + try debug.refFunc(func); + try di_w.writeUleb128((if (zcu.comp.config.incremental) + 0 + else + zcu.navSrcLine(zcu.funcInfo(debug.wip_nav.func).owner_nav) + 1) + line); + try di_w.writeUleb128(column + 1); + block.low_pc_off = code_off; + try dwarf.addrSym(di_nw, debug.wip_nav.func_si, code_off); + block.high_pc = @intCast(di_w.end); + try di_w.writeInt(u32, 0, dwarf.endian); + try debug.setInlineFunc(func); + debug.any_children = false; + } + + pub fn leaveInlineFunc(debug: *Debug, func: InternPool.Index, code_off: usize) link.Error!void { + return debug.leaveInlineFuncInner(func, code_off) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.info_writer), + else => |e| e, + }; + } + fn leaveInlineFuncInner( + debug: *Debug, + func: InternPool.Index, + code_off: usize, + ) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const inlined_func_size = comptime uleb128Size(@backingInt(AbbrevCode.inlined_func)); + const block = debug.blocks.pop().?; + + const di_nw = &debug.info_writer; + const di_w = &di_nw.interface; + if (debug.any_children) + try di_w.writeUleb128(@backingInt(AbbrevCode.null)) + else + std.leb.writeUnsignedFixed( + inlined_func_size, + di_w.buffered()[block.abbrev_code..][0..inlined_func_size], + @intCast(try dwarf.refAbbrevCode(di_nw.mf, .empty_inlined_func)), + ); + std.mem.writeInt( + u32, + di_w.buffered()[block.high_pc..][0..4], + @intCast(code_off - block.low_pc_off), + dwarf.endian, + ); + try debug.setInlineFunc(func); + debug.any_children = true; + } + + pub fn setInlineFunc(debug: *Debug, func: InternPool.Index) link.Error!void { + return debug.setInlineFuncInner(func) catch |err| switch (err) { + error.WriteFailed => return debug.wip_nav.dwarf.reportWriteError(&debug.line_writer), + else => |e| e, + }; + } + fn setInlineFuncInner(debug: *Debug, func: InternPool.Index) link.EmitError!void { + const zcu = debug.pt.zcu; + const ip = &zcu.intern_pool; + const dwarf = debug.wip_nav.dwarf; + if (debug.wip_nav.func == func) return; + + const dl_nw = &debug.line_writer; + const dl_w = &dl_nw.interface; + const new_owner_nav = zcu.funcInfo(func).owner_nav; + if (zcu.comp.config.incremental) { + const new_func = try dwarf.getFunc(new_owner_nav); + try dl_w.writeByte(DW.LNS.extended_op); + try dl_w.writeUleb128(1 + dwarf.secOffsetSize()); + try dl_w.writeByte(DW.LNE.ZIG_set_decl); + try dwarf.secOffset(dl_nw, new_func.get(dwarf).debug_info_ni.unwrap().?, 0); + return; + } + + const old_owner_nav = zcu.funcInfo(debug.wip_nav.func).owner_nav; + const old_inst_info = ip.getNav(old_owner_nav).srcInst(ip).resolveFull(ip).?; + const old_zf = zcu.fileByIndex(old_inst_info.file); + const new_inst_info = ip.getNav(new_owner_nav).srcInst(ip).resolveFull(ip).?; + const new_zf = zcu.fileByIndex(new_inst_info.file); + if (old_inst_info.file != new_inst_info.file) { + const new_ui = dwarf.getUnit(new_zf.mod.?); + _, const new_fi = + try debug.wip_nav.unit.get(dwarf).getFile(zcu.gpa, new_ui, new_inst_info.file); + + try dl_w.writeByte(DW.LNS.set_file); + try dl_w.writeUleb128(@backingInt(new_fi)); + } + + const old_src_line: i33 = old_zf.zir.?.getDeclaration(old_inst_info.inst).src_line; + const new_src_line: i33 = new_zf.zir.?.getDeclaration(new_inst_info.inst).src_line; + if (new_src_line != old_src_line) { + try dl_w.writeByte(DW.LNS.advance_line); + try dl_w.writeSleb128(new_src_line - old_src_line); + } + + debug.wip_nav.func = func; + } + + fn refFunc(debug: *Debug, func: InternPool.Index) link.EmitError!void { + const dwarf = debug.wip_nav.dwarf; + const fi = try dwarf.getFunc(debug.pt.zcu.funcInfo(func).owner_nav); + try debug.wip_nav.dwarf.secOffset( + &debug.info_writer, + fi.get(dwarf).debug_info_ni.unwrap().?, + 0, + ); + } + }; + + pub fn deinit(wip_nav: *WipNav) void { + wip_nav.fde_writer.deinit(); + wip_nav.* = undefined; + } + + pub fn genDebugFrameHeader(wip_nav: *WipNav) link.Error!void { + wip_nav.genDebugFrameHeaderInner() catch |err| switch (err) { + error.WriteFailed => return wip_nav.dwarf.reportWriteError(&wip_nav.fde_writer), + else => |e| return e, + }; + } + fn genDebugFrameHeaderInner(wip_nav: *WipNav) link.EmitError!void { + assert(wip_nav.func != .none); + const dwarf = wip_nav.dwarf; + const df_nw = &wip_nav.fde_writer; + const df_w = &df_nw.interface; + try dwarf.genUnitLength(df_w); + switch (wip_nav.frame_format) { + .eh_frame => { + try df_w.writeInt(u32, undefined, dwarf.endian); + { + const offset = df_w.end; + try df_w.writeInt(u32, 0, dwarf.endian); + if (dwarf.lf.cast(.elf2)) |elf| try elf.addReloc( + @bitCast(df_nw.ni), + offset, + wip_nav.func_si, + 0, + .rel32(elf), + ) else unreachable; + } + wip_nav.frame_func_length = .{ .offset = df_w.end, .size = .@"32" }; + try df_w.writeInt(u32, undefined, dwarf.endian); + try df_w.writeUleb128(0); + }, + .debug_frame => { + try dwarf.secOffset(df_nw, wip_nav.unit.get(dwarf).cie_ni.unwrap().?, 0); + try dwarf.addrSym(df_nw, wip_nav.func_si, 0); + wip_nav.frame_func_length = .{ .offset = df_w.end, .size = dwarf.address_size }; + try dwarf.addrPlaceholder(df_w); + }, + } + } + + pub fn genDebugFrame(wip_nav: *WipNav, loc: u32, cfa: Cfa) link.Error!void { + return wip_nav.genDebugFrameInner(loc, cfa) catch |err| switch (err) { + error.WriteFailed => return wip_nav.dwarf.reportWriteError(&wip_nav.fde_writer), + else => |e| return e, + }; + } + fn genDebugFrameInner(wip_nav: *WipNav, loc: u32, cfa: Cfa) link.EmitError!void { + assert(wip_nav.func != .none); + const loc_cfa: Cfa = .{ .advance_loc = loc }; + try loc_cfa.write(wip_nav); + try cfa.write(wip_nav); + } + + pub fn finishDebugFrameFde(wip_nav: *WipNav, func_length: u64) void { + const dwarf = wip_nav.dwarf; + const df_w = &wip_nav.fde_writer.interface; + switch (wip_nav.frame_func_length.size) { + _ => unreachable, + .@"32" => std.mem.writeInt( + u32, + df_w.buffered()[wip_nav.frame_func_length.offset..][0..4], + @intCast(func_length), + dwarf.endian, + ), + .@"64" => std.mem.writeInt( + u64, + df_w.buffered()[wip_nav.frame_func_length.offset..][0..8], + func_length, + dwarf.endian, + ), + } + @memset(df_w.unusedCapacitySlice(), DW.CFA.nop); + } +}; + +pub fn init(lf: *link.File, format: DW.Format) Dwarf { + const target = &lf.comp.root_mod.resolved_target.result; + return .{ + .lf = lf, + .format = format, + .address_size = switch (target.ptrBitWidth()) { + 0...32 => .@"32", + 33...64 => .@"64", + else => unreachable, + }, + .endian = target.cpu.arch.endian(), + .const_pool = .empty, + + .units = &.{}, + .consts = .empty, + .globals = .empty, + .funcs = .empty, + .decls = .empty, + .pending_decl = .{ .di = undefined, .instance_val = .none }, + + .debug_abbrev = .{ + .ni = .none, + .end = 0, + .set = .empty, + }, + .frame = .{ + .header = if (target.cpu.arch == .x86_64 and target.ofmt == .elf) header: { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); + const Register = @import("../codegen/x86_64/bits.zig").Register; + break :header comptime .{ + .code_alignment_factor = 1, + .data_alignment_factor = -8, + .return_address_register = Register.rip.dwarfNum(), + .initial_instructions = &.{ + .{ .def_cfa = .{ .reg = Register.rsp.dwarfNum(), .off = 8 } }, + .{ .offset = .{ .reg = Register.rip.dwarfNum(), .off = -8 } }, + }, + }; + } else .{ + .code_alignment_factor = undefined, + .data_alignment_factor = undefined, + .return_address_register = undefined, + .initial_instructions = &.{}, + }, + }, + .debug_info = .{}, + .debug_line = .{ + .header = switch (target.cpu.arch) { + .x86_64, .aarch64 => .{ + .minimum_instruction_length = 1, + .maximum_operations_per_instruction = 1, + .default_is_stmt = true, + .line_base = -5, + .line_range = 14, + .opcode_base = DW.LNS.set_isa + 1, + }, + else => .{ + .minimum_instruction_length = 1, + .maximum_operations_per_instruction = 1, + .default_is_stmt = true, + .line_base = 0, + .line_range = 1, + .opcode_base = DW.LNS.set_isa + 1, + }, + }, + }, + .debug_line_str = .{ + .ni = .none, + .offset = 0, + .map = .empty, + }, + .debug_rnglists = .{}, + .debug_str = .{ + .ni = .none, + .offset = 0, + .map = .empty, + }, + .debug_str_offsets = .{ + .ni = .none, + .offset = 0, + }, + }; +} + +pub fn deinit(dwarf: *Dwarf) void { + const gpa = dwarf.lf.comp.gpa; + dwarf.const_pool.deinit(gpa); + for (dwarf.units) |*unit| unit.deinit(gpa); + gpa.free(dwarf.units); + dwarf.consts.deinit(gpa); + dwarf.globals.deinit(gpa); + dwarf.funcs.deinit(gpa); + dwarf.decls.deinit(gpa); + dwarf.debug_line_str.map.deinit(gpa); + dwarf.debug_str.map.deinit(gpa); + dwarf.* = undefined; +} + +pub fn initUnits(dwarf: *Dwarf, gpa: std.mem.Allocator, units_len: usize) std.mem.Allocator.Error!void { + assert(dwarf.units.len == 0); + dwarf.units = try gpa.alloc(Unit, units_len); + @memset(dwarf.units, .{ + .alive = false, + .dirs = .empty, + .files = .empty, + .frame_ni = .none, + .cie_ni = .none, + .debug_info_ni = .none, + .debug_info_header_ni = .none, + .debug_info_footer_ni = .none, + .debug_line_ni = .none, + .debug_line_header_ni = .none, + .debug_line_header_changed = false, + .debug_rnglists_ni = .none, + .debug_rnglists_offsets_table_offset = undefined, + .debug_rnglists_end = undefined, + }); +} +pub fn updateUnits(dwarf: *Dwarf, zcu: *Zcu) std.mem.Allocator.Error!bool { + var units_changed = false; + for (zcu.module_roots.values(), dwarf.units, 0..) |root, *unit, ui| { + const root_zfi = root.unwrap() orelse continue; // non-zig + const alive = zcu.alive_files.contains(root_zfi); + if (unit.alive == alive) continue; // unchanged + unit.alive = alive; + units_changed = true; + if (!alive) continue; // unreferenced + assert(zcu.fileByIndex(root_zfi).mod != null); + const root_di, const root_fi = try unit.getFile( + zcu.gpa, + @fromBackingInt(@intCast(ui)), + root_zfi, + ); + assert(root_di == .root and root_fi == .root); + } + return units_changed; +} + +pub fn getUnit(dwarf: *Dwarf, mod: *Module) Unit.Index { + return @fromBackingInt(@intCast(dwarf.lf.comp.zcu.?.module_roots.getIndex(mod).?)); +} + +pub fn getConst(dwarf: *Dwarf, pt: Zcu.PerThread, val: Value) link.Error!link.ConstPool.Index { + assert(val.typeOf(pt.zcu).comptimeOnly(pt.zcu)); + return dwarf.const_pool.get(pt, dwarf.constPoolUser(), val.toIntern()); +} + +pub fn getGlobal(dwarf: *Dwarf, nav: InternPool.Nav.Index) link.Error!Global.Index { + const comp = dwarf.lf.comp; + const gpa = comp.gpa; + const global_gop = try dwarf.globals.getOrPut(gpa, nav); + if (!global_gop.found_existing) global_gop.value_ptr.* = .{ + .debug_info_ni = .none, + }; + const gi: Global.Index = @fromBackingInt(@intCast(global_gop.index)); + if (global_gop.value_ptr.debug_info_ni != .none) return gi; + const mod = comp.zcu.?.navFileScope(nav).mod.?; + assert(!mod.strip); + const elf = dwarf.lf.cast(.elf2).?; + try elf.nodes.ensureUnusedCapacity(gpa, 1); + try elf.dwarf_globals.append(gpa, .{ + .debug_info_first_target_reloc = .none, + .debug_info_first_node_reloc = .none, + .debug_info_first_symbol_reloc = .none, + }); + const unit = dwarf.getUnit(mod).get(dwarf); + global_gop.value_ptr.debug_info_ni = .wrap(elf.addNodeAssumeCapacity( + unit.debug_info_ni.unwrap().?.addFloatingChild(gpa, &elf.mf, .{ + .enable_next_moved = true, + }) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{ + elf.mf.io_err.?, + }), + }, + .{ .global_debug_info = gi }, + )); + return gi; +} +pub fn getGlobalIfExists(dwarf: *Dwarf, nav: InternPool.Nav.Index) ?Global.Index { + return @fromBackingInt(@intCast(dwarf.globals.getIndex(nav) orelse return null)); +} + +pub fn getFunc(dwarf: *Dwarf, nav: InternPool.Nav.Index) link.Error!Func.Index { + const comp = dwarf.lf.comp; + const gpa = comp.gpa; + const func_gop = try dwarf.funcs.getOrPut(gpa, nav); + if (!func_gop.found_existing) func_gop.value_ptr.* = .{ + .state = .unresolved, + .fde_ni = .none, + .debug_info_ni = .none, + .debug_line_ni = .none, + }; + const fi: Func.Index = @fromBackingInt(@intCast(func_gop.index)); + if (func_gop.value_ptr.debug_info_ni != .none) return fi; + const mod = comp.zcu.?.navFileScope(nav).mod.?; + const elf = dwarf.lf.cast(.elf2).?; + try elf.nodes.ensureUnusedCapacity(gpa, 1); + try elf.dwarf_funcs.append(gpa, .{ + .frame_fde_first_symbol_reloc = .none, + .frame_fde_first_node_reloc = .none, + .debug_info_first_target_reloc = .none, + .debug_info_first_symbol_reloc = .none, + .debug_info_first_node_reloc = .none, + .debug_line_first_symbol_reloc = .none, + .debug_line_first_node_reloc = .none, + }); + if (mod.strip) return fi; + const unit = dwarf.getUnit(mod).get(dwarf); + func_gop.value_ptr.debug_info_ni = .wrap(elf.addNodeAssumeCapacity( + unit.debug_info_ni.unwrap().?.addFloatingChild(gpa, &elf.mf, .{ + .enable_next_moved = true, + }) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{ + elf.mf.io_err.?, + }), + }, + .{ .func_debug_info = fi }, + )); + return fi; +} +pub fn getFuncIfExists(dwarf: *Dwarf, nav: InternPool.Nav.Index) ?Func.Index { + return @fromBackingInt(@intCast(dwarf.funcs.getIndex(nav) orelse return null)); +} + +fn getDeclInst(dwarf: *Dwarf, val: InternPool.Index) ?InternPool.TrackedInst.Index { + const ip = &dwarf.lf.comp.zcu.?.intern_pool; + switch (ip.indexToKey(val)) { + else => unreachable, + .struct_type, .union_type, .enum_type, .opaque_type => |container, tag| switch (container) { + .declared => |declared| switch (declared.captures.owned.len) { + 0 => return null, + else => switch (tag) { + else => unreachable, + .struct_type => { + const loaded_struct = ip.loadStructType(val); + return ip.getNav(loaded_struct.name_nav.unwrap() orelse + return loaded_struct.zir_index).srcInst(ip); + }, + .union_type => { + const loaded_union = ip.loadUnionType(val); + return ip.getNav(loaded_union.name_nav.unwrap() orelse + return loaded_union.zir_index).srcInst(ip); + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(val); + return ip.getNav(loaded_enum.name_nav.unwrap() orelse + return loaded_enum.zir_index.unwrap().?).srcInst(ip); + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(val); + return ip.getNav(loaded_opaque.name_nav.unwrap() orelse + return loaded_opaque.zir_index).srcInst(ip); + }, + }, + }, + .reified => |reified| { + assert(reified.zir_index.resolve(ip).? != .main_struct_inst); + return reified.zir_index; + }, + .generated_union_tag => unreachable, + }, + .func => |func| return ip.getNav(switch (func.generic_owner) { + .none => func.owner_nav, + else => |generic_owner| ip.indexToKey(generic_owner).func.owner_nav, + }).srcInst(ip), + } +} +pub fn getDecl( + dwarf: *Dwarf, + pt: Zcu.PerThread, + instance_val: InternPool.Index, +) link.Error!link.MappedFile.Node.Index { + assert(dwarf.pending_decl.instance_val == .none); + const comp = dwarf.lf.comp; + const gpa = comp.gpa; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const inst = dwarf.getDeclInst(instance_val) orelse { + const cpi = try dwarf.getConst(pt, .fromInterned(instance_val)); + return Const.get(cpi, dwarf).debug_info_ni.unwrap().?; + }; + const decl_gop = try dwarf.decls.getOrPut(gpa, inst); + if (!decl_gop.found_existing) decl_gop.value_ptr.* = .{ + .debug_info_ni = .none, + }; + const di: Decl.Index = @fromBackingInt(@intCast(decl_gop.index)); + if (decl_gop.value_ptr.debug_info_ni.unwrap()) |debug_info_ni| return debug_info_ni; + dwarf.pending_decl = .{ .di = di, .instance_val = instance_val }; + const elf = dwarf.lf.cast(.elf2).?; + try elf.nodes.ensureUnusedCapacity(gpa, 1); + try elf.dwarf_decls.putNoClobber(gpa, di, .{ + .debug_info_first_target_reloc = .none, + .debug_info_first_node_reloc = .none, + }); + const unit = dwarf.getUnit(zcu.fileByIndex(di.srcInst(dwarf).resolveFile(ip)).mod.?).get(dwarf); + const debug_info_ni = elf.addNodeAssumeCapacity( + unit.debug_info_ni.unwrap().?.addFloatingChild(gpa, &elf.mf, .{ + .enable_next_moved = true, + }) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{ + elf.mf.io_err.?, + }), + }, + .{ .decl_debug_info = di }, + ); + decl_gop.value_ptr.debug_info_ni = .wrap(debug_info_ni); + return debug_info_ni; +} +pub fn getDeclIfExists(dwarf: *Dwarf, inst: InternPool.TrackedInst.Index) ?Decl.Index { + return @fromBackingInt(@intCast(dwarf.decls.getIndex(inst) orelse return null)); +} + +pub fn unitLengthSize(dwarf: *Dwarf) usize { + return switch (dwarf.format) { + .@"32" => 4, + .@"64" => 4 + 8, + }; +} +pub fn genUnitLength(dwarf: *Dwarf, w: *std.Io.Writer) std.Io.Writer.Error!void { + switch (dwarf.format) { + .@"32" => try w.writeInt(u32, undefined, dwarf.endian), + .@"64" => { + try w.writeInt(u32, std.math.maxInt(u32), dwarf.endian); + try w.writeInt(u64, undefined, dwarf.endian); + }, + } +} +pub fn updateUnitLength(dwarf: *Dwarf, header: []u8, unit_length: u64) void { + switch (dwarf.format) { + .@"32" => std.mem.writeInt(u32, header[0..4], @intCast(unit_length - 4), dwarf.endian), + .@"64" => std.mem.writeInt(u64, header[4..12], unit_length - 12, dwarf.endian), + } +} + +pub fn genUnitPadding(dwarf: *Dwarf, w: *std.Io.Writer) std.Io.Writer.Error!void { + try dwarf.genUnitLength(w); + try w.writeInt(u16, 0, dwarf.endian); +} + +pub const EhFrameHdr = extern struct { + version: u8, + eh_frame_ptr_enc: std.dwarf.EH.PE, + fde_count_enc: std.dwarf.EH.PE, + table_enc: std.dwarf.EH.PE, + eh_frame_ptr: u32, +}; +pub fn genEhFrameHdr( + dwarf: *Dwarf, + eh_frame_hdr_ai: link.File.AtomId, + eh_frame_hdr: *EhFrameHdr, + eh_frame_si: link.File.SymbolId, +) link.Error!void { + eh_frame_hdr.* = .{ + .version = 1, + .eh_frame_ptr_enc = .{ .type = .sdata4, .rel = .pcrel }, + .fde_count_enc = .omit, + .table_enc = .omit, + .eh_frame_ptr = undefined, + }; + if (dwarf.lf.cast(.elf2)) |elf| try elf.addReloc( + eh_frame_hdr_ai, + @offsetOf(EhFrameHdr, "eh_frame_ptr"), + eh_frame_si, + 0, + .rel32(elf), + ) else unreachable; +} + +pub fn genDebugFrameCie( + dwarf: *Dwarf, + df_w: *std.Io.Writer, + /// `null` means to generate an architecture-agnostic padding cie + arch: ?std.Target.Cpu.Arch, + format: Frame.Format, +) std.Io.Writer.Error!void { + try dwarf.genUnitLength(df_w); + switch (format) { + .eh_frame => try df_w.writeInt(u32, 0, dwarf.endian), + .debug_frame => switch (dwarf.format) { + .@"32" => try df_w.writeInt(u32, std.math.maxInt(u32), dwarf.endian), + .@"64" => try df_w.writeInt(u64, std.math.maxInt(u64), dwarf.endian), + }, + } + try df_w.writeByte(if (arch) |_| switch (format) { + .eh_frame => 1, + .debug_frame => 4, + } else 0); + switch (arch orelse return) { + else => unreachable, + .x86_64 => { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); + const Register = @import("../codegen/x86_64/bits.zig").Register; + switch (format) { + .eh_frame => try df_w.writeAll("zR\x00"), + .debug_frame => { + try df_w.writeAll("\x00"); + try df_w.writeByte(@backingInt(dwarf.address_size)); + try df_w.writeByte(0); + }, + } + try df_w.writeUleb128(dwarf.frame.header.code_alignment_factor); + try df_w.writeSleb128(dwarf.frame.header.data_alignment_factor); + switch (format) { + .eh_frame => try df_w.writeByte(@intCast(dwarf.frame.header.return_address_register)), + .debug_frame => try df_w.writeUleb128(dwarf.frame.header.return_address_register), + } + switch (format) { + .eh_frame => { + try df_w.writeUleb128(1); + try df_w.writeByte(@bitCast(@as(DW.EH.PE, .{ .type = .sdata4, .rel = .pcrel }))); + }, + .debug_frame => {}, + } + try df_w.writeByte(DW.CFA.def_cfa_sf); + try df_w.writeUleb128(Register.rsp.dwarfNum()); + try df_w.writeSleb128(-1); + try df_w.writeByte(@as(u8, DW.CFA.offset) + Register.rip.dwarfNum()); + try df_w.writeUleb128(1); + }, + } + @memset(df_w.unusedCapacitySlice(), DW.CFA.nop); +} + +pub fn updateEhFrameFde(dwarf: *Dwarf, fde: []u8, fde_offset: u64) void { + const cie_pointer_offset = dwarf.unitLengthSize(); + std.mem.writeInt( + u32, + fde[cie_pointer_offset..][0..4], + @intCast(fde_offset + cie_pointer_offset), + dwarf.endian, + ); +} + +pub fn genDebugInfoHeader( + dwarf: *Dwarf, + zcu: *Zcu, + mod: *Module, + unit: *Unit, + dih_nw: *link.MappedFile.Node.Writer, +) link.EmitError!void { + const comp = zcu.comp; + const dih_w = &dih_nw.interface; + if (!unit.alive) return dwarf.genUnitPadding(dih_w); + try dwarf.genUnitLength(dih_w); + try dih_w.writeInt(u16, 5, dwarf.endian); + try dih_w.writeByte(DW.UT.compile); + try dih_w.writeByte(@backingInt(dwarf.address_size)); + try dwarf.secOffset(dih_nw, dwarf.debug_abbrev.ni.unwrap().?, 0); + const compile_unit_offset = dih_w.end; + try dwarf.abbrevCode(dih_nw, .compile_unit); + try dih_w.writeByte(DW.LANG.Zig); + try dwarf.strp(&dwarf.debug_str, dih_nw, "zig " ++ @import("build_options").version); + const root_dir_path = try mod.root.toAbsolute(&comp.dirs, comp.gpa); + defer comp.gpa.free(root_dir_path); + try dwarf.strp(&dwarf.debug_line_str, dih_nw, root_dir_path); + try dwarf.strp(&dwarf.debug_line_str, dih_nw, mod.root_src_path); + try dwarf.secOffset( + dih_nw, + dwarf.getUnit(zcu.root_mod).get(dwarf).debug_info_header_ni.unwrap().?, + compile_unit_offset, + ); + try dwarf.secOffset(dih_nw, unit.debug_line_header_ni.unwrap().?, 0); + try dwarf.secOffset(dih_nw, unit.debug_rnglists_ni.unwrap().?, Rnglists.offsetsTableOffset(dwarf)); + try dih_w.writeUleb128(0); + const module_offset = dih_w.end; + try dwarf.abbrevCode(dih_nw, .module); + try dwarf.strp(&dwarf.debug_str, dih_nw, mod.fully_qualified_name); + try dih_w.writeUleb128(0); + try dwarf.genModuleDependency( + dih_nw, + "builtin", + zcu.builtin_modules.get(mod.getBuiltinOptions(comp.config).hash()).?, + module_offset, + ); + try dwarf.genModuleDependency(dih_nw, "root", zcu.root_mod, module_offset); + try dwarf.genModuleDependency(dih_nw, "std", zcu.std_mod, module_offset); + for (mod.deps.keys(), mod.deps.values()) |name, dep| + try dwarf.genModuleDependency(dih_nw, name, dep, module_offset); + for ([2]AbbrevCode{ .pad_1, .pad_n }) |pad| _ = try dwarf.refAbbrevCode(dih_nw.mf, pad); + try dwarf.genDebugInfoPadding(dih_w, dih_w.unusedCapacityLen()); +} + +fn genModuleDependency( + dwarf: *Dwarf, + di_nw: *link.MappedFile.Node.Writer, + name: []const u8, + dep: *Module, + module_offset: usize, +) link.EmitError!void { + const dep_unit = dwarf.getUnit(dep).get(dwarf); + if (!dep_unit.alive) return; + try dwarf.abbrevCode(di_nw, .module_dependency); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try dwarf.secOffset(di_nw, dep_unit.debug_info_header_ni.unwrap().?, module_offset); +} + +pub fn genDebugInfoPadding(dwarf: *Dwarf, di_w: *std.Io.Writer, size: u64) std.Io.Writer.Error!void { + switch (size) { + 0 => {}, + 1 => try di_w.writeUleb128(dwarf.refAbbrevCodeIfExists(.pad_1).?), + else => { + const abbrev_code_offset = di_w.end; + try di_w.writeUleb128(dwarf.refAbbrevCodeIfExists(.pad_n).?); + const abbrev_code_size = di_w.end - abbrev_code_offset; + var block_len_size: u5 = 1; + while (true) switch (std.math.order( + size - abbrev_code_size - block_len_size, + @as(u64, 1) << 7 * block_len_size, + )) { + .lt => break try di_w.writeUleb128(size - abbrev_code_size - block_len_size), + .eq => { + // no length will ever work, so undercount and futz with + // the leb encoding to make up the missing byte + block_len_size += 1; + std.leb.writeUnsignedExtended( + try di_w.writableSlice(block_len_size), + size - abbrev_code_size - block_len_size, + ); + break; + }, + .gt => block_len_size += 1, + }; + }, + } +} + +pub fn genDebugLineHeader( + dwarf: *Dwarf, + unit: *Unit, + dlh_nw: *link.MappedFile.Node.Writer, + zcu: *Zcu, +) link.EmitError!void { + const comp = zcu.comp; + const dlh_w = &dlh_nw.interface; + try dwarf.genUnitLength(dlh_w); + try dlh_w.writeInt(u16, 5, dwarf.endian); + try dlh_w.writeByte(@backingInt(dwarf.address_size)); + try dlh_w.writeByte(0); + const header_length_offset = dlh_w.end; + switch (dwarf.format) { + .@"32" => try dlh_w.writeInt(u32, undefined, dwarf.endian), + .@"64" => try dlh_w.writeInt(u64, undefined, dwarf.endian), + } + const header_start = dlh_w.end; + const StandardOpcode = DeclValEnum(DW.LNS); + try dlh_w.writeAll(&.{ + dwarf.debug_line.header.minimum_instruction_length, + dwarf.debug_line.header.maximum_operations_per_instruction, + @intFromBool(dwarf.debug_line.header.default_is_stmt), + @bitCast(dwarf.debug_line.header.line_base), + dwarf.debug_line.header.line_range, + dwarf.debug_line.header.opcode_base, + }); + try dlh_w.writeAll(std.enums.EnumArray(StandardOpcode, u8).init(.{ + .extended_op = undefined, + .copy = 0, + .advance_pc = 1, + .advance_line = 1, + .set_file = 1, + .set_column = 1, + .negate_stmt = 0, + .set_basic_block = 0, + .const_add_pc = 0, + .fixed_advance_pc = 1, + .set_prologue_end = 0, + .set_epilogue_begin = 0, + .set_isa = 1, + }).values[1..dwarf.debug_line.header.opcode_base]); + try dlh_w.writeByte(1); + try dlh_w.writeUleb128(DW.LNCT.path); + try dlh_w.writeUleb128(DW.FORM.line_strp); + const dir_count = unit.dirs.count(); + const directory_index_form: DeclValEnum(DW.FORM) = if (dir_count <= 1 << 8) + .data1 + else if (dir_count <= 1 << 16) + .data2 + else + .udata; + try dlh_w.writeUleb128(dir_count); + for (unit.dirs.keys()) |ui| { + const root_dir_path = try ui.mod(dwarf).root.toAbsolute(&zcu.comp.dirs, comp.gpa); + defer comp.gpa.free(root_dir_path); + try dwarf.strp(&dwarf.debug_line_str, dlh_nw, root_dir_path); + } + try dlh_w.writeByte(5); + try dlh_w.writeUleb128(DW.LNCT.path); + try dlh_w.writeUleb128(DW.FORM.line_strp); + try dlh_w.writeUleb128(DW.LNCT.directory_index); + try dlh_w.writeUleb128(@backingInt(directory_index_form)); + try dlh_w.writeUleb128(DW.LNCT.timestamp); + try dlh_w.writeUleb128(DW.FORM.data8); + try dlh_w.writeUleb128(DW.LNCT.size); + try dlh_w.writeUleb128(DW.FORM.data8); + try dlh_w.writeUleb128(DW.LNCT.LLVM_source); + try dlh_w.writeUleb128(DW.FORM.line_strp); + try dlh_w.writeUleb128(unit.files.count()); + for (unit.files.keys()) |zfi| { + const zf = zcu.fileByIndex(zfi); + try dwarf.strp(&dwarf.debug_line_str, dlh_nw, zf.sub_file_path); + const di = + if (zcu.alive_files.contains(zfi)) unit.dirs.getIndex(dwarf.getUnit(zf.mod.?)).? else 0; + switch (directory_index_form) { + else => unreachable, + .data1 => try dlh_w.writeByte(@intCast(di)), + .data2 => try dlh_w.writeInt(u16, @intCast(di), dwarf.endian), + .udata => try dlh_w.writeUleb128(di), + } + try dlh_w.writeInt(i64, @truncate(zf.stat.mtime.nanoseconds), dwarf.endian); + try dlh_w.writeInt(u64, zf.stat.size, dwarf.endian); + try dwarf.strp( + &dwarf.debug_line_str, + dlh_nw, + if (zf.is_builtin) zf.source.? else "", + ); + } + switch (dwarf.format) { + .@"32" => std.mem.writeInt( + u32, + dlh_w.buffer[header_length_offset..][0..4], + @intCast(dlh_w.end - header_start), + dwarf.endian, + ), + .@"64" => std.mem.writeInt( + u64, + dlh_w.buffer[header_length_offset..][0..8], + dlh_w.end - header_start, + dwarf.endian, + ), + } + try genDebugLinePadding(dlh_w, dlh_w.unusedCapacityLen()); +} + +pub fn genDebugLinePadding(dl_w: *std.Io.Writer, size: u64) std.Io.Writer.Error!void { + switch (size) { + 0 => {}, + 1 => try dl_w.writeByte(DW.LNS.const_add_pc), + 2 => try dl_w.writeAll(&.{ DW.LNS.negate_stmt, DW.LNS.negate_stmt }), + else => { + const extended_op_offset = dl_w.end; + try dl_w.writeByte(DW.LNS.extended_op); + const extended_op_size = dl_w.end - extended_op_offset; + var op_len_size: u5 = 1; + while (true) switch (std.math.order( + size - extended_op_size - op_len_size, + @as(u64, 1) << 7 * op_len_size, + )) { + .lt => break try dl_w.writeUleb128(size - extended_op_size - op_len_size), + .eq => { + // no length will ever work, so undercount and futz with + // the leb encoding to make up the missing byte + op_len_size += 1; + std.leb.writeUnsignedExtended( + try dl_w.writableSlice(op_len_size), + size - extended_op_size - op_len_size, + ); + break; + }, + .gt => op_len_size += 1, + }; + try dl_w.writeByte(DW.LNE.padding); + }, + } +} + +pub fn genDebugRnglistsHeader( + dwarf: *Dwarf, + unit: *Unit, + drh_nw: *link.MappedFile.Node.Writer, +) std.Io.Writer.Error!void { + const drh_w = &drh_nw.interface; + try dwarf.genUnitLength(drh_w); + try drh_w.writeInt(u16, 5, dwarf.endian); + try drh_w.writeByte(@backingInt(dwarf.address_size)); + try drh_w.writeByte(0); + try drh_w.writeInt(u32, 1, dwarf.endian); + assert(drh_w.end == Rnglists.offsetsTableOffset(dwarf)); + switch (dwarf.format) { + .@"32" => try drh_w.writeInt(u32, 4, dwarf.endian), + .@"64" => try drh_w.writeInt(u64, 8, dwarf.endian), + } + unit.debug_rnglists_end = drh_w.end; + try drh_w.writeByte(DW.RLE.end_of_list); +} + +pub fn genDebugRnglists( + dwarf: *Dwarf, + unit: *Unit, + dr_nw: *link.MappedFile.Node.Writer, + func_si: link.File.SymbolId, + func_length: u64, +) link.EmitError!void { + const dr_w = &dr_nw.interface; + dr_w.end = unit.debug_rnglists_end; + try dr_w.writeByte(DW.RLE.start_length); + try dwarf.addrSym(dr_nw, func_si, 0); + try dr_w.writeUleb128(func_length); + unit.debug_rnglists_end = dr_w.end; + try dr_w.writeByte(DW.RLE.end_of_list); +} + +pub fn updateComptimeNav( + dwarf: *Dwarf, + pt: Zcu.PerThread, + nav_index: InternPool.Nav.Index, +) link.Error!void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); + log.debug("updateComptimeNav({f})", .{nav.fqn.fmt(ip)}); + const inst_info = nav.srcInst(ip).resolveFull(ip).?; + const nav_val: Value = .fromInterned(nav.resolved.?.value); + const zf = zcu.fileByIndex(inst_info.file); + const decl = zf.zir.?.getDeclaration(inst_info.inst); + switch (decl.kind) { + .unnamed_test, .@"test", .decltest => return, + .@"comptime", .@"const", .@"var" => {}, + } + done: switch (ip.indexToKey(nav_val.toIntern())) { + .struct_type => { + const loaded_struct = ip.loadStructType(nav_val.toIntern()); + if (nav_index.toOptional() == loaded_struct.name_nav) { + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + break :done; + } + return; + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(nav_val.toIntern()); + if (nav_index.toOptional() == loaded_enum.name_nav) { + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + break :done; + } + return; + }, + .union_type => { + const loaded_union = ip.loadUnionType(nav_val.toIntern()); + if (nav_index.toOptional() == loaded_union.name_nav) { + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + break :done; + } + return; + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(nav_val.toIntern()); + if (nav_index.toOptional() == loaded_opaque.name_nav) { + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + break :done; + } + return; + }, + .func => |func| if (func.owner_nav == nav_index and func.generic_owner == .none) { + _ = try dwarf.const_pool.get(pt, dwarf.constPoolUser(), nav_val.toIntern()); + break :done; + } else return, + + else => return, + + // memoization, not values + .memoized_call => unreachable, + } + try dwarf.const_pool.flushPending(pt, dwarf.constPoolUser()); +} + +pub fn addConst( + dwarf: *Dwarf, + cpi: link.ConstPool.Index, + val: InternPool.Index, + addConstNode: *const fn ( + lf: *link.File, + ui: Unit.Index, + cpi: link.ConstPool.Index, + ) link.Error!link.MappedFile.Node.Index, +) link.Error!void { + const zcu = dwarf.lf.comp.zcu.?; + const ip = &zcu.intern_pool; + assert(@backingInt(cpi) == dwarf.consts.items.len); + dwarf.consts.appendAssumeCapacity(.{ + .debug_info_ni = debug_info_ni: switch (ip.indexToKey(val)) { + else => try addConstNode(dwarf.lf, dwarf.getUnit(zcu.root_mod), cpi), + .func => |func| { + const fi = try dwarf.getFunc(func.owner_nav); + break :debug_info_ni fi.get(dwarf).debug_info_ni.unwrap().?; + }, + .@"extern" => |@"extern"| { + const gi = try dwarf.getGlobal(@"extern".owner_nav); + break :debug_info_ni gi.get(dwarf).debug_info_ni.unwrap().?; + }, + .struct_type, .union_type, .enum_type, .opaque_type => |_, tag| { + if (switch (tag) { + else => unreachable, + .struct_type => ip.loadStructType(val).name_nav, + .union_type => ip.loadUnionType(val).name_nav, + .enum_type => ip.loadEnumType(val).name_nav, + .opaque_type => ip.loadOpaqueType(val).name_nav, + }.unwrap()) |name_nav| { + const name_gi = try dwarf.getGlobal(name_nav); + break :debug_info_ni name_gi.get(dwarf).debug_info_ni.unwrap().?; + } + break :debug_info_ni try addConstNode(dwarf.lf, dwarf.getUnit(zcu.fileByIndex( + Type.fromInterned(val).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip), + ).mod.?), cpi); + }, + }.toOptional(), + }); +} + +pub fn updateConst( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + val: InternPool.Index, +) link.Error!void { + switch (val) { + .generic_poison_type => log.debug("updateConst(anytype)", .{}), + else => log.debug("updateConst({f})", .{Value.fromInterned(val).fmtValue(pt)}), + } + dwarf.updateConstInner(pt, di_nw, val) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dwarf.reportWriteError(di_nw), + }; +} +fn updateConstInner( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + val: InternPool.Index, +) link.EmitError!void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const di_w = &di_nw.interface; + switch (ip.indexToKey(val)) { + .int_type => |int_type| { + const ty: Type = .fromInterned(val); + try dwarf.abbrevCode(di_nw, .numeric_type); + var name_buf: [std.fmt.count("i{d}", .{std.math.maxInt(u16)})]u8 = undefined; + try dwarf.strp(&dwarf.debug_str, di_nw, std.mem.print(&name_buf, "{f}", .{ + ty.fmt(pt), + }) catch unreachable); + try di_w.writeByte(switch (int_type.signedness) { + .signed => DW.ATE.signed, + .unsigned => DW.ATE.unsigned, + }); + try di_w.writeUleb128(int_type.bits); + try di_w.writeUleb128(ty.abiSize(zcu)); + try di_w.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + }, + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .one, .many, .c => { + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, switch (ptr_type.sentinel) { + .none => switch (ptr_type.flags.alignment) { + .none => .ptr_type, + else => .ptr_aligned_type, + }, + else => switch (ptr_type.flags.alignment) { + .none => .ptr_sentinel_type, + else => .ptr_aligned_sentinel_type, + }, + }); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + switch (ptr_type.sentinel) { + .none => {}, + else => |sentinel| try dwarf.blockConst(pt, di_nw, .fromInterned(sentinel)), + } + if (ptr_type.flags.alignment.toByteUnits()) |a| try di_w.writeUleb128(a); + try di_w.writeByte(@backingInt(ptr_type.flags.address_space)); + if (ptr_type.flags.is_const or ptr_type.flags.is_volatile) try dwarf.secOffset( + di_nw, + di_nw.ni, + di_w.end + dwarf.secOffsetSize(), + ); + if (ptr_type.flags.is_const) { + try dwarf.abbrevCode(di_nw, .is_const); + if (ptr_type.flags.is_volatile) try dwarf.secOffset( + di_nw, + di_nw.ni, + di_w.end + dwarf.secOffsetSize(), + ); + } + if (ptr_type.flags.is_volatile) try dwarf.abbrevCode(di_nw, .is_volatile); + try dwarf.refType(pt, di_nw, .fromInterned(ptr_type.child)); + }, + .slice => { + const ty: Type = .fromInterned(val); + const name = try zcu.gpa.print("{f}", .{ty.fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, .generated_struct_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try di_w.writeUleb128(ty.abiSize(zcu)); + try di_w.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "ptr"); + const ptr_field_ty = ty.slicePtrFieldType(zcu); + try dwarf.refType(pt, di_nw, ptr_field_ty); + try di_w.writeUleb128(0); + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "len"); + const len_field_ty: Type = .usize; + try dwarf.refType(pt, di_nw, len_field_ty); + try di_w.writeUleb128(len_field_ty.abiAlignment(zcu).forward(ptr_field_ty.abiSize(zcu))); + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + }, + .array_type => |array_type| { + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode( + di_nw, + if (array_type.sentinel == .none) .array_type else .array_sentinel_type, + ); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + if (array_type.sentinel != .none) + try dwarf.blockConst(pt, di_nw, .fromInterned(array_type.sentinel)); + try dwarf.refType(pt, di_nw, .fromInterned(array_type.child)); + try dwarf.abbrevCode(di_nw, .array_len); + try dwarf.refType(pt, di_nw, .usize); + try di_w.writeUleb128(array_type.len); + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .vector_type => |vector_type| { + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, .vector_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try dwarf.refType(pt, di_nw, .fromInterned(vector_type.child)); + try dwarf.abbrevCode(di_nw, .array_len); + try dwarf.refType(pt, di_nw, .usize); + try di_w.writeUleb128(vector_type.len); + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .opt_type => |opt_child_type_index| { + const opt_ty: Type = .fromInterned(val); + const opt_child_ty: Type = .fromInterned(opt_child_type_index); + const opt_repr = optRepr(opt_child_ty, zcu); + const name = try zcu.gpa.print("{f}", .{opt_ty.fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, .generated_union_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try di_w.writeUleb128(opt_ty.abiSize(zcu)); + try di_w.writeUleb128(opt_ty.abiAlignment(zcu).toByteUnits().?); + switch (opt_repr) { + .opv_null => { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "null"); + try dwarf.refType(pt, di_nw, .null); + try di_w.writeUleb128(0); + }, + .unpacked, .error_set, .pointer => { + try dwarf.abbrevCode(di_nw, .tagged_union); + try dwarf.secOffset(di_nw, di_nw.ni, di_w.end + dwarf.secOffsetSize()); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "has_value"); + switch (opt_repr) { + .opv_null => unreachable, + .unpacked => { + try dwarf.refType(pt, di_nw, .bool); + try di_w.writeUleb128(if (opt_child_ty.hasRuntimeBits(zcu)) + opt_child_ty.abiSize(zcu) + else + 0); + }, + .error_set => { + try dwarf.refType(pt, di_nw, try pt.intType(.unsigned, zcu.errorSetBits())); + try di_w.writeUleb128(0); + }, + .pointer => { + try dwarf.refType(pt, di_nw, .usize); + try di_w.writeUleb128(0); + }, + } + + try dwarf.abbrevCode(di_nw, .tagged_union_field); + try di_w.writeUleb128(DW.FORM.data1); + try di_w.writeByte(0); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "null"); + try dwarf.refType(pt, di_nw, .null); + try di_w.writeUleb128(0); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + + try dwarf.abbrevCode(di_nw, .tagged_union_default_field); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "?"); + try dwarf.refType(pt, di_nw, opt_child_ty); + try di_w.writeUleb128(0); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .anyframe_type => unreachable, + .error_union_type => |error_union_type| { + const eu_ty: Type = .fromInterned(val); + const eu_error_set_ty: Type = .fromInterned(error_union_type.error_set_type); + const eu_payload_ty: Type = .fromInterned(error_union_type.payload_type); + const eu_error_set_offset, const eu_payload_offset = switch (error_union_type.payload_type) { + .generic_poison_type => .{ 0, 0 }, + else => .{ + codegen.errUnionErrorOffset(eu_payload_ty, zcu), + codegen.errUnionPayloadOffset(eu_payload_ty, zcu), + }, + }; + const name = try zcu.gpa.print("{f}", .{eu_ty.fmt(pt)}); + defer zcu.gpa.free(name); + + try dwarf.abbrevCode(di_nw, .generated_union_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + if (error_union_type.error_set_type != .generic_poison_type and + error_union_type.payload_type != .generic_poison_type) + { + try di_w.writeUleb128(eu_ty.abiSize(zcu)); + try di_w.writeUleb128(eu_ty.abiAlignment(zcu).toByteUnits().?); + } else { + try di_w.writeUleb128(0); + try di_w.writeUleb128(1); + } + { + try dwarf.abbrevCode(di_nw, .tagged_union); + try dwarf.secOffset(di_nw, di_nw.ni, di_w.end + dwarf.secOffsetSize()); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "is_error"); + try dwarf.refType(pt, di_nw, try pt.intType(.unsigned, zcu.errorSetBits())); + try di_w.writeUleb128(eu_error_set_offset); + + try dwarf.abbrevCode(di_nw, .tagged_union_field); + try di_w.writeUleb128(DW.FORM.udata); + try di_w.writeUleb128(0); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "value"); + try dwarf.refType(pt, di_nw, eu_payload_ty); + try di_w.writeUleb128(eu_payload_offset); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + + try dwarf.abbrevCode(di_nw, .tagged_union_default_field); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "error"); + try dwarf.refType(pt, di_nw, eu_error_set_ty); + try di_w.writeUleb128(eu_error_set_offset); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .simple_type => |simple_type| switch (simple_type) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_char, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .bool, + => { + const ty: Type = .fromInterned(val); + try dwarf.abbrevCode(di_nw, .numeric_type); + try dwarf.strp(&dwarf.debug_str, di_nw, @tagName(simple_type)); + try di_w.writeByte(if (val == .bool_type) + DW.ATE.boolean + else if (ty.isRuntimeFloat()) + DW.ATE.float + else if (ty.isSignedInt(zcu)) + DW.ATE.signed + else if (ty.isUnsignedInt(zcu)) + DW.ATE.unsigned + else + unreachable); + try di_w.writeUleb128(ty.bitSize(zcu)); + try di_w.writeUleb128(ty.abiSize(zcu)); + try di_w.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + }, + .generic_poison => { + try dwarf.abbrevCode(di_nw, .void_type); + try dwarf.strp(&dwarf.debug_str, di_nw, "anytype"); + }, + .anyopaque, + .void, + .type, + .comptime_int, + .comptime_float, + .noreturn, + .null, + .undefined, + .enum_literal, + => { + const ty: Type = .fromInterned(val); + try dwarf.abbrevCode(di_nw, .void_type); + var name_buf: ["@TypeOf(undefined)".len]u8 = undefined; + try dwarf.strp(&dwarf.debug_str, di_nw, std.mem.print(&name_buf, "{f}", .{ + ty.fmt(pt), + }) catch unreachable); + }, + .anyerror => { + const global_error_set_names = ip.global_error_set.getNamesFromMainThread(); + try dwarf.abbrevCode(di_nw, if (global_error_set_names.len > 0) + .generated_enum_type + else + .generated_empty_enum_type); + try dwarf.strp(&dwarf.debug_str, di_nw, "anyerror"); + try dwarf.refType(pt, di_nw, try pt.intType(.unsigned, zcu.errorSetBits())); + for (global_error_set_names, 1..) |name, value| { + try dwarf.abbrevCode(di_nw, .enum_field); + try di_w.writeUleb128(DW.FORM.udata); + try di_w.writeUleb128(value); + try dwarf.strp(&dwarf.debug_str, di_nw, name.toSlice(ip)); + } + if (global_error_set_names.len > 0) try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .adhoc_inferred_error_set => unreachable, + }, + .tuple_type => |tuple_type| { + const ty: Type = .fromInterned(val); + const name = try zcu.gpa.print("{f}", .{ty.fmt(pt)}); + defer zcu.gpa.free(name); + if (tuple_type.types.len == 0) { + try dwarf.abbrevCode(di_nw, .generated_empty_struct_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try di_w.writeByte(@intFromBool(false)); + } else { + try dwarf.abbrevCode(di_nw, .generated_struct_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try di_w.writeUleb128(ty.abiSize(zcu)); + try di_w.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + var field_byte_offset: u64 = 0; + for (0..tuple_type.types.len) |field_index| { + const comptime_value = tuple_type.values.get(ip)[field_index]; + const field_ty: Type = .fromInterned(tuple_type.types.get(ip)[field_index]); + const comptime_value_class = switch (comptime_value) { + .none => .no_possible_value, + else => field_ty.classify(zcu), + }; + try dwarf.abbrevCode(di_nw, switch (comptime_value) { + .none => .field, + else => switch (comptime_value_class) { + .no_possible_value, .one_possible_value => .field_comptime, + .runtime => .field_comptime_fully_runtime, + .partially_comptime => .field_comptime_partially_comptime, + .fully_comptime => .field_comptime_fully_comptime, + }, + }); + var field_name_buf: [std.fmt.count("{d}", .{std.math.maxInt(u32)})]u8 = undefined; + try dwarf.strp(&dwarf.debug_str, di_nw, std.mem.print(&field_name_buf, "{d}", .{ + field_index, + }) catch unreachable); + try dwarf.refType(pt, di_nw, field_ty); + if (comptime_value == .none) { + const field_align = field_ty.abiAlignment(zcu); + field_byte_offset = field_align.forward(field_byte_offset); + try di_w.writeUleb128(field_byte_offset); + try di_w.writeUleb128(field_ty.abiAlignment(zcu).toByteUnits().?); + field_byte_offset += field_ty.abiSize(zcu); + } + if (comptime_value_class.hasRuntimeBits()) + try dwarf.blockConst(pt, di_nw, .fromInterned(comptime_value)); + if (comptime_value_class.comptimeOnly()) + try dwarf.refConst(pt, di_nw, .fromInterned(comptime_value)); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + }, + .struct_type => { + const loaded_struct = ip.loadStructType(val); + const zfi = loaded_struct.zir_index.resolveFile(ip); + const zf = zcu.fileByIndex(zfi); + const src_inst = loaded_struct.zir_index.resolve(ip); + if (src_inst == .main_struct_inst) { + assert(loaded_struct.captures.len == 0); + const ui = dwarf.getUnit(zf.mod.?); + _, const fi = try ui.get(dwarf).getFile(zcu.gpa, ui, zfi); + try dwarf.abbrevCode(di_nw, switch (loaded_struct.layout) { + .auto => if (loaded_struct.field_types.len > 0) .file else .empty_file, + .@"extern", .@"packed" => unreachable, + }); + try di_w.writeUleb128(@backingInt(fi)); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_struct.name.toSlice(ip)); + } else if (loaded_struct.captures.len > 0 or loaded_struct.is_reified) { + try dwarf.abbrevCode(di_nw, if (loaded_struct.captures.len > 0 or + loaded_struct.field_types.len > 0) switch (loaded_struct.layout) { + .auto, .@"extern" => .decl_instance_struct, + .@"packed" => .decl_instance_packed_struct, + } else switch (loaded_struct.layout) { + .auto, .@"extern" => .decl_instance_empty_struct, + .@"packed" => .decl_instance_empty_packed_struct, + }); + try dwarf.secOffset(di_nw, try dwarf.getDecl(pt, val), 0); + } else if (loaded_struct.name_nav.unwrap()) |name_ni| { + const name_nav = ip.getNav(name_ni); + const decl = zf.zir.?.getDeclaration(name_nav.srcInst(ip).resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + name_nav.analysis.?.namespace, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (loaded_struct.field_types.len > 0) + switch (loaded_struct.layout) { + .auto, .@"extern" => .decl_struct, + .@"packed" => .decl_packed_struct, + } + else switch (loaded_struct.layout) { + .auto, .@"extern" => .decl_empty_struct, + .@"packed" => .decl_empty_packed_struct, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, name_nav.name.toSlice(ip)); + } else { + const decl = zf.zir.?.getStructDecl(src_inst.?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + ip.namespacePtr(loaded_struct.namespace).parent.unwrap().?, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (loaded_struct.field_types.len > 0) + switch (loaded_struct.layout) { + .auto, .@"extern" => .type_decl_struct, + .@"packed" => .type_decl_packed_struct, + } + else switch (loaded_struct.layout) { + .auto, .@"extern" => .type_decl_empty_struct, + .@"packed" => .type_decl_empty_packed_struct, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_struct.name.toSlice(ip)); + } + switch (loaded_struct.layout) { + .auto, .@"extern" => { + const ty: Type = .fromInterned(val); + try di_w.writeUleb128(ty.abiSize(zcu)); + try di_w.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try dwarf.genCaptures(pt, di_nw, loaded_struct.captures); + for (0..loaded_struct.field_types.len) |field_index| { + const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index); + // TODO: we currently don't emit information about default values for + // non-`comptime` fields, because these default values are resolved at a + // separate time in the compiler frontend. To emit this information, the + // frontend needs to tell us when the default values are available: like + // how `Zcu.PerThread.ensureTypeLayoutUpToDate` enqueues a link task to + // indicate completion of the type's layout, a task should be enqueued + // by `Zcu.PerThread.ensureStructDefaultsUpToDate`, and upon receiving + // it we should patch the correct default field values in. + const field_default = if (is_comptime) + loaded_struct.field_defaults.getOrNone(ip, field_index) + else + .none; + assert(!(is_comptime and field_default == .none)); + const field_ty: Type = + .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + const field_default_class = switch (field_default) { + .none => .no_possible_value, + else => field_ty.classify(zcu), + }; + try dwarf.abbrevCode(di_nw, if (is_comptime) switch (field_default_class) { + .no_possible_value, .one_possible_value => .field_comptime, + .runtime => .field_comptime_fully_runtime, + .partially_comptime => .field_comptime_partially_comptime, + .fully_comptime => .field_comptime_fully_comptime, + } else switch (field_default_class) { + .no_possible_value, .one_possible_value => .field, + .runtime => .field_default_fully_runtime, + .partially_comptime => .field_default_partially_comptime, + .fully_comptime => .field_default_fully_comptime, + }); + try dwarf.strp( + &dwarf.debug_str, + di_nw, + loaded_struct.field_names.get(ip)[field_index].toSlice(ip), + ); + try dwarf.refType(pt, di_nw, field_ty); + if (!is_comptime) { + try di_w.writeUleb128(loaded_struct.field_offsets.get(ip)[field_index]); + try di_w.writeUleb128(loaded_struct.field_aligns.getOrNone( + ip, + field_index, + ).toByteUnits() orelse field_ty.abiAlignment(zcu).toByteUnits().?); + } + if (field_default_class.hasRuntimeBits()) + try dwarf.blockConst(pt, di_nw, .fromInterned(field_default)); + if (field_default_class.comptimeOnly()) + try dwarf.refConst(pt, di_nw, .fromInterned(field_default)); + } + }, + .@"packed" => { + try dwarf.refType(pt, di_nw, .fromInterned(loaded_struct.packed_backing_int_type)); + try dwarf.genCaptures(pt, di_nw, loaded_struct.captures); + var field_bit_offset: u16 = 0; + for (0..loaded_struct.field_types.len) |field_index| { + try dwarf.abbrevCode(di_nw, .packed_field); + try dwarf.strp( + &dwarf.debug_str, + di_nw, + loaded_struct.field_names.get(ip)[field_index].toSlice(ip), + ); + const field_ty: Type = + .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + try dwarf.refType(pt, di_nw, field_ty); + try di_w.writeUleb128(field_bit_offset); + field_bit_offset += @intCast(field_ty.bitSize(zcu)); + } + }, + } + if (loaded_struct.captures.len > 0 or loaded_struct.field_types.len > 0) + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .union_type => { + const loaded_union = ip.loadUnionType(val); + const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); + const zfi = loaded_union.zir_index.resolveFile(ip); + const zf = zcu.fileByIndex(zfi); + if (loaded_union.captures.len > 0 or loaded_union.is_reified) { + try dwarf.abbrevCode(di_nw, if (loaded_union.captures.len > 0 or + loaded_union.field_types.len > 0) switch (loaded_union.layout) { + .auto, .@"extern" => .decl_instance_union, + .@"packed" => .decl_instance_packed_union, + } else switch (loaded_union.layout) { + .auto, .@"extern" => .decl_instance_empty_union, + .@"packed" => .decl_instance_empty_packed_union, + }); + try dwarf.secOffset(di_nw, try dwarf.getDecl(pt, val), 0); + } else if (loaded_union.name_nav.unwrap()) |name_ni| { + const name_nav = ip.getNav(name_ni); + const decl = zf.zir.?.getDeclaration(name_nav.srcInst(ip).resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + name_nav.analysis.?.namespace, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (loaded_union.field_types.len > 0) + switch (loaded_union.layout) { + .auto, .@"extern" => .decl_union, + .@"packed" => .decl_packed_union, + } + else switch (loaded_union.layout) { + .auto, .@"extern" => .decl_empty_union, + .@"packed" => .decl_empty_packed_union, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, name_nav.name.toSlice(ip)); + } else { + const decl = zf.zir.?.getUnionDecl(loaded_union.zir_index.resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + ip.namespacePtr(loaded_union.namespace).parent.unwrap().?, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (loaded_union.field_types.len > 0) + switch (loaded_union.layout) { + .auto, .@"extern" => .type_decl_union, + .@"packed" => .type_decl_packed_union, + } + else switch (loaded_union.layout) { + .auto, .@"extern" => .type_decl_empty_union, + .@"packed" => .type_decl_empty_packed_union, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_union.name.toSlice(ip)); + } + switch (loaded_union.layout) { + .auto, .@"extern" => { + const union_layout = Type.getUnionLayout(loaded_union, zcu); + try di_w.writeUleb128(union_layout.abi_size); + try di_w.writeUleb128(union_layout.abi_align.toByteUnits().?); + try dwarf.genCaptures(pt, di_nw, loaded_union.captures); + if (loaded_union.has_runtime_tag) { + try dwarf.abbrevCode(di_nw, .tagged_union); + try dwarf.secOffset(di_nw, di_nw.ni, di_w.end + dwarf.secOffsetSize()); + { + try dwarf.abbrevCode(di_nw, .generated_field); + try dwarf.strp(&dwarf.debug_str, di_nw, "tag"); + try dwarf.refType(pt, di_nw, .fromInterned(loaded_union.enum_tag_type)); + try di_w.writeUleb128(union_layout.tagOffset()); + + for (0..loaded_union.field_types.len) |field_index| { + try dwarf.abbrevCode(di_nw, .tagged_union_field); + try dwarf.enumConstValue(di_w, loaded_tag, field_index); + { + try dwarf.abbrevCode(di_nw, .field); + try dwarf.strp( + &dwarf.debug_str, + di_nw, + loaded_tag.field_names.get(ip)[field_index].toSlice(ip), + ); + const field_ty: Type = + .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try dwarf.refType(pt, di_nw, field_ty); + try di_w.writeUleb128(union_layout.payloadOffset()); + try di_w.writeUleb128(loaded_union.field_aligns.getOrNone( + ip, + field_index, + ).toByteUnits() orelse if (field_ty.isNoReturn(zcu)) + 1 + else + field_ty.abiAlignment(zcu).toByteUnits().?); + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + } + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } else for (0..loaded_union.field_types.len) |field_index| { + try dwarf.abbrevCode(di_nw, .field); + try dwarf.strp( + &dwarf.debug_str, + di_nw, + loaded_tag.field_names.get(ip)[field_index].toSlice(ip), + ); + const field_ty: Type = + .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try dwarf.refType(pt, di_nw, field_ty); + try di_w.writeUleb128(0); + try di_w.writeUleb128(loaded_union.field_aligns.getOrNone( + ip, + field_index, + ).toByteUnits() orelse if (field_ty.isNoReturn(zcu)) + 1 + else + field_ty.abiAlignment(zcu).toByteUnits().?); + } + }, + .@"packed" => { + try dwarf.refType(pt, di_nw, .fromInterned(loaded_union.packed_backing_int_type)); + for (0..loaded_union.field_types.len) |field_index| { + try dwarf.abbrevCode(di_nw, .packed_field); + try dwarf.strp( + &dwarf.debug_str, + di_nw, + loaded_tag.field_names.get(ip)[field_index].toSlice(ip), + ); + try dwarf.refType(pt, di_nw, .fromInterned( + loaded_union.field_types.get(ip)[field_index], + )); + try di_w.writeUleb128(0); + } + }, + } + if (loaded_union.captures.len > 0 or loaded_union.field_types.len > 0) + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(val); + switch (loaded_enum.owner_union) { + .none => { + const zfi = loaded_enum.zir_index.unwrap().?.resolveFile(ip); + const zf = zcu.fileByIndex(zfi); + if (loaded_enum.captures.len > 0 or loaded_enum.is_reified) { + try dwarf.abbrevCode(di_nw, if (loaded_enum.captures.len > 0 or + loaded_enum.field_names.len > 0) + .decl_instance_enum + else + .decl_instance_empty_enum); + try dwarf.secOffset(di_nw, try dwarf.getDecl(pt, val), 0); + } else if (loaded_enum.name_nav.unwrap()) |name_ni| { + const name_nav = ip.getNav(name_ni); + const decl = zf.zir.?.getDeclaration(name_nav.srcInst(ip).resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + name_nav.analysis.?.namespace, + ).owner_type); + try dwarf.abbrevCode( + di_nw, + if (loaded_enum.field_names.len > 0) .decl_enum else .decl_empty_enum, + ); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, name_nav.name.toSlice(ip)); + } else { + const decl = + zf.zir.?.getEnumDecl(loaded_enum.zir_index.unwrap().?.resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + ip.namespacePtr(loaded_enum.namespace).parent.unwrap().?, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (loaded_enum.field_names.len > 0) + .type_decl_enum + else + .type_decl_empty_enum); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_enum.name.toSlice(ip)); + } + }, + else => { + try dwarf.abbrevCode(di_nw, if (loaded_enum.field_names.len > 0) + .generated_enum_type + else + .generated_empty_enum_type); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_enum.fqn.toSlice(ip)); + }, + } + try dwarf.refType(pt, di_nw, .fromInterned(loaded_enum.int_tag_type)); + for (0..loaded_enum.field_names.len) |field_index| { + try dwarf.abbrevCode(di_nw, .enum_field); + try dwarf.enumConstValue(di_w, loaded_enum, field_index); + try dwarf.strp( + &dwarf.debug_str, + di_nw, + loaded_enum.field_names.get(ip)[field_index].toSlice(ip), + ); + } + if (loaded_enum.captures.len > 0 or loaded_enum.field_names.len > 0) + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + // no defined size, so lowered the same as incomplete struct types + .opaque_type => return dwarf.updateConstIncompleteInner(pt, di_nw, val), + .spirv_type => unreachable, + .func_type => |func_type| { + const is_nullary = func_type.param_types.len == 0 and !func_type.is_var_args; + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, if (is_nullary) .nullary_func_type else .func_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + const cc: DW.CC = cc: { + if (zcu.getTarget().cCallingConvention()) |cc| { + if (@as(std.lang.CallingConvention.Tag, cc) == func_type.cc) { + break :cc .normal; + } + } + // For better or worse, we try to match what Clang emits. + break :cc switch (func_type.cc) { + .@"inline" => .nocall, + .async, .auto, .naked => .normal, + .x86_64_sysv => .LLVM_X86_64SysV, + .x86_64_win => .LLVM_Win64, + .x86_64_regcall_v3_sysv => .LLVM_X86RegCall, + .x86_64_regcall_v4_win => .LLVM_X86RegCall, + .x86_64_vectorcall => .LLVM_vectorcall, + .x86_sysv, .x86_win, .x86_mingw => .normal, + .x86_64_preserve_none => .LLVM_PreserveNone, + .x86_stdcall => .BORLAND_stdcall, + .x86_fastcall => .BORLAND_msfastcall, + .x86_thiscall => .BORLAND_thiscall, + .x86_thiscall_mingw => .BORLAND_thiscall, + .x86_regcall_v3 => .LLVM_X86RegCall, + .x86_regcall_v4_win => .LLVM_X86RegCall, + .x86_vectorcall => .LLVM_vectorcall, + + .aarch64_aapcs => .normal, + .aarch64_aapcs_darwin => .normal, + .aarch64_aapcs_win => .normal, + .aarch64_vfabi => .LLVM_AAPCS, + .aarch64_vfabi_sve => .LLVM_AAPCS, + .aarch64_preserve_none => .LLVM_PreserveNone, + + .arm_aapcs => .LLVM_AAPCS, + .arm_aapcs_vfp => .LLVM_AAPCS_VFP, + + .riscv64_lp64_v, + .riscv32_ilp32_v, + => .LLVM_RISCVVectorCall, + + .m68k_rtd => .LLVM_M68kRTD, + + .sh_renesas => .GNU_renesas_sh, + + .amdgcn_kernel => .LLVM_OpenCLKernel, + .nvptx_kernel, + .spirv_kernel, + => .nocall, + + .x86_64_interrupt, + .x86_interrupt, + .arm_interrupt, + .mips64_interrupt, + .mips_interrupt, + .riscv64_interrupt, + .riscv32_interrupt, + .sh_interrupt, + .arc_interrupt, + .avr_builtin, + .avr_signal, + .avr_interrupt, + .csky_interrupt, + .m68k_interrupt, + .microblaze_interrupt, + .msp430_interrupt, + => .normal, + + else => .nocall, + }; + }; + try di_w.writeByte(@backingInt(cc)); + try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type)); + if (!is_nullary) { + for (0..func_type.param_types.len) |param_index| { + try dwarf.abbrevCode(di_nw, .unnamed_param); + try dwarf.refType(pt, di_nw, .fromInterned( + func_type.param_types.get(ip)[param_index], + )); + } + if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args); + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + }, + .error_set_type => |error_set_type| { + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode( + di_nw, + if (error_set_type.names.len > 0) .generated_enum_type else .generated_empty_enum_type, + ); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try dwarf.refType(pt, di_nw, try pt.intType(.unsigned, zcu.errorSetBits())); + for (0..error_set_type.names.len) |field_index| { + const field_name = error_set_type.names.get(ip)[field_index]; + try dwarf.abbrevCode(di_nw, .enum_field); + try di_w.writeUleb128(DW.FORM.udata); + try di_w.writeUleb128(ip.getErrorValueIfExists(field_name).?); + try dwarf.strp(&dwarf.debug_str, di_nw, field_name.toSlice(ip)); + } + if (error_set_type.names.len > 0) try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + .inferred_error_set_type => |func| { + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, .inferred_error_set_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try dwarf.refType(pt, di_nw, switch (ies: { + const fi = dwarf.getFuncIfExists(ip.indexToKey(func).func.owner_nav) orelse + break :ies .none; + break :ies switch (fi.get(dwarf).state) { + .unresolved => .none, + .resolved => ip.funcIesResolvedUnordered(func), + }; + }) { + .none => .anyerror, + else => |ies| .fromInterned(ies), + }); + }, + + else => return, + .func => |func| { + const fn_ty = ip.indexToKey(func.ty).func_type; + const nav = ip.getNav(func.owner_nav); + const inst_info = nav.srcInst(ip).resolveFull(ip).?; + const zf = zcu.fileByIndex(inst_info.file); + const decl = zf.zir.?.getDeclaration(inst_info.inst); + const parent_ty: Type = .fromInterned(ip.namespacePtr(nav.analysis.?.namespace).owner_type); + try dwarf.abbrevCode(di_nw, .decl_func_generic); + try dwarf.refType(pt, di_nw, parent_ty); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, nav.name.toSlice(ip)); + try dwarf.refType(pt, di_nw, .fromInterned(fn_ty.return_type)); + var param_index: u32 = 0; + for (zf.zir.?.getParamBody(func.zir_body_inst.resolve(ip).?)) |param_inst| { + switch (zf.zir.?.getParamName(param_inst) orelse break) { + .empty => try dwarf.abbrevCode(di_nw, .unnamed_param), + else => |param_name| { + try dwarf.abbrevCode(di_nw, .param); + try dwarf.strp(&dwarf.debug_str, di_nw, zf.zir.?.nullTerminatedString( + param_name, + )); + }, + } + try dwarf.refType(pt, di_nw, .fromInterned( + fn_ty.param_types.get(&zcu.intern_pool)[param_index], + )); + param_index += 1; + } + if (fn_ty.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args); + try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + }, + + .memoized_call => unreachable, // not a value + } + try dwarf.genDebugInfoPadding(di_w, di_w.unusedCapacityLen()); +} + +fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum { unpacked, opv_null, error_set, pointer } { + if (opt_child_type.isNoReturn(zcu)) return .opv_null; + return switch (opt_child_type.toIntern()) { + .anyerror_type => .error_set, + else => switch (zcu.intern_pool.indexToKey(opt_child_type.toIntern())) { + else => .unpacked, + .error_set_type, .inferred_error_set_type => .error_set, + .ptr_type => |ptr_type| if (ptr_type.flags.is_allowzero) .unpacked else .pointer, + }, + }; +} + +pub fn updateConstIncomplete( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + val: InternPool.Index, +) link.Error!void { + log.debug("updateConstIncomplete({f})", .{Value.fromInterned(val).fmtValue(pt)}); + dwarf.updateConstIncompleteInner(pt, di_nw, val) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dwarf.reportWriteError(di_nw), + }; +} +fn updateConstIncompleteInner( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + val: InternPool.Index, +) link.EmitError!void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const di_w = &di_nw.interface; + done: { + const kind: enum { @"struct", @"union", @"enum" }, const zf, const src_line, const src_column, const is_reified, const captures, const name, const maybe_name_nav, const namespace = container: switch (ip.indexToKey(val)) { + .struct_type => { + const loaded_struct = ip.loadStructType(val); + const src_inst = loaded_struct.zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + switch (src_inst.inst) { + .main_struct_inst => { + const ui = dwarf.getUnit(zf.mod.?); + _, const fi = try ui.get(dwarf).getFile(zcu.gpa, ui, src_inst.file); + try dwarf.abbrevCode(di_nw, .empty_file); + try di_w.writeUleb128(@backingInt(fi)); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_struct.name.toSlice(ip)); + try di_w.writeByte(@intFromBool(true)); + break :done; + }, + else => { + const data = + zf.zir.?.instructions.items(.data)[@backingInt(src_inst.inst)].extended; + const src_line, const src_column = src_loc: switch (data.opcode) { + else => unreachable, + .struct_decl => { + const decl = zf.zir.?.getStructDecl(src_inst.inst); + break :src_loc .{ decl.src_line, decl.src_column }; + }, + .reify_struct => { + const decl = zf.zir.?.extraData( + std.zig.Zir.Inst.ReifyStruct, + data.operand, + ).data; + break :src_loc .{ decl.src_line, decl.src_column }; + }, + }; + break :container .{ + .@"struct", + zf, + src_line, + src_column, + loaded_struct.is_reified, + loaded_struct.captures, + loaded_struct.name, + loaded_struct.name_nav, + loaded_struct.namespace, + }; + }, + } + }, + .union_type => { + const loaded_union = ip.loadUnionType(val); + const src_inst = loaded_union.zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const data = zf.zir.?.instructions.items(.data)[@backingInt(src_inst.inst)].extended; + const src_line, const src_column = src_loc: switch (data.opcode) { + else => unreachable, + .union_decl => { + const decl = zf.zir.?.getUnionDecl(src_inst.inst); + break :src_loc .{ decl.src_line, decl.src_column }; + }, + .reify_union => { + const decl = zf.zir.?.extraData(std.zig.Zir.Inst.ReifyUnion, data.operand).data; + break :src_loc .{ decl.src_line, decl.src_column }; + }, + }; + break :container .{ + .@"union", + zf, + src_line, + src_column, + loaded_union.is_reified, + loaded_union.captures, + loaded_union.name, + loaded_union.name_nav, + loaded_union.namespace, + }; + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(val); + const zir_index = loaded_enum.zir_index.unwrap() orelse { + try dwarf.abbrevCode(di_nw, .generated_empty_struct_type); + try dwarf.strp(&dwarf.debug_str, di_nw, loaded_enum.name.toSlice(ip)); + try di_w.writeByte(@intFromBool(true)); + break :done; + }; + const src_inst = zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const data = zf.zir.?.instructions.items(.data)[@backingInt(src_inst.inst)].extended; + const src_line, const src_column = src_loc: switch (data.opcode) { + else => unreachable, + .enum_decl => { + const decl = zf.zir.?.getEnumDecl(src_inst.inst); + break :src_loc .{ decl.src_line, decl.src_column }; + }, + .reify_enum => { + const decl = zf.zir.?.extraData(std.zig.Zir.Inst.ReifyEnum, data.operand).data; + break :src_loc .{ decl.src_line, decl.src_column }; + }, + }; + break :container .{ + .@"enum", + zf, + src_line, + src_column, + loaded_enum.is_reified, + loaded_enum.captures, + loaded_enum.name, + loaded_enum.name_nav, + loaded_enum.namespace, + }; + }, + // always complete, but forwarded from `updateConstInner` + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(val); + const src_inst = loaded_opaque.zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const decl = zf.zir.?.getOpaqueDecl(src_inst.inst); + break :container .{ + .@"struct", + zf, + decl.src_line, + decl.src_column, + false, + loaded_opaque.captures, + loaded_opaque.name, + loaded_opaque.name_nav, + loaded_opaque.namespace, + }; + }, + else => |val_key| break :done switch (val_key.typeOf()) { + .type_type => { + const name = try zcu.gpa.print("{f}", .{Type.fromInterned(val).fmt(pt)}); + defer zcu.gpa.free(name); + try dwarf.abbrevCode(di_nw, .generated_empty_struct_type); + try dwarf.strp(&dwarf.debug_str, di_nw, name); + try di_w.writeByte(@intFromBool(true)); + }, + else => |ty| { + try dwarf.abbrevCode(di_nw, .undefined_comptime_value); + try dwarf.refType(pt, di_nw, .fromInterned(ty)); + }, + }, + }; + if (captures.len > 0 or is_reified) { + try dwarf.abbrevCode(di_nw, if (captures.len > 0) switch (kind) { + .@"struct" => .decl_instance_incomplete_struct, + .@"union" => .decl_instance_incomplete_union, + .@"enum" => .decl_instance_incomplete_enum, + } else switch (kind) { + .@"struct" => .decl_instance_empty_incomplete_struct, + .@"union" => .decl_instance_empty_incomplete_union, + .@"enum" => .decl_instance_empty_incomplete_enum, + }); + try dwarf.secOffset(di_nw, try dwarf.getDecl(pt, val), 0); + } else if (maybe_name_nav.unwrap()) |name_ni| { + const name_nav = ip.getNav(name_ni); + const decl = zf.zir.?.getDeclaration(name_nav.srcInst(ip).resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + name_nav.analysis.?.namespace, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (captures.len > 0) switch (kind) { + .@"struct" => .decl_incomplete_struct, + .@"union" => .decl_incomplete_union, + .@"enum" => .decl_incomplete_enum, + } else switch (kind) { + .@"struct" => .decl_empty_incomplete_struct, + .@"union" => .decl_empty_incomplete_union, + .@"enum" => .decl_empty_incomplete_enum, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, name_nav.name.toSlice(ip)); + } else { + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + ip.namespacePtr(namespace).parent.unwrap().?, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (captures.len > 0) switch (kind) { + .@"struct" => .type_decl_incomplete_struct, + .@"union" => .type_decl_incomplete_union, + .@"enum" => .type_decl_incomplete_enum, + } else switch (kind) { + .@"struct" => .type_decl_empty_incomplete_struct, + .@"union" => .type_decl_empty_incomplete_union, + .@"enum" => .type_decl_empty_incomplete_enum, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, src_line + 1, dwarf.endian); + try di_w.writeUleb128(src_column + 1); + try dwarf.strp(&dwarf.debug_str, di_nw, name.toSlice(ip)); + } + try dwarf.genCaptures(pt, di_nw, captures); + if (captures.len > 0) try di_w.writeByte(@backingInt(AbbrevCode.null)); + } + try dwarf.genDebugInfoPadding(di_w, di_w.unusedCapacityLen()); +} + +fn genCaptures( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + captures: anytype, +) link.EmitError!void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + for (captures.get(ip)) |capture| switch (capture.unwrap()) { + .@"comptime" => |capture_val| { + const ty: Type = .fromInterned(ip.typeOf(capture_val)); + const ty_class = ty.classify(zcu); + try dwarf.abbrevCode(di_nw, switch (ty_class) { + .no_possible_value => unreachable, + .one_possible_value => .comptime_capture, + .runtime => .comptime_capture_runtime, + .partially_comptime => .comptime_capture_partially_comptime, + .fully_comptime => .comptime_capture_fully_comptime, + }); + try dwarf.refType(pt, di_nw, ty); + if (ty_class.hasRuntimeBits()) try dwarf.blockConst(pt, di_nw, .fromInterned(capture_val)); + if (ty_class.comptimeOnly()) try dwarf.refConst(pt, di_nw, .fromInterned(capture_val)); + }, + .runtime => |capture_ty| { + try dwarf.abbrevCode(di_nw, .runtime_capture); + try dwarf.refType(pt, di_nw, .fromInterned(capture_ty)); + }, + .nav_val => |capture_nav| { + const gi = try dwarf.getGlobal(capture_nav); + try dwarf.abbrevCode(di_nw, .nav_capture); + try dwarf.exprLoc(di_nw, .{ .implicit_pointer = .{ + .node = gi.get(dwarf).debug_info_ni.unwrap().?, + } }); + }, + .nav_ref => |capture_nav| { + const gi = try dwarf.getGlobal(capture_nav); + try dwarf.abbrevCode(di_nw, .nav_capture); + try dwarf.exprLoc(di_nw, .{ .stack_value = &.{ .implicit_pointer = .{ + .node = gi.get(dwarf).debug_info_ni.unwrap().?, + } } }); + }, + }; +} + +pub fn genDecl( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + instance_val: InternPool.Index, +) link.Error!void { + log.debug("genDecl({f})", .{Value.fromInterned(instance_val).fmtValue(pt)}); + dwarf.genDeclInner(pt, di_nw, instance_val) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dwarf.reportWriteError(di_nw), + }; +} +fn genDeclInner( + dwarf: *Dwarf, + pt: Zcu.PerThread, + di_nw: *link.MappedFile.Node.Writer, + instance_val: InternPool.Index, +) link.EmitError!void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const di_w = &di_nw.interface; + done: { + const kind: enum { @"struct", @"union", @"enum" }, const zf, const src_line, const src_column, const capture_names, const captures, const name, const maybe_name_nav, const namespace = container: switch (ip.indexToKey(instance_val)) { + else => unreachable, + .struct_type => { + const loaded_struct = ip.loadStructType(instance_val); + const src_inst = loaded_struct.zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const inst = zf.zir.?.instructions.get(@backingInt(src_inst.inst)); + const src_line, const src_column, const capture_names = decl: switch (inst.tag) { + else => unreachable, + .struct_init, .struct_init_ref => { + const decl = zf.zir.?.extraData( + std.zig.Zir.Inst.StructInit, + inst.data.pl_node.payload_index, + ).data; + break :decl .{ decl.src_line, decl.src_column, &.{} }; + }, + .struct_init_anon => { + const decl = zf.zir.?.extraData( + std.zig.Zir.Inst.StructInitAnon, + inst.data.pl_node.payload_index, + ).data; + break :decl .{ decl.src_line, decl.src_column, &.{} }; + }, + .extended => switch (inst.data.extended.opcode) { + else => unreachable, + .struct_decl => { + const decl = zf.zir.?.getStructDecl(src_inst.inst); + break :decl .{ decl.src_line, decl.src_column, decl.capture_names }; + }, + .reify_struct => { + const decl = zf.zir.?.extraData( + std.zig.Zir.Inst.ReifyStruct, + inst.data.extended.operand, + ).data; + break :decl .{ decl.src_line, decl.src_column, &.{} }; + }, + }, + }; + break :container .{ + .@"struct", + zf, + src_line, + src_column, + capture_names, + loaded_struct.captures, + loaded_struct.name, + loaded_struct.name_nav, + loaded_struct.namespace, + }; + }, + .union_type => { + const loaded_union = ip.loadUnionType(instance_val); + const src_inst = loaded_union.zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const inst = zf.zir.?.instructions.get(@backingInt(src_inst.inst)); + const src_line, const src_column, const capture_names = decl: switch (inst.tag) { + else => unreachable, + .extended => switch (inst.data.extended.opcode) { + else => unreachable, + .union_decl => { + const decl = zf.zir.?.getUnionDecl(src_inst.inst); + break :decl .{ decl.src_line, decl.src_column, decl.capture_names }; + }, + .reify_union => { + const decl = zf.zir.?.extraData( + std.zig.Zir.Inst.ReifyUnion, + inst.data.extended.operand, + ).data; + break :decl .{ decl.src_line, decl.src_column, &.{} }; + }, + }, + }; + break :container .{ + .@"union", + zf, + src_line, + src_column, + capture_names, + loaded_union.captures, + loaded_union.name, + loaded_union.name_nav, + loaded_union.namespace, + }; + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(instance_val); + const src_inst = loaded_enum.zir_index.unwrap().?.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const inst = zf.zir.?.instructions.get(@backingInt(src_inst.inst)); + const src_line, const src_column, const capture_names = decl: switch (inst.tag) { + else => unreachable, + .extended => switch (inst.data.extended.opcode) { + else => unreachable, + .enum_decl => { + const decl = zf.zir.?.getEnumDecl(src_inst.inst); + break :decl .{ decl.src_line, decl.src_column, decl.capture_names }; + }, + .reify_enum => { + const decl = zf.zir.?.extraData( + std.zig.Zir.Inst.ReifyEnum, + inst.data.extended.operand, + ).data; + break :decl .{ decl.src_line, decl.src_column, &.{} }; + }, + }, + }; + break :container .{ + .@"enum", + zf, + src_line, + src_column, + capture_names, + loaded_enum.captures, + loaded_enum.name, + loaded_enum.name_nav, + loaded_enum.namespace, + }; + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(instance_val); + const src_inst = loaded_opaque.zir_index.resolveFull(ip) orelse { + try dwarf.lostTracking(di_nw); + break :done; + }; + const zf = zcu.fileByIndex(src_inst.file); + const decl = zf.zir.?.getOpaqueDecl(src_inst.inst); + break :container .{ + .@"struct", + zf, + decl.src_line, + decl.src_column, + decl.capture_names, + loaded_opaque.captures, + loaded_opaque.name, + loaded_opaque.name_nav, + loaded_opaque.namespace, + }; + }, + }; + if (maybe_name_nav.unwrap()) |name_ni| { + const name_nav = ip.getNav(name_ni); + const decl = zf.zir.?.getDeclaration(name_nav.srcInst(ip).resolve(ip).?); + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + name_nav.analysis.?.namespace, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (captures.len > 0) switch (kind) { + .@"struct" => .decl_specification_struct, + .@"union" => .decl_specification_union, + .@"enum" => .decl_specification_enum, + } else switch (kind) { + .@"struct" => .decl_specification_empty_struct, + .@"union" => .decl_specification_empty_union, + .@"enum" => .decl_specification_empty_enum, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian); + try di_w.writeUleb128(decl.src_column + 1); + try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private); + try dwarf.strp(&dwarf.debug_str, di_nw, name.toSlice(ip)); + } else { + const parent_ni = try dwarf.getDecl(pt, ip.namespacePtr( + ip.namespacePtr(namespace).parent.unwrap().?, + ).owner_type); + try dwarf.abbrevCode(di_nw, if (captures.len > 0) switch (kind) { + .@"struct" => .type_decl_specification_struct, + .@"union" => .type_decl_specification_union, + .@"enum" => .type_decl_specification_enum, + } else switch (kind) { + .@"struct" => .type_decl_specification_empty_struct, + .@"union" => .type_decl_specification_empty_union, + .@"enum" => .type_decl_specification_empty_enum, + }); + try dwarf.secOffset(di_nw, parent_ni, 0); + try di_w.writeInt(u32, src_line + 1, dwarf.endian); + try di_w.writeUleb128(src_column + 1); + try dwarf.strp(&dwarf.debug_str, di_nw, name.toSlice(ip)); + } + for (capture_names, captures.get(ip)) |capture_name, capture| { + try dwarf.abbrevCode(di_nw, .capture_specification); + switch (capture.unwrap()) { + .@"comptime", .runtime, .nav_val => try dwarf.strp( + &dwarf.debug_str, + di_nw, + zf.zir.?.nullTerminatedString(capture_name), + ), + .nav_ref => { + const capture_name_slice = try zcu.gpa.print("&{s}", .{ + zf.zir.?.nullTerminatedString(capture_name), + }); + defer zcu.gpa.free(capture_name_slice); + try dwarf.strp(&dwarf.debug_str, di_nw, capture_name_slice); + }, + } + } + if (captures.len > 0) try di_w.writeUleb128(@backingInt(AbbrevCode.null)); + } + try dwarf.genDebugInfoPadding(di_w, di_w.unusedCapacityLen()); +} + +pub fn updateLineNumber( + dwarf: *Dwarf, + mf: *link.MappedFile, + inst: InternPool.TrackedInst.Index, + line: u32, +) void { + const di = dwarf.getDeclIfExists(inst) orelse return; + const decl_ni = di.get(dwarf).debug_info_ni.unwrap().?; + std.mem.writeInt( + u32, + decl_ni.slice(mf)[AbbrevCode.decl_size..][0..4], + line + 1, + dwarf.endian, + ); +} + +pub fn lostTracking(dwarf: *Dwarf, di_nw: *link.MappedFile.Node.Writer) link.EmitError!void { + try dwarf.abbrevCode(di_nw, .decl_lost); +} + +fn refAbbrevCodeIfExists( + dwarf: *Dwarf, + abbrev_code: AbbrevCode, +) ?@typeInfo(AbbrevCode).@"enum".tag_type { + assert(abbrev_code != .null); + return if (dwarf.debug_abbrev.set.contains(abbrev_code)) @backingInt(abbrev_code) else null; +} +fn refAbbrevCode( + dwarf: *Dwarf, + mf: *link.MappedFile, + abbrev_code: AbbrevCode, +) link.Error!@typeInfo(AbbrevCode).@"enum".tag_type { + if (dwarf.refAbbrevCodeIfExists(abbrev_code)) |backing_int| { + @branchHint(.likely); + return backing_int; + } + var da_nw: link.MappedFile.Node.Writer = undefined; + dwarf.debug_abbrev.ni.unwrap().?.writer(dwarf.lf.comp.gpa, mf, &da_nw); + defer da_nw.deinit(); + dwarf.genDebugAbbrev(&da_nw, abbrev_code) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dwarf.reportWriteError(&da_nw), + }; + dwarf.debug_abbrev.set.insert(abbrev_code); + return dwarf.refAbbrevCodeIfExists(abbrev_code).?; +} +fn abbrevCode( + dwarf: *Dwarf, + nw: *link.MappedFile.Node.Writer, + abbrev_code: AbbrevCode, +) link.EmitError!void { + try nw.interface.writeUleb128(try dwarf.refAbbrevCode(nw.mf, abbrev_code)); +} + +fn genDebugAbbrev( + dwarf: *Dwarf, + da_nw: *link.MappedFile.Node.Writer, + abbrev_code: AbbrevCode, +) link.EmitError!void { + const abbrev = AbbrevCode.abbrevs.get(abbrev_code); + const da_w = &da_nw.interface; + da_w.end = dwarf.debug_abbrev.end; + try da_w.writeUleb128(@backingInt(abbrev_code)); + try da_w.writeUleb128(@backingInt(abbrev.tag)); + try da_w.writeByte(if (abbrev.children) DW.CHILDREN.yes else DW.CHILDREN.no); + for (abbrev.attrs) |*attr| { + try da_w.writeUleb128(@backingInt(switch (attr[0]) { + else => |at| at, + .ZIG_call_line_relative => |at| if (dwarf.lf.comp.config.incremental) at else .call_line, + })); + try da_w.writeUleb128(@backingInt(attr[1])); + } + for (0..2) |_| try da_w.writeUleb128(0); + dwarf.debug_abbrev.end = da_w.end; +} + +pub fn secOffsetSize(dwarf: *Dwarf) usize { + return switch (dwarf.format) { + .@"32" => 4, + .@"64" => 8, + }; +} +fn secOffsetPlaceholder(dwarf: *Dwarf, w: *std.Io.Writer) std.Io.Writer.Error!void { + @memset(try w.writableSlice(dwarf.secOffsetSize()), undefined); +} +fn secOffset( + dwarf: *Dwarf, + nw: *link.MappedFile.Node.Writer, + target_ni: link.MappedFile.Node.Index, + addend: usize, +) link.EmitError!void { + const offset = nw.interface.end; + try dwarf.secOffsetPlaceholder(&nw.interface); + if (dwarf.lf.cast(.elf2)) |elf| try elf.addNodeReloc( + nw.ni, + offset, + target_ni, + @bitCast(@as(u64, addend)), + switch (dwarf.format) { + .@"32" => .abs32, + .@"64" => .abs64, + }, + ) else unreachable; +} + +fn addrPlaceholder(dwarf: *Dwarf, w: *std.Io.Writer) std.Io.Writer.Error!void { + @memset(try w.writableSlice(@backingInt(dwarf.address_size)), undefined); +} +fn addrSym( + dwarf: *Dwarf, + nw: *link.MappedFile.Node.Writer, + target_si: link.File.SymbolId, + addend: usize, +) link.EmitError!void { + const offset = nw.interface.end; + try dwarf.addrPlaceholder(&nw.interface); + if (dwarf.lf.cast(.elf2)) |elf| try elf.addReloc( + @bitCast(nw.ni), + offset, + target_si, + @bitCast(@as(u64, addend)), + .absAddr(elf), + ) else unreachable; +} + +fn blockConst( + dwarf: *Dwarf, + pt: Zcu.PerThread, + nw: *link.MappedFile.Node.Writer, + val: Value, +) link.EmitError!void { + const ty = val.typeOf(pt.zcu); + const size = ty.abiSize(pt.zcu); + try nw.interface.writeUleb128(size); + const start = nw.interface.end; + if (size > 0) try codegen.generateSymbol( + dwarf.lf, + pt, + val, + &nw.interface, + .{ .atom_index = @bitCast(nw.ni) }, + ); + assert(start + size == nw.interface.end); +} + +fn refType( + dwarf: *Dwarf, + pt: Zcu.PerThread, + nw: *link.MappedFile.Node.Writer, + ty: Type, +) link.EmitError!void { + return dwarf.refConst(pt, nw, ty.toValue()); +} +fn refConst( + dwarf: *Dwarf, + pt: Zcu.PerThread, + nw: *link.MappedFile.Node.Writer, + val: Value, +) link.EmitError!void { + try dwarf.secOffset(nw, Const.get(try dwarf.getConst(pt, val), dwarf).debug_info_ni.unwrap().?, 0); +} + +fn bigIntConstValue( + dwarf: *Dwarf, + di_w: *std.Io.Writer, + ty: Type, + big_int: std.math.big.int.Const, +) link.EmitError!void { + const zcu = dwarf.lf.comp.zcu.?; + const signedness = switch (ty.toIntern()) { + .comptime_int_type => .signed, + else => ty.intInfo(zcu).signedness, + }; + const bits = @max(1, big_int.bitCountTwosCompForSignedness(signedness)); + if (bits <= 64) { + try di_w.writeUleb128(@as(u13, switch (signedness) { + .signed => DW.FORM.sdata, + .unsigned => DW.FORM.udata, + })); + var bit: usize = 0; + var carry: u1 = 1; + for (try di_w.writableSlice(@divCeil(bits, 7))) |*byte| { + const limb_bits = @typeInfo(std.math.big.Limb).int.bits; + const limb_index = bit / limb_bits; + const limb_shift: std.math.Log2Int(std.math.big.Limb) = @intCast(bit % limb_bits); + const low_abs_part: u7 = @truncate(big_int.limbs[limb_index] >> limb_shift); + const abs_part = if (limb_shift > limb_bits - 7 and + limb_index + 1 < big_int.limbs.len) + abs_part: { + const high_abs_part: u7 = @truncate(big_int.limbs[limb_index + 1] << -%limb_shift); + break :abs_part high_abs_part | low_abs_part; + } else low_abs_part; + const twos_comp_part = if (big_int.positive) abs_part else twos_comp_part: { + const twos_comp_part, carry = @addWithOverflow(~abs_part, carry); + break :twos_comp_part twos_comp_part; + }; + bit += 7; + byte.* = @as(u8, if (bit < bits) 0x80 else 0x00) | twos_comp_part; + } + } else { + try di_w.writeUleb128(DW.FORM.block); + const size = switch (ty.toIntern()) { + .comptime_int_type => @divCeil(bits, 8), + else => ty.abiSize(zcu), + }; + try di_w.writeUleb128(size); + big_int.writeTwosComplement(try di_w.writableSlice(@intCast(size)), dwarf.endian); + } +} + +fn enumConstValue( + dwarf: *Dwarf, + di_w: *std.Io.Writer, + loaded_enum: InternPool.LoadedEnumType, + field_index: usize, +) link.EmitError!void { + const zcu = dwarf.lf.comp.zcu.?; + var big_int_space: Value.BigIntSpace = undefined; + try dwarf.bigIntConstValue( + di_w, + .fromInterned(loaded_enum.int_tag_type), + if (loaded_enum.field_values.len > 0) + Value.fromInterned(loaded_enum.field_values.get(&zcu.intern_pool)[field_index]) + .toBigInt(&big_int_space, zcu) + else + std.math.big.int.Mutable.init(&big_int_space.limbs, field_index).toConst(), + ); +} + +fn exprLoc(dwarf: *Dwarf, nw: *link.MappedFile.Node.Writer, loc: Loc) link.EmitError!void { + var buf: [@max(8, std.atomic.cache_line)]u8 = undefined; + var dw: std.Io.Writer.Discarding = .init(&buf); + try loc.write(.{ .io = &dw.writer }, dwarf); + + try nw.interface.writeUleb128(dw.fullCount()); + try loc.write(.{ .mf = nw }, dwarf); +} + +fn strp(dwarf: *Dwarf, s: *Str, nw: *link.MappedFile.Node.Writer, str: []const u8) link.EmitError!void { + const comp = dwarf.lf.comp; + try dwarf.secOffset(nw, s.ni.unwrap().?, s.get(comp.gpa, nw.mf, str) catch |err| switch (err) { + error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{ + nw.mf.io_err.?, + }), + else => |e| return e, + }); +} + +fn reportWriteError(dwarf: *Dwarf, nw: *const link.MappedFile.Node.Writer) link.Error { + switch (nw.err.?) { + else => |e| return e, + error.MappedFileIo => return dwarf.lf.comp.link_diags.fail( + "failed to write output file: {t}", + .{nw.mf.io_err.?}, + ), + } +} + +fn constPoolUser(dwarf: *Dwarf) link.ConstPool.User { + return if (dwarf.lf.cast(.elf2)) |elf| .{ + .elf2 = elf, + } else unreachable; +} + +fn DeclValEnum(comptime T: type) type { + const decl_names = @typeInfo(T).@"struct".decl_names; + @setEvalBranchQuota(10 * decl_names.len); + var field_names: [decl_names.len][]const u8 = undefined; + var fields_len = 0; + var min_value: ?comptime_int = null; + var max_value: ?comptime_int = null; + for (decl_names) |decl_name| { + if (std.mem.startsWith(u8, decl_name, "HP_") or + std.mem.endsWith(u8, decl_name, "_user")) continue; + const value = @field(T, decl_name); + field_names[fields_len] = decl_name; + fields_len += 1; + if (min_value == null or min_value.? > value) min_value = value; + if (max_value == null or max_value.? < value) max_value = value; + } + if (fields_len == 0) return enum {}; + const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0); + var field_vals: [fields_len]TagInt = undefined; + for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name); + return @Enum(TagInt, .exhaustive, field_names[0..fields_len], &field_vals); +} + +pub const AbbrevCode = enum { + null, + // padding codes must be one byte uleb128 values to function + pad_1, + pad_n, + // decl, specification, and instance codes are assumed to all have the same uleb128 size + decl_lost, + decl_alias, + decl_empty_incomplete_enum, + decl_incomplete_enum, + decl_empty_enum, + decl_enum, + type_decl_empty_incomplete_enum, + type_decl_incomplete_enum, + type_decl_empty_enum, + type_decl_enum, + decl_empty_incomplete_struct, + decl_incomplete_struct, + decl_empty_struct, + decl_struct, + type_decl_empty_incomplete_struct, + type_decl_incomplete_struct, + type_decl_empty_struct, + type_decl_struct, + decl_empty_packed_struct, + decl_packed_struct, + type_decl_empty_packed_struct, + type_decl_packed_struct, + decl_empty_incomplete_union, + decl_incomplete_union, + decl_empty_union, + decl_union, + type_decl_empty_incomplete_union, + type_decl_incomplete_union, + type_decl_empty_union, + type_decl_union, + decl_empty_packed_union, + decl_packed_union, + type_decl_empty_packed_union, + type_decl_packed_union, + decl_var, + decl_const, + decl_const_runtime_bits, + decl_const_comptime_state, + decl_const_runtime_bits_comptime_state, + decl_nullary_func, + decl_func, + decl_nullary_func_generic, + decl_func_generic, + decl_extern_nullary_func, + decl_extern_func, + decl_specification_empty_struct, + decl_specification_struct, + type_decl_specification_empty_struct, + type_decl_specification_struct, + decl_specification_empty_enum, + decl_specification_enum, + type_decl_specification_empty_enum, + type_decl_specification_enum, + decl_specification_empty_union, + decl_specification_union, + type_decl_specification_empty_union, + type_decl_specification_union, + decl_specification_func, + decl_instance_alias, + decl_instance_empty_incomplete_enum, + decl_instance_incomplete_enum, + decl_instance_empty_enum, + decl_instance_enum, + decl_instance_empty_incomplete_struct, + decl_instance_incomplete_struct, + decl_instance_empty_struct, + decl_instance_struct, + decl_instance_empty_packed_struct, + decl_instance_packed_struct, + decl_instance_empty_incomplete_union, + decl_instance_incomplete_union, + decl_instance_empty_union, + decl_instance_union, + decl_instance_empty_packed_union, + decl_instance_packed_union, + decl_instance_var, + decl_instance_const, + decl_instance_const_runtime_bits, + decl_instance_const_comptime_state, + decl_instance_const_runtime_bits_comptime_state, + decl_instance_nullary_func, + decl_instance_func, + decl_instance_nullary_func_generic, + decl_instance_func_generic, + decl_instance_extern_nullary_func, + decl_instance_extern_func, + // the rest are unrestricted other than empty variants must not be longer + // than the non-empty variant, and so should appear first + compile_unit, + module, + module_dependency, + empty_file, + file, + access, + enum_field, + generated_field, + field, + field_default_fully_runtime, + field_default_partially_comptime, + field_default_fully_comptime, + field_comptime, + field_comptime_fully_runtime, + field_comptime_partially_comptime, + field_comptime_fully_comptime, + packed_field, + tagged_union, + tagged_union_field, + tagged_union_default_field, + void_type, + numeric_type, + inferred_error_set_type, + ptr_type, + ptr_sentinel_type, + ptr_aligned_type, + ptr_aligned_sentinel_type, + is_const, + is_volatile, + array_type, + array_sentinel_type, + vector_type, + array_index, + array_len, + nullary_func_type, + func_type, + param, + unnamed_param, + is_var_args, + generated_empty_enum_type, + generated_enum_type, + generated_empty_struct_type, + generated_struct_type, + generated_union_type, + capture_specification, + comptime_capture, + comptime_capture_runtime, + comptime_capture_partially_comptime, + comptime_capture_fully_comptime, + runtime_capture, + nav_capture, + builtin_extern_nullary_func, + builtin_extern_func, + builtin_extern_var, + empty_block, + block, + empty_inlined_func, + inlined_func, + arg, + unnamed_arg, + comptime_arg, + comptime_arg_fully_runtime, + comptime_arg_partially_comptime, + comptime_arg_fully_comptime, + unnamed_comptime_arg, + unnamed_comptime_arg_fully_runtime, + unnamed_comptime_arg_partially_comptime, + unnamed_comptime_arg_fully_comptime, + extern_param, + local_var, + local_const, + local_const_fully_runtime, + local_const_partially_comptime, + local_const_fully_comptime, + undefined_comptime_value, + comptime_value, + location_comptime_value, + aggregate_undefined_comptime_value, + aggregate_comptime_value, + aggregate_location_comptime_value, + comptime_value_field_runtime_bits, + comptime_value_field_comptime_state, + comptime_value_elem_runtime_bits, + comptime_value_elem_comptime_state, + + const decl_size = uleb128Size(@backingInt(AbbrevCode.decl_instance_extern_func)); + comptime { + assert(uleb128Size(@backingInt(AbbrevCode.pad_1)) == 1); + assert(uleb128Size(@backingInt(AbbrevCode.pad_n)) == 1); + assert(uleb128Size(@backingInt(AbbrevCode.decl_alias)) == decl_size); + } + + const Attr = struct { + DeclValEnum(DW.AT), + DeclValEnum(DW.FORM), + }; + const decl_attrs = &[_]Attr{ + .{ .ZIG_parent, .ref_addr }, + .{ .decl_line, .data4 }, + .{ .decl_column, .udata }, + .{ .accessibility, .data1 }, + .{ .name, .strp }, + }; + const type_decl_attrs = &[_]Attr{ + .{ .ZIG_parent, .ref_addr }, + .{ .decl_line, .data4 }, + .{ .decl_column, .udata }, + .{ .name, .strp }, + }; + const decl_specification_attrs = decl_attrs ++ &[_]Attr{ + .{ .declaration, .flag_present }, + }; + const type_decl_specification_attrs = type_decl_attrs ++ &[_]Attr{ + .{ .declaration, .flag_present }, + }; + const decl_instance_attrs = &[_]Attr{ + .{ .specification, .ref_addr }, + }; + + const abbrevs = std.EnumArray(AbbrevCode, struct { + tag: DeclValEnum(DW.TAG), + children: bool = false, + attrs: []const Attr = &.{}, + }).init(.{ + .null = undefined, + .pad_1 = .{ + .tag = .ZIG_padding, + }, + .pad_n = .{ + .tag = .ZIG_padding, + .attrs = &.{ + .{ .ZIG_padding, .block }, + }, + }, + .decl_lost = .{ + .tag = .ZIG_lost_declaration, + }, + .decl_alias = .{ + .tag = .imported_declaration, + .attrs = decl_attrs ++ .{ + .{ .import, .ref_addr }, + }, + }, + .decl_empty_incomplete_enum = .{ + .tag = .enumeration_type, + .attrs = decl_attrs, + }, + .decl_incomplete_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = decl_attrs, + }, + .decl_empty_enum = .{ + .tag = .enumeration_type, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .type_decl_empty_incomplete_enum = .{ + .tag = .enumeration_type, + .attrs = type_decl_attrs, + }, + .type_decl_incomplete_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = type_decl_attrs, + }, + .type_decl_empty_enum = .{ + .tag = .enumeration_type, + .attrs = type_decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .type_decl_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = type_decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_empty_incomplete_struct = .{ + .tag = .structure_type, + .attrs = decl_attrs, + }, + .decl_incomplete_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_attrs, + }, + .decl_empty_struct = .{ + .tag = .structure_type, + .attrs = decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .type_decl_empty_incomplete_struct = .{ + .tag = .structure_type, + .attrs = type_decl_attrs, + }, + .type_decl_incomplete_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = type_decl_attrs, + }, + .type_decl_empty_struct = .{ + .tag = .structure_type, + .attrs = type_decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .type_decl_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = type_decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_empty_packed_struct = .{ + .tag = .structure_type, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_packed_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .type_decl_empty_packed_struct = .{ + .tag = .structure_type, + .attrs = type_decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .type_decl_packed_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = type_decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_empty_incomplete_union = .{ + .tag = .union_type, + .attrs = decl_attrs, + }, + .decl_incomplete_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_attrs, + }, + .decl_empty_union = .{ + .tag = .union_type, + .attrs = decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .type_decl_empty_incomplete_union = .{ + .tag = .union_type, + .attrs = type_decl_attrs, + }, + .type_decl_incomplete_union = .{ + .tag = .union_type, + .children = true, + .attrs = type_decl_attrs, + }, + .type_decl_empty_union = .{ + .tag = .union_type, + .attrs = type_decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .type_decl_union = .{ + .tag = .union_type, + .children = true, + .attrs = type_decl_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_empty_packed_union = .{ + .tag = .union_type, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_packed_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .type_decl_empty_packed_union = .{ + .tag = .union_type, + .attrs = type_decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .type_decl_packed_union = .{ + .tag = .union_type, + .children = true, + .attrs = type_decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_var = .{ + .tag = .variable, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_const = .{ + .tag = .constant, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_const_runtime_bits = .{ + .tag = .constant, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + }, + }, + .decl_const_comptime_state = .{ + .tag = .constant, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_const_runtime_bits_comptime_state = .{ + .tag = .constant, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_nullary_func_generic = .{ + .tag = .subprogram, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + .{ .noreturn, .flag }, + }, + }, + .decl_func_generic = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_extern_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .decl_extern_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .decl_specification_empty_struct = .{ + .tag = .structure_type, + .attrs = decl_specification_attrs, + }, + .decl_specification_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_specification_attrs, + }, + .type_decl_specification_empty_struct = .{ + .tag = .structure_type, + .attrs = type_decl_specification_attrs, + }, + .type_decl_specification_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = type_decl_specification_attrs, + }, + .decl_specification_empty_enum = .{ + .tag = .enumeration_type, + .attrs = decl_specification_attrs, + }, + .decl_specification_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = decl_specification_attrs, + }, + .type_decl_specification_empty_enum = .{ + .tag = .enumeration_type, + .attrs = type_decl_specification_attrs, + }, + .type_decl_specification_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = type_decl_specification_attrs, + }, + .decl_specification_empty_union = .{ + .tag = .union_type, + .attrs = decl_specification_attrs, + }, + .decl_specification_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_specification_attrs, + }, + .type_decl_specification_empty_union = .{ + .tag = .union_type, + .attrs = type_decl_specification_attrs, + }, + .type_decl_specification_union = .{ + .tag = .union_type, + .children = true, + .attrs = type_decl_specification_attrs, + }, + .decl_specification_func = .{ + .tag = .subprogram, + .attrs = decl_specification_attrs, + }, + .decl_instance_alias = .{ + .tag = .imported_declaration, + .attrs = decl_instance_attrs ++ .{ + .{ .import, .ref_addr }, + }, + }, + .decl_instance_empty_incomplete_enum = .{ + .tag = .enumeration_type, + .attrs = decl_instance_attrs, + }, + .decl_instance_incomplete_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = decl_instance_attrs, + }, + .decl_instance_empty_enum = .{ + .tag = .enumeration_type, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_empty_incomplete_struct = .{ + .tag = .structure_type, + .attrs = decl_instance_attrs, + }, + .decl_instance_incomplete_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_instance_attrs, + }, + .decl_instance_empty_struct = .{ + .tag = .structure_type, + .attrs = decl_instance_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_instance_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_instance_empty_packed_struct = .{ + .tag = .structure_type, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_packed_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_empty_incomplete_union = .{ + .tag = .union_type, + .attrs = decl_instance_attrs, + }, + .decl_instance_incomplete_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_instance_attrs, + }, + .decl_instance_empty_union = .{ + .tag = .union_type, + .attrs = decl_instance_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_instance_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_instance_empty_packed_union = .{ + .tag = .union_type, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_packed_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_var = .{ + .tag = .variable, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_instance_const = .{ + .tag = .constant, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_instance_const_runtime_bits = .{ + .tag = .constant, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + }, + }, + .decl_instance_const_comptime_state = .{ + .tag = .constant, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_instance_const_runtime_bits_comptime_state = .{ + .tag = .constant, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_instance_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_instance_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_instance_nullary_func_generic = .{ + .tag = .subprogram, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_func_generic = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_extern_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .decl_instance_extern_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_instance_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .compile_unit = .{ + .tag = .compile_unit, + .children = true, + .attrs = &.{ + .{ .language, .data1 }, + .{ .producer, .strp }, + .{ .comp_dir, .line_strp }, + .{ .name, .line_strp }, + .{ .base_types, .ref_addr }, + .{ .stmt_list, .sec_offset }, + .{ .rnglists_base, .sec_offset }, + .{ .ranges, .rnglistx }, + .{ .use_UTF8, .flag_present }, + }, + }, + .module = .{ + .tag = .module, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .ranges, .rnglistx }, + }, + }, + .module_dependency = .{ + .tag = .imported_module, + .attrs = &.{ + .{ .name, .strp }, + .{ .import, .ref_addr }, + }, + }, + .empty_file = .{ + .tag = .structure_type, + .attrs = &.{ + .{ .decl_file, .udata }, + .{ .name, .strp }, + .{ .declaration, .flag }, + }, + }, + .file = .{ + .tag = .structure_type, + .children = true, + .attrs = &.{ + .{ .decl_file, .udata }, + .{ .name, .strp }, + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .access = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + }, + }, + .enum_field = .{ + .tag = .enumerator, + .attrs = &.{ + .{ .const_value, .indirect }, + .{ .name, .strp }, + }, + }, + .generated_field = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .artificial, .flag_present }, + }, + }, + .field = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + }, + }, + .field_default_fully_runtime = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + .{ .default_value, .block }, + }, + }, + .field_default_partially_comptime = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + .{ .default_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .field_default_fully_comptime = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .field_comptime = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .field_comptime_fully_runtime = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .field_comptime_partially_comptime = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .field_comptime_fully_comptime = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .packed_field = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_bit_offset, .udata }, + }, + }, + .tagged_union = .{ + .tag = .variant_part, + .children = true, + .attrs = &.{ + .{ .discr, .ref_addr }, + }, + }, + .tagged_union_field = .{ + .tag = .variant, + .children = true, + .attrs = &.{ + .{ .discr_value, .indirect }, + }, + }, + .tagged_union_default_field = .{ + .tag = .variant, + .children = true, + }, + .void_type = .{ + .tag = .unspecified_type, + .attrs = &.{ + .{ .name, .strp }, + }, + }, + .numeric_type = .{ + .tag = .base_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .encoding, .data1 }, + .{ .bit_size, .udata }, + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .inferred_error_set_type = .{ + .tag = .typedef, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .ptr_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_sentinel_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .ZIG_sentinel, .block }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_aligned_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .alignment, .udata }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_aligned_sentinel_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .ZIG_sentinel, .block }, + .{ .alignment, .udata }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .is_const = .{ + .tag = .const_type, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .is_volatile = .{ + .tag = .volatile_type, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .array_type = .{ + .tag = .array_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .array_sentinel_type = .{ + .tag = .array_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .ZIG_sentinel, .block }, + .{ .type, .ref_addr }, + }, + }, + .vector_type = .{ + .tag = .array_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .GNU_vector, .flag_present }, + }, + }, + .array_index = .{ + .tag = .subrange_type, + .attrs = &.{ + .{ .lower_bound, .udata }, + }, + }, + .array_len = .{ + .tag = .subrange_type, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .count, .udata }, + }, + }, + .nullary_func_type = .{ + .tag = .subroutine_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .calling_convention, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .func_type = .{ + .tag = .subroutine_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .calling_convention, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .param = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .unnamed_param = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .is_var_args = .{ + .tag = .unspecified_parameters, + }, + .generated_empty_enum_type = .{ + .tag = .enumeration_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .generated_enum_type = .{ + .tag = .enumeration_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .generated_empty_struct_type = .{ + .tag = .structure_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .declaration, .flag }, + }, + }, + .generated_struct_type = .{ + .tag = .structure_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .generated_union_type = .{ + .tag = .union_type, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .capture_specification = .{ + .tag = .template_value_parameter, + .attrs = &.{ + .{ .name, .strp }, + }, + }, + .comptime_capture = .{ + .tag = .template_value_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .comptime_capture_runtime = .{ + .tag = .template_value_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .comptime_capture_partially_comptime = .{ + .tag = .template_value_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .comptime_capture_fully_comptime = .{ + .tag = .template_value_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .runtime_capture = .{ + .tag = .template_type_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .nav_capture = .{ + .tag = .template_value_parameter, + .attrs = &.{ + .{ .location, .exprloc }, + }, + }, + .builtin_extern_nullary_func = .{ + .tag = .subprogram, + .attrs = &.{ + .{ .ZIG_parent, .ref_addr }, + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .builtin_extern_func = .{ + .tag = .subprogram, + .children = true, + .attrs = &.{ + .{ .ZIG_parent, .ref_addr }, + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .builtin_extern_var = .{ + .tag = .variable, + .attrs = &.{ + .{ .ZIG_parent, .ref_addr }, + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + .{ .external, .flag_present }, + }, + }, + .empty_block = .{ + .tag = .lexical_block, + .attrs = &.{ + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .block = .{ + .tag = .lexical_block, + .children = true, + .attrs = &.{ + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .empty_inlined_func = .{ + .tag = .inlined_subroutine, + .attrs = &.{ + .{ .abstract_origin, .ref_addr }, + .{ .ZIG_call_line_relative, .udata }, + .{ .call_column, .udata }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .inlined_func = .{ + .tag = .inlined_subroutine, + .children = true, + .attrs = &.{ + .{ .abstract_origin, .ref_addr }, + .{ .ZIG_call_line_relative, .udata }, + .{ .call_column, .udata }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .unnamed_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .comptime_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .comptime_arg_fully_runtime = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .comptime_arg_partially_comptime = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .comptime_arg_fully_comptime = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .unnamed_comptime_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + }, + }, + .unnamed_comptime_arg_fully_runtime = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .unnamed_comptime_arg_partially_comptime = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .unnamed_comptime_arg_fully_comptime = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .extern_param = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .local_var = .{ + .tag = .variable, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .local_const = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .local_const_fully_runtime = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .local_const_partially_comptime = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .local_const_fully_comptime = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .undefined_comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .aggregate_undefined_comptime_value = .{ + .tag = .ZIG_comptime_value, + .children = true, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .const_value, .indirect }, + }, + }, + .aggregate_comptime_value = .{ + .tag = .ZIG_comptime_value, + .children = true, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .const_value, .indirect }, + }, + }, + .location_comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .aggregate_location_comptime_value = .{ + .tag = .ZIG_comptime_value, + .children = true, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .comptime_value_field_runtime_bits = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .const_value, .block }, + }, + }, + .comptime_value_field_comptime_state = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .comptime_value_elem_runtime_bits = .{ + .tag = .member, + .attrs = &.{ + .{ .const_value, .block }, + }, + }, + .comptime_value_elem_comptime_state = .{ + .tag = .member, + .attrs = &.{ + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + }); +}; + +pub fn uleb128Size(value: anytype) u32 { + var buf: [std.atomic.cache_line]u8 = undefined; + var dw: std.Io.Writer.Discarding = .init(&buf); + dw.writer.writeUleb128(value) catch unreachable; + return @intCast(dw.fullCount()); +} + +pub fn sleb128Size(value: anytype) u32 { + var buf: [std.atomic.cache_line]u8 = undefined; + var dw: std.Io.Writer.Discarding = .init(&buf); + dw.writer.writeSleb128(value) catch unreachable; + return @intCast(dw.fullCount()); +} + +const assert = std.debug.assert; +const codegen = @import("../codegen.zig"); +const Compilation = @import("../Compilation.zig"); +const dev = @import("../dev.zig"); +const DW = std.dwarf; +const Dwarf = @This(); +const InternPool = @import("../InternPool.zig"); +const link = @import("../link.zig"); +const log = std.log.scoped(.dwarf); +const Module = @import("../Module.zig"); +const std = @import("std"); +const target_info = @import("../target.zig"); +const Type = @import("../Type.zig"); +const Value = @import("../Value.zig"); +const Zcu = @import("../Zcu.zig"); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d372c2e58a9990eb17812e39b9dc3a2e4588b62b..416eab37e05651c42cfedb4e025bca62b9cac016 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1677,9 +1677,7 @@ pub fn updateContainerType( ty: InternPool.Index, success: bool, ) link.Error!void { - return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - }; + try self.zigObjectPtr().?.updateContainerType(pt, ty, success); } pub fn updateExports( @@ -1690,8 +1688,8 @@ pub fn updateExports( return self.zigObjectPtr().?.updateExports(self, pt, export_indices); } -pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { - return self.zigObjectPtr().?.updateLineNumber(pt, ti_id); +pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, inst: InternPool.TrackedInst.Index, line: u32) link.Error!void { + return self.zigObjectPtr().?.updateLineNumber(pt, inst, line); } fn checkDuplicates(self: *Elf) !void { @@ -4412,7 +4410,6 @@ const Path = std.Build.Cache.Path; const Stat = std.Build.Cache.File.Stat; const codegen = @import("../codegen.zig"); -const dev = @import("../dev.zig"); const eh_frame = @import("Elf/eh_frame.zig"); const gc = @import("Elf/gc.zig"); const musl = @import("../libs/musl.zig"); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 075d4aced771cef907276dba8196b981c063369e..9309189f67f4307ec49be64440d5edbff16b388e 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -945,7 +945,7 @@ const x86_64 = struct { code: ?[]const u8, it: *RelocsIterator, ) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const t = &elf_file.base.comp.root_mod.resolved_target.result; const is_static = elf_file.base.isStatic(); const is_dyn_lib = elf_file.isEffectivelyDynLib(); @@ -1059,7 +1059,7 @@ const x86_64 = struct { it: *RelocsIterator, code: []u8, ) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const t = &elf_file.base.comp.root_mod.resolved_target.result; const diags = &elf_file.base.comp.link_diags; const r_type: elf.R_X86_64 = @fromBackingInt(@intCast(rel.r_type())); @@ -1200,7 +1200,7 @@ const x86_64 = struct { args: ResolveArgs, code: []u8, ) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const r_type: elf.R_X86_64 = @fromBackingInt(@intCast(rel.r_type())); _, const A, const S, const GOT, _, _, const DTP = args; @@ -1240,7 +1240,7 @@ const x86_64 = struct { } fn relaxGotpcrelx(code: []u8, t: *const std.Target) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const old_inst = disassemble(code) orelse return error.RelaxFailure; const inst: Instruction = switch (old_inst.encoding.mnemonic) { .call => try .new(old_inst.prefix, .call, &.{ @@ -1259,7 +1259,7 @@ const x86_64 = struct { } fn relaxRexGotpcrelx(code: []u8, t: *const std.Target) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const old_inst = disassemble(code) orelse return error.RelaxFailure; switch (old_inst.encoding.mnemonic) { .mov => { @@ -1279,7 +1279,7 @@ const x86_64 = struct { code: []u8, r_offset: usize, ) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); assert(rels.len == 2); const diags = &elf_file.base.comp.link_diags; const rel: elf.R_X86_64 = @fromBackingInt(@intCast(rels[1].r_type())); @@ -1319,7 +1319,7 @@ const x86_64 = struct { code: []u8, r_offset: usize, ) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); assert(rels.len == 2); const diags = &elf_file.base.comp.link_diags; const rel: elf.R_X86_64 = @fromBackingInt(@intCast(rels[1].r_type())); @@ -1366,7 +1366,7 @@ const x86_64 = struct { } fn canRelaxGotTpOff(code: []const u8, t: *const std.Target) bool { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const old_inst = disassemble(code) orelse return false; switch (old_inst.encoding.mnemonic) { .mov => { @@ -1384,7 +1384,7 @@ const x86_64 = struct { } fn relaxGotTpOff(code: []u8, t: *const std.Target) void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const old_inst = disassemble(code) orelse unreachable; switch (old_inst.encoding.mnemonic) { .mov => { @@ -1401,7 +1401,7 @@ const x86_64 = struct { } fn relaxGotPcTlsDesc(code: []u8, target: *const std.Target) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const old_inst = disassemble(code) orelse return error.RelaxFailure; switch (old_inst.encoding.mnemonic) { .lea => { @@ -1425,7 +1425,7 @@ const x86_64 = struct { code: []u8, r_offset: usize, ) !void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); assert(rels.len == 2); const diags = &elf_file.base.comp.link_diags; const rel: elf.R_X86_64 = @fromBackingInt(@intCast(rels[1].r_type())); @@ -1492,6 +1492,7 @@ const aarch64 = struct { ) !void { _ = code; _ = it; + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); const r_type: elf.R_AARCH64 = @fromBackingInt(@intCast(rel.r_type())); const is_dyn_lib = elf_file.isEffectivelyDynLib(); @@ -1569,6 +1570,7 @@ const aarch64 = struct { code_buffer: []u8, ) (error{ UnexpectedRemainder, DivisionByZero } || RelocError)!void { _ = it; + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); const diags = &elf_file.base.comp.link_diags; const r_type: elf.R_AARCH64 = @fromBackingInt(@intCast(rel.r_type())); @@ -1742,6 +1744,7 @@ const aarch64 = struct { args: ResolveArgs, code: []u8, ) !void { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); const r_type: elf.R_AARCH64 = @fromBackingInt(@intCast(rel.r_type())); _, const A, const S, _, _, _, _ = args; @@ -1772,6 +1775,7 @@ const riscv = struct { ) !void { _ = code; _ = it; + dev.checkAny(&.{ .llvm_backend, .riscv64_backend }); const r_type: elf.R_RISCV = @fromBackingInt(@intCast(rel.r_type())); @@ -1815,6 +1819,7 @@ const riscv = struct { it: *RelocsIterator, code: []u8, ) !void { + dev.checkAny(&.{ .llvm_backend, .riscv64_backend }); const diags = &elf_file.base.comp.link_diags; const r_type: elf.R_RISCV = @fromBackingInt(@intCast(rel.r_type())); const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; @@ -1951,6 +1956,7 @@ const riscv = struct { args: ResolveArgs, code: []u8, ) !void { + dev.checkAny(&.{ .llvm_backend, .riscv64_backend }); const r_type: elf.R_RISCV = @fromBackingInt(@intCast(rel.r_type())); _, const A, const S, const GOT, _, _, const DTP = args; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 960df2a52ec5ea63820a5b64e6c0bbd490152ec5..6677c60b4578957bc93b75070a87adb94d3fd87f 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -1236,29 +1236,22 @@ pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; + var compressed_reader: Io.Reader = .fixed(data[@sizeOf(elf.Elf64_Chdr)..]); + const size = std.math.cast(usize, chdr.ch_size) orelse return error.Overflow; + var aw: Io.Writer.Allocating = try .initCapacity(gpa, size); + defer aw.deinit(); switch (chdr.ch_type) { .ZLIB => { - var stream: std.Io.Reader = .fixed(data[@sizeOf(elf.Elf64_Chdr)..]); - var zlib_stream: std.compress.flate.Decompress = .init(&stream, .zlib, &.{}); - const size = std.math.cast(usize, chdr.ch_size) orelse return error.Overflow; - var aw: std.Io.Writer.Allocating = .init(gpa); - try aw.ensureUnusedCapacity(size); - defer aw.deinit(); - _ = try zlib_stream.reader.streamRemaining(&aw.writer); - return aw.toOwnedSlice(); + var decompress: std.compress.flate.Decompress = .init(&compressed_reader, .zlib, &.{}); + _ = try decompress.reader.streamRemaining(&aw.writer); }, .ZSTD => { - var input: std.Io.Reader = .fixed(data[@sizeOf(elf.Elf64_Chdr)..]); - var stream: std.compress.zstd.Decompress = .init(&input, &.{}, .{}); - const size = std.math.cast(usize, chdr.ch_size) orelse return error.Overflow; - var aw: std.Io.Writer.Allocating = try .initCapacity(gpa, size); - defer aw.deinit(); - _ = try stream.reader.streamRemaining(&aw.writer); - - return aw.toOwnedSlice(); + var decompress: std.compress.zstd.Decompress = .init(&compressed_reader, &.{}, .{}); + _ = try decompress.reader.streamRemaining(&aw.writer); }, else => @panic("TODO unhandled compression scheme"), } + return aw.toOwnedSlice(); } return data; @@ -1492,7 +1485,7 @@ const Format = struct { object: *Object, elf_file: *Elf, - fn symtab(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { + fn symtab(f: Format, writer: *Io.Writer) Io.Writer.Error!void { const object = f.object; const elf_file = f.elf_file; try writer.writeAll(" locals\n"); @@ -1511,7 +1504,7 @@ const Format = struct { } } - fn atoms(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { + fn atoms(f: Format, writer: *Io.Writer) Io.Writer.Error!void { const object = f.object; try writer.writeAll(" atoms\n"); for (object.atoms_indexes.items) |atom_index| { @@ -1520,7 +1513,7 @@ const Format = struct { } } - fn cies(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { + fn cies(f: Format, writer: *Io.Writer) Io.Writer.Error!void { const object = f.object; try writer.writeAll(" cies\n"); for (object.cies.items, 0..) |cie, i| { @@ -1528,7 +1521,7 @@ const Format = struct { } } - fn fdes(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { + fn fdes(f: Format, writer: *Io.Writer) Io.Writer.Error!void { const object = f.object; try writer.writeAll(" fdes\n"); for (object.fdes.items, 0..) |fde, i| { @@ -1536,7 +1529,7 @@ const Format = struct { } } - fn groups(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { + fn groups(f: Format, writer: *Io.Writer) Io.Writer.Error!void { const object = f.object; const elf_file = f.elf_file; try writer.writeAll(" groups\n"); @@ -1586,7 +1579,7 @@ pub fn fmtPath(self: Object) std.fmt.Alt(Object, formatPath) { return .{ .data = self }; } -fn formatPath(object: Object, writer: *std.Io.Writer) std.Io.Writer.Error!void { +fn formatPath(object: Object, writer: *Io.Writer) Io.Writer.Error!void { if (object.archive) |ar| { try writer.print("{f}({f})", .{ ar.path, object.path }); } else { diff --git a/src/link/Elf/Thunk.zig b/src/link/Elf/Thunk.zig index 69af9707bc9e8d41c2b81952c32b5cc981ccfdbd..003d4399acee2896214221936c5d20a93a80a602 100644 --- a/src/link/Elf/Thunk.zig +++ b/src/link/Elf/Thunk.zig @@ -91,6 +91,7 @@ pub const Index = u32; const aarch64 = struct { fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); for (thunk.symbols.keys(), 0..) |ref, i| { const sym = elf_file.symbol(ref).?; const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size)); @@ -113,6 +114,7 @@ const aarch64 = struct { }; const assert = std.debug.assert; +const dev = @import("../../dev.zig"); const elf = std.elf; const log = std.log.scoped(.link); const math = std.math; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 2ba12bcef1972cd8295ddee3e95bd6a5998e9907..98ca8b9f25792bd25f091735689ec6e6fecaf916 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -946,14 +946,11 @@ pub fn getNavVAddr( .r_addend = reloc_info.addend, }, self); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(this_sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(this_sym_index)), + .target_off = reloc_info.addend, + }), } return @intCast(vaddr); } @@ -978,14 +975,11 @@ pub fn getUavVAddr( .r_addend = reloc_info.addend, }, self); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(sym_index)), + .target_off = reloc_info.addend, + }), } return @intCast(vaddr); } @@ -1952,11 +1946,11 @@ pub fn updateExports( } } -pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { +pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) link.Error!void { if (self.dwarf) |*dwarf| { const comp = dwarf.bin_file.comp; const diags = &comp.link_diags; - dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { + dwarf.updateLineNumber(pt.zcu, ti_id, line) catch |err| switch (err) { error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), }; @@ -2402,6 +2396,7 @@ const TlsTable = std.array_hash_map.Auto(Atom.Index, void); const x86_64 = struct { fn writeTrampolineCode(source_addr: i64, target_addr: i64, buf: *[max_trampoline_len]u8) ![]u8 { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const disp = @as(i64, @intCast(target_addr)) - source_addr - 5; var bytes = [_]u8{ 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32 @@ -2417,6 +2412,7 @@ const assert = std.debug.assert; const build_options = @import("build_options"); const builtin = @import("builtin"); const codegen = @import("../../codegen.zig"); +const dev = @import("../../dev.zig"); const elf = std.elf; const link = @import("../../link.zig"); const log = std.log.scoped(.link); diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index b4ac44cc9e7338519fc1f1110845a38bbe82e7ae..fb5692486b5be97c70210387709d0c4aace9f071 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -535,6 +535,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { const x86_64 = struct { fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const r_type: elf.R_X86_64 = @fromBackingInt(@intCast(rel.r_type())); switch (r_type) { .NONE => {}, @@ -549,6 +550,7 @@ const x86_64 = struct { const aarch64 = struct { fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); const r_type: elf.R_AARCH64 = @fromBackingInt(@intCast(rel.r_type())); switch (r_type) { .NONE => {}, @@ -562,6 +564,7 @@ const aarch64 = struct { const riscv = struct { fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void { + dev.checkAny(&.{ .llvm_backend, .riscv64_backend }); const r_type: elf.R_RISCV = @fromBackingInt(@intCast(rel.r_type())); switch (r_type) { .NONE => {}, @@ -584,6 +587,7 @@ fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void { const std = @import("std"); const assert = std.debug.assert; +const dev = @import("../../dev.zig"); const elf = std.elf; const math = std.math; const relocs_log = std.log.scoped(.link_relocs); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index c1b9c0c53e907a2aaab52cd840a4df56d9dcb773..946134a9b26112ec02bdd427a354a5724bf84f9a 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -772,6 +772,7 @@ pub const PltSection = struct { const x86_64 = struct { fn write(plt: PltSection, elf_file: *Elf, writer: *std.Io.Writer) !void { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const shdrs = elf_file.sections.items(.shdr); const plt_addr = shdrs[elf_file.section_indexes.plt.?].sh_addr; const got_plt_addr = shdrs[elf_file.section_indexes.got_plt.?].sh_addr; @@ -807,6 +808,7 @@ pub const PltSection = struct { const aarch64 = struct { fn write(plt: PltSection, elf_file: *Elf, writer: *std.Io.Writer) !void { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); { const shdrs = elf_file.sections.items(.shdr); const plt_addr: i64 = @intCast(shdrs[elf_file.section_indexes.plt.?].sh_addr); @@ -949,6 +951,7 @@ pub const PltGotSection = struct { const x86_64 = struct { pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); for (plt_got.symbols.items) |ref| { const sym = elf_file.symbol(ref).?; const target_addr = sym.gotAddress(elf_file); @@ -967,6 +970,7 @@ pub const PltGotSection = struct { const aarch64 = struct { fn write(plt_got: PltGotSection, elf_file: *Elf, writer: *std.Io.Writer) !void { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); for (plt_got.symbols.items) |ref| { const sym = elf_file.symbol(ref).?; const target_addr = sym.gotAddress(elf_file); @@ -1518,6 +1522,7 @@ fn writeInt(value: anytype, elf_file: *Elf, writer: *std.Io.Writer) !void { const assert = std.debug.assert; const builtin = @import("builtin"); +const dev = @import("../../dev.zig"); const elf = std.elf; const math = std.math; const mem = std.mem; diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 61d9c81f9cd3da43219e24ebf9581540a920894b..20514f0a4a44d59e5c6e13f947d024a428b55980 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -1,8 +1,5 @@ const Elf = @This(); -const builtin = @import("builtin"); -const native_endian = builtin.cpu.arch.endian(); - const std = @import("std"); const Io = std.Io; const assert = std.debug.assert; @@ -10,9 +7,10 @@ const log = std.log.scoped(.link); const codegen = @import("../codegen.zig"); const Compilation = @import("../Compilation.zig"); +const Dwarf = @import("Dwarf2.zig"); const InternPool = @import("../InternPool.zig"); const link = @import("../link.zig"); -const MappedFile = @import("MappedFile.zig"); +const MappedFile = link.MappedFile; const target_util = @import("../target.zig"); const tracy = @import("../tracy.zig"); const Type = @import("../Type.zig"); @@ -23,7 +21,18 @@ const Alignment = MappedFile.Alignment; base: link.File, options: link.File.OpenOptions, mf: MappedFile, -ni: Node.Known, +ni: struct { + elf: MappedFile.Node.Index, + ehdr: MappedFile.Node.Index, + shdr: MappedFile.Node.Index, + rodata: MappedFile.Node.Index, + phdr: MappedFile.Node.Index, + text: MappedFile.Node.Index, + data: MappedFile.Node.Index, + data_rel_ro: MappedFile.Node.Index, + tls: MappedFile.Node.Index.Optional, + gnu_eh_frame: MappedFile.Node.Index.Optional, +}, archive: ?Archive, nodes: std.MultiArrayList(Node), /// Does not contain an item for `SHN_UNDEF`. @@ -43,6 +52,16 @@ shndx: struct { tdata: Section.Index, rela_dyn: Section.Index, rela_plt: Section.Index, + debug_abbrev: Section.Index, + eh_frame_hdr: Section.Index, + eh_frame: Section.Index, + debug_frame: Section.Index, + debug_info: Section.Index, + debug_line: Section.Index, + debug_line_str: Section.Index, + debug_rnglists: Section.Index, + debug_str: Section.Index, + debug_str_offsets: Section.Index, // These sections are created only as needed, and are initially `.UNDEF`. init_array: Section.Index, fini_array: Section.Index, @@ -123,6 +142,8 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), plt: std.array_hash_map.Auto(String(.strtab), void), /// The `.plt` section contains zero or more symbol relocations starting at this index. plt_first_symbol_reloc: SymbolReloc.Index, +/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index. +eh_frame_hdr_first_symbol_reloc: SymbolReloc.Index, needed: std.array_hash_map.Auto(String(.dynstr), void), inputs: std.ArrayList(struct { @@ -175,6 +196,7 @@ lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { }), pending_uavs: std.ArrayList(Node.UavMapIndex), symbol_relocs: std.ArrayList(SymbolReloc), +node_relocs: std.ArrayList(NodeReloc), got_relocs: std.ArrayList(GotReloc), /// Set of relocations which must be re-applied if the size of the TLS segment changes. tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), @@ -191,16 +213,25 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), /// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`. textrel_count: u32, +dwarf: Dwarf, +dwarf_shared: std.enums.EnumArray(Dwarf.SharedSection, dwarf_relocs.Shared), +dwarf_units: []dwarf_relocs.Unit, +dwarf_consts: std.array_hash_map.Auto(link.ConstPool.Index, dwarf_relocs.Const), +dwarf_globals: std.ArrayList(dwarf_relocs.Global), +dwarf_funcs: std.ArrayList(dwarf_relocs.Func), +dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl), + overflowed_reloc_count: u32, misaligned_reloc_count: u32, const_prog_node: std.Progress.Node, -synth_prog_node: std.Progress.Node, input_prog_node: std.Progress.Node, const Error = link.Error || error{MappedFileIo}; const Node = union(enum) { + deleted, + /// Only used when emitting a static library. /// /// Contains a header node which is an `.archive_header`. @@ -232,8 +263,9 @@ const Node = union(enum) { ehdr, shdr, segment: u32, - /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. section: Section.Index, + /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. + section_manual_size: Section.Index, /// May contain relocations. input_section: InputSection.Index, /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for @@ -253,6 +285,25 @@ const Node = union(enum) { /// May contain relocations. lazy_const_data: LazyMapRef.Index(.const_data), + debug_shared: Dwarf.SharedSection, + eh_frame_footer, + unit_padding, + unit_frame: Dwarf.Unit.Index, + unit_frame_cie: Dwarf.Unit.Index, + unit_debug_info: Dwarf.Unit.Index, + unit_debug_info_header: Dwarf.Unit.Index, + unit_debug_info_footer: Dwarf.Unit.Index, + unit_debug_line: Dwarf.Unit.Index, + unit_debug_line_header: Dwarf.Unit.Index, + unit_debug_rnglists: Dwarf.Unit.Index, + + const_debug_info: link.ConstPool.Index, + global_debug_info: Dwarf.Global.Index, + func_frame_fde: Dwarf.Func.Index, + func_debug_info: Dwarf.Func.Index, + func_debug_line: Dwarf.Func.Index, + decl_debug_info: Dwarf.Decl.Index, + pub const InputIndex = enum(u32) { _, @@ -288,7 +339,7 @@ const Node = union(enum) { pub const NavMapIndex = enum(u32) { _, - pub fn navIndex(nmi: NavMapIndex, elf: *const Elf) InternPool.Nav.Index { + pub fn nav(nmi: NavMapIndex, elf: *const Elf) InternPool.Nav.Index { return elf.navs.keys()[@backingInt(nmi)]; } @@ -363,18 +414,6 @@ const Node = union(enum) { } }; - pub const Known = struct { - elf: MappedFile.Node.Index, - ehdr: MappedFile.Node.Index, - shdr: MappedFile.Node.Index, - rodata: MappedFile.Node.Index, - phdr: MappedFile.Node.Index, - text: MappedFile.Node.Index, - data: MappedFile.Node.Index, - data_rel_ro: MappedFile.Node.Index, - tls: MappedFile.Node.Index.Optional, - }; - comptime { if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8); } @@ -439,7 +478,7 @@ const Section = struct { ni: MappedFile.Node.Index, /// A symbol which is exactly at the start of this section. /// - /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`. + /// When not emitting a relocatable, or for special section types, this is `.null`. lsi: Symbol.LocalIndex, rela: union { /// This field is active if and only if this section is *not* a `SHT_RELA` section. @@ -526,8 +565,8 @@ const Section = struct { std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(reserve(sec)), }; } - pub fn toSection(s: Index) ?std.elf.Section { - return switch (@backingInt(s)) { + pub fn toSection(shndx: Index) ?std.elf.Section { + return switch (@backingInt(shndx)) { std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec), std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null, reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast( @@ -536,28 +575,40 @@ const Section = struct { }; } - fn get(s: Index, elf: *Elf) *Section { - return &elf.shdrs.items[@backingInt(s) - 1]; // overflow means you tried to get the `.UNDEF` section + fn get(shndx: Index, elf: *Elf) *Section { + return &elf.shdrs.items[@backingInt(shndx) - 1]; // overflow means you tried to get the `.UNDEF` section } - fn name(s: Index, elf: *Elf) String(.shstrtab) { - return switch (elf.shdrPtr(s)) { + fn name(shndx: Index, elf: *Elf) String(.shstrtab) { + return switch (elf.shdrPtr(shndx)) { inline else => |shdr| @fromBackingInt(elf.targetLoad(&shdr.name)), }; } - fn vaddr(s: Index, elf: *Elf) u64 { - return switch (elf.shdrPtr(s)) { + fn vaddr(shndx: Index, elf: *Elf) u64 { + return switch (elf.shdrPtr(shndx)) { inline else => |shdr| elf.targetLoad(&shdr.addr), }; } - fn size(s: Index, elf: *Elf) u64 { - return switch (elf.shdrPtr(s)) { + fn size(shndx: Index, elf: *Elf) u64 { + return switch (elf.shdrPtr(shndx)) { inline else => |shdr| elf.targetLoad(&shdr.size), }; } + fn setSize(shndx: Index, elf: *Elf, new_size: u64) void { + return switch (elf.shdrPtr(shndx)) { + inline else => |shdr| { + elf.targetStore(&shdr.type, switch (new_size) { + 0 => .NULL, + else => .PROGBITS, + }); + elf.targetStore(&shdr.size, @intCast(new_size)); + }, + }; + } + fn flags(s: Index, elf: *Elf) std.elf.SHF { return switch (elf.shdrPtr(s)) { inline else => |shdr| elf.targetLoad(&shdr.flags).shf, @@ -582,7 +633,7 @@ const Section = struct { } const ni = shndx.get(elf).ni; if (min_align.compare(.gt, ni.alignment(&elf.mf))) { - try ni.realign(&elf.mf, elf.base.comp.gpa, min_align); + try ni.realign(elf.base.comp.gpa, &elf.mf, min_align); } switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { .elf => {}, @@ -615,7 +666,7 @@ const Section = struct { break :need_size cur_size + need_additional * ent_size; }, }; - try node.ensureMinimumSize(&elf.mf, elf.base.comp.gpa, need_size); + try node.ensureMinimumSize(elf.base.comp.gpa, &elf.mf, need_size); } /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at @@ -649,7 +700,7 @@ const Section = struct { }, .addend = @intCast(old_free_len + 1), // list length }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(index)]); } }, @@ -708,7 +759,7 @@ const Section = struct { }, .addend = @intCast(opts.addend), }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(new_index)]); } return new_index; @@ -786,10 +837,10 @@ const Section = struct { } } - /// Asserts that `rela_shndx` is a `SHT_RELA` section, and asserts that `index` refers to an - /// `R_*_RELATIVE` relocation inside of it; then, updates that relocation's addend (which is - /// an address in this DSO without the runtime load offset applied) to the given value. - fn relaSetRelativeOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_addend: u64) void { + /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `addend` field of the + /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e. + /// it is not deleted). + fn relaSetAddend(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_addend: u64) void { switch (elf.shdrPtr(rela_shndx)) { inline else => |shdr, class| { assert(elf.targetLoad(&shdr.type) == .RELA); @@ -807,244 +858,60 @@ const Section = struct { }, } } + + fn debugFrameFormat(shndx: Index, elf: *Elf) ?Dwarf.Frame.Format { + if (shndx == elf.shndx.eh_frame) return .eh_frame; + if (shndx == elf.shndx.debug_frame) return .debug_frame; + return null; + } }; }; - -/// Identifies a single entry in the GOT. -const GotKey = union(enum) { - /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these - /// as the target machine ABI requires. - /// - /// This `u32` value exists to allow reserving multiple words with distinct keys. - reserved: u32, - - /// Value is the address of the given symbol. - symbol: Symbol.Id, - - /// Value is the signed offset of the given symbol from the TLS pointer. - tpoff: Symbol.Id, - - /// Value is the TLS module ID of the DSO we are creating. - /// - /// Used for the first of the two GOT entries generated by a TLSLD relocation. - tlsld0, - /// Value is always 0. - /// - /// Used for the second of the two GOT entries generated by a TLSLD relocation. - tlsld1, - - /// Value is the TLS module ID for the given STT_TLS symbol. - /// - /// Used for the first of the two GOT entries generated by a TLSGD relocation. - tlsgd0: Symbol.Id, - /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area. - /// - /// Used for the second of the two GOT entries generated by a TLSGD relocation. - tlsgd1: Symbol.Id, -}; - -/// A relocation targeting a particular GOT entry. -const GotReloc = struct { - /// The node containing this relocation. Possible values are: - /// * An input section - /// * A section - /// * A NAV, UAV, or lazy code/data - /// * `.none`, if this relocation was deleted (in which case it should be ignored) - node: MappedFile.Node.Index.Optional, - /// The offset of the relocation inside of `node`. - offset: u64, - target: GotKey, - addend: i64, - type: GotReloc.Type, - result: enum(u8) { ok, overflowed, misaligned }, - - /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target` - /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview. - const Type = packed struct(u16) { - fn simple(target: Target, action: Simple) GotReloc.Type { - assert(target != .special); - return .{ .target = target, .action = .{ .simple = action } }; - } - - fn special(s: Special) GotReloc.Type { - return .{ .target = .special, .action = .{ .special = s } }; - } - - target: Target, - action: packed union { - simple: Simple, - special: Special, +fn debugFrameFooterSize(elf: *Elf, frame_format: Dwarf.Frame.Format) usize { + return switch (frame_format) { + .eh_frame => switch (elf.ehdrType()) { + .REL => 0, + .EXEC, .DYN => 4, }, - - /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there - /// are fewer different kinds of GOT relocation. - const Target = enum(u3) { - /// This is a "special" relocation whose specific type is in the `action.special` field. - special, - - /// Absolute address of the GOT entry. - abs, - /// Offset from the relocation itself to the GOT entry ("PC-relative"). - rel, - /// Offset from the base of the GOT to the GOT entry. - offset, - }; - - const Simple = SymbolReloc.Type.Simple; - - /// Like `SymbolReloc.Special`, but for GOT relocations. - const Special = enum(u13) { - larch_pcala_hi20, - larch_pcala64_lo20, - larch_pcala64_hi12, - - sparc_op_lox10, - sparc_op_hix22, - - fn applyInner( - s: Special, - elf: *Elf, - got_vaddr: u64, - got_offset: u64, - addend: u64, - dest_vaddr: u64, - dest_slice: []u8, - ) error{ RelocationMisaligned, RelocationOverflow }!void { - switch (s) { - .larch_pcala_hi20 => { - const val = got_vaddr +% got_offset +% addend; - const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); - elf.targetStore(inst, .{ - .b0_4 = elf.targetLoad(inst).b0_4, - .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), - .b25_31 = elf.targetLoad(inst).b25_31, - }); - }, - .larch_pcala64_lo20 => { - const val = got_vaddr +% got_offset +% addend; - const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); - elf.targetStore(inst, .{ - .b0_4 = elf.targetLoad(inst).b0_4, - .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), - .b25_31 = elf.targetLoad(inst).b25_31, - }); - }, - .larch_pcala64_hi12 => { - const val = got_vaddr +% got_offset +% addend; - const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); - elf.targetStore(inst, .{ - .b0_9 = elf.targetLoad(inst).b0_9, - .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), - .b22_31 = elf.targetLoad(inst).b22_31, - }); - }, - .sparc_op_lox10 => { - const dest_ptr: *align(1) packed struct(u32) { - imm13: u13, - b13_31: u19, - } = @ptrCast(dest_slice); - elf.targetStore(dest_ptr, .{ - .imm13 = @as(u10, @truncate(got_offset)), - .b13_31 = elf.targetLoad(dest_ptr).b13_31, - }); - }, - .sparc_op_hix22 => { - const dest_ptr: *align(1) packed struct(u32) { - imm22: u22, - b22_31: u10, - } = @ptrCast(dest_slice); - elf.targetStore(dest_ptr, .{ - .imm22 = @truncate(got_offset >> 10), - .b22_31 = elf.targetLoad(dest_ptr).b22_31, - }); - }, - } - } - }; + .debug_frame => 0, }; +} - const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - fn get(index: GotReloc.Index, elf: *Elf) *GotReloc { - return &elf.got_relocs.items[@backingInt(index)]; - } +const dwarf_relocs = struct { + const Shared = struct { + first_target_reloc: NodeReloc.Index, + }; + const Unit = struct { + frame_cie_first_target_reloc: NodeReloc.Index, + debug_info_header_first_target_reloc: NodeReloc.Index, + debug_info_header_first_node_reloc: NodeReloc.Index, + debug_line_header_first_target_reloc: NodeReloc.Index, + debug_line_header_first_node_reloc: NodeReloc.Index, + debug_rnglists_first_target_reloc: NodeReloc.Index, + debug_rnglists_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), + }; + const Const = struct { + debug_info_first_target_reloc: NodeReloc.Index, + debug_info_first_symbol_reloc: SymbolReloc.Index, + debug_info_first_node_reloc: NodeReloc.Index, + }; + const Global = struct { + debug_info_first_target_reloc: NodeReloc.Index, + debug_info_first_symbol_reloc: SymbolReloc.Index, + debug_info_first_node_reloc: NodeReloc.Index, + }; + const Func = struct { + frame_fde_first_symbol_reloc: SymbolReloc.Index, + frame_fde_first_node_reloc: NodeReloc.Index, + debug_info_first_target_reloc: NodeReloc.Index, + debug_info_first_symbol_reloc: SymbolReloc.Index, + debug_info_first_node_reloc: NodeReloc.Index, + debug_line_first_symbol_reloc: SymbolReloc.Index, + debug_line_first_node_reloc: NodeReloc.Index, + }; + const Decl = struct { + debug_info_first_target_reloc: NodeReloc.Index, + debug_info_first_node_reloc: NodeReloc.Index, }; - - fn apply(reloc: *GotReloc, elf: *Elf) void { - assert(elf.ehdrType() != .REL); - const node = reloc.node.unwrap() orelse { - return; // deleted - }; - if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { - // There's no point applying the relocation now, because it will be re-applied by - // `flushMoved` at some point anyway. - return; - } - switch (reloc.result) { - .ok => {}, - .overflowed => elf.overflowed_reloc_count -= 1, - .misaligned => elf.misaligned_reloc_count -= 1, - } - if (reloc.applyInner(elf)) { - @branchHint(.likely); - reloc.result = .ok; - } else |err| switch (err) { - error.RelocationOverflow => { - reloc.result = .overflowed; - elf.overflowed_reloc_count += 1; - }, - error.RelocationMisaligned => { - reloc.result = .misaligned; - elf.misaligned_reloc_count += 1; - }, - } - } - fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { - const node = reloc.node.unwrap().?; - const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset; - const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; - - const got_vaddr = elf.shndx.got.vaddr(elf); - const got_index: u64 = elf.got.getIndex(reloc.target).?; - const got_offset: u64 = switch (elf.identClass()) { - .NONE, _ => unreachable, - inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, - }; - const addend: u64 = @bitCast(reloc.addend); - - const target_val: u64 = switch (reloc.type.target) { - .abs => got_vaddr +% got_offset +% addend, - .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, - .offset => got_offset +% addend, - .special => return reloc.type.action.special.applyInner( - elf, - got_vaddr, - got_offset, - addend, - dest_vaddr, - dest_slice, - ), - }; - try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); - } - - fn delete(reloc: *GotReloc, elf: *Elf) void { - switch (reloc.result) { - .ok => {}, - .overflowed => elf.overflowed_reloc_count -= 1, - .misaligned => elf.misaligned_reloc_count -= 1, - } - reloc.* = .{ - .node = .none, - .offset = undefined, - .target = undefined, - .addend = undefined, - .type = undefined, - .result = undefined, - }; - } }; pub const MachineRelocType = union { @@ -1118,51 +985,144 @@ pub const MachineRelocType = union { pub fn globDat(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { .AARCH64 => .{ .AARCH64 = .GLOB_DAT }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"32", + .@"64" => .@"64", + } }, .PPC64 => .{ .PPC64 = .GLOB_DAT }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"32", + .@"64" => .@"64", + } }, .SPARCV9 => .{ .SPARC = .GLOB_DAT }, .X86_64 => .{ .X86_64 = .GLOB_DAT }, }; } pub fn dtpMod(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, + .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .P32_TLS_DTPMOD, + .@"64" => .TLS_DTPMOD, + } }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPMOD32, + .@"64" => .TLS_DTPMOD64, + } }, .PPC64 => .{ .PPC64 = .DTPMOD64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPMOD32, + .@"64" => .TLS_DTPMOD64, + } }, + .SPARCV9 => .{ .SPARC = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPMOD32, + .@"64" => .TLS_DTPMOD64, + } }, .X86_64 => .{ .X86_64 = .DTPMOD64 }, }; } pub fn dtpOff(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPREL else .P32_TLS_DTPREL }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, + .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .P32_TLS_DTPREL, + .@"64" => .TLS_DTPREL, + } }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPREL32, + .@"64" => .TLS_DTPREL64, + } }, .PPC64 => .{ .PPC64 = .DTPREL64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPREL32, + .@"64" => .TLS_DTPREL64, + } }, + .SPARCV9 => .{ .SPARC = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPOFF32, + .@"64" => .TLS_DTPOFF64, + } }, .X86_64 => .{ .X86_64 = .DTPOFF64 }, }; } pub fn tpOff(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_TPREL else .P32_TLS_TPREL }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, + .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .P32_TLS_TPREL, + .@"64" => .TLS_TPREL, + } }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_TPREL32, + .@"64" => .TLS_TPREL64, + } }, .PPC64 => .{ .PPC64 = .TPREL64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_TPREL32, + .@"64" => .TLS_TPREL64, + } }, + .SPARCV9 => .{ .SPARC = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_TPOFF32, + .@"64" => .TLS_TPOFF64, + } }, .X86_64 => .{ .X86_64 = .TPOFF64 }, }; } pub fn absAddr(elf: *const Elf) MachineRelocType { + return switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .abs32(elf), + .@"64" => .abs64(elf), + }; + } + pub fn abs32(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .AARCH64 => .{ .AARCH64 = .P32_ABS32 }, + .LOONGARCH => .{ .LARCH = .@"32" }, + .PPC64 => .{ .PPC64 = .ADDR32 }, + .RISCV => .{ .RISCV = .@"32" }, + .SPARCV9 => .{ .SPARC = .@"32" }, + .X86_64 => .{ .X86_64 = .@"32" }, + }; + } + pub fn abs64(elf: *const Elf) MachineRelocType { + return switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = .ABS64 }, + .LOONGARCH => .{ .LARCH = .@"64" }, .PPC64 => .{ .PPC64 = .ADDR64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" }, - .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .RISCV => .{ .RISCV = .@"64" }, + .SPARCV9 => .{ .SPARC = .@"64" }, + .X86_64 => .{ .X86_64 = .@"64" }, + }; + } + pub fn rel32(elf: *const Elf) MachineRelocType { + return switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = .PREL32 }, + .LOONGARCH => .{ .LARCH = .@"32_PCREL" }, + .PPC64 => .{ .PPC64 = .REL32 }, + .RISCV => .{ .RISCV = .@"32_PCREL" }, + .SPARCV9 => .{ .SPARC = .DISP32 }, + .X86_64 => .{ .X86_64 = .PC32 }, + }; + } + pub fn rel64(elf: *const Elf) MachineRelocType { + return switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = .PREL64 }, + .LOONGARCH => unreachable, + .PPC64 => .{ .PPC64 = .REL64 }, + .RISCV => unreachable, + .SPARCV9 => .{ .SPARC = .DISP64 }, + .X86_64 => .{ .X86_64 = .PC64 }, }; } pub fn size32(elf: *const Elf) ?MachineRelocType { @@ -1218,7 +1178,8 @@ const SymbolReloc = struct { /// * An input section /// * A section /// * A NAV, UAV, or lazy code/data - node: MappedFile.Node.Index, + /// * `.none`, if this relocation was deleted (in which case it should be ignored) + node: MappedFile.Node.Index.Optional, /// The offset of the relocation inside of `node`. offset: u64, /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`. @@ -1258,22 +1219,13 @@ const SymbolReloc = struct { /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries. fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index { const shndx = switch (elf.ehdrType()) { - .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx, + .REL => elf.getNodeShndx(sr.node.unwrap().?).get(elf).rela.shndx, .EXEC, .DYN => elf.shndx.rela_dyn, }; assert(shndx != .UNDEF); return shndx; } - const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc { - return &elf.symbol_relocs.items[@backingInt(index)]; - } - }; - /// Instead of using the ELF relocation enums, we have our own internal representation for /// relocation types. This representation is more compact (requiring only 16 bits), and allows /// sharing a lot of relocation handling between multiple relocs and target architectures. @@ -1679,9 +1631,33 @@ const SymbolReloc = struct { } }; + const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc { + return &elf.symbol_relocs.items[@backingInt(index)]; + } + }; + + fn flushMovedNode(reloc: *SymbolReloc, elf: *Elf, node_vaddr: u64) void { + if (reloc.rela_index.unwrap()) |rela_index| { + // The node has moved, so the offset of the relocation within the section might have + // changed, so update the `offset` field of the `ElfN.Rela` entry. + reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); + } + // This is not just the inverse of the above condition, because if `reloc` is relative + // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we + // still need to call `SymbolReloc.apply` to update that relocation's addend. + if (elf.ehdrType() != .REL) { + reloc.apply(elf); + } + } + fn apply(reloc: *SymbolReloc, elf: *Elf) void { assert(elf.ehdrType() != .REL); - if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { + const node = reloc.node.unwrap() orelse return; // deleted + if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { // There's no point applying the relocation now, because it will be re-applied by // `flushMoved` at some point anyway. return; @@ -1706,8 +1682,9 @@ const SymbolReloc = struct { } } fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { - const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset; - const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; + const node = reloc.node.unwrap().?; + const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset; + const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; const addend: u64 = @bitCast(reloc.addend); const target_val: u64 = type: switch (reloc.type.target) { @@ -1764,7 +1741,7 @@ const SymbolReloc = struct { } assert(reloc.type.action.simple.cast == .unsigned); assert(reloc.type.action.simple.shift == .@"0"); - elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, target_val); + elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val); return; }, }; @@ -1782,9 +1759,9 @@ const SymbolReloc = struct { switch (reloc.prev) { .none => { - const target_ptr = reloc.target.index(elf).ptr(elf); - assert(target_ptr.first_target_reloc == index); - target_ptr.first_target_reloc = reloc.next; + const first_target_reloc = &reloc.target.index(elf).ptr(elf).first_target_reloc; + assert(first_target_reloc.* == index); + first_target_reloc.* = reloc.next; }, else => |prev| prev.get(elf).next = reloc.next, } @@ -1799,6 +1776,7 @@ const SymbolReloc = struct { } reloc.* = undefined; + reloc.node = .none; } /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation, updating @@ -1808,7 +1786,7 @@ const SymbolReloc = struct { reloc.relaSection(elf).relaDeleteOne(elf, rela_index); switch (elf.ehdrType()) { .REL => {}, - .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) { + .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) { .no => unreachable, // there *was* a dynamic relocation! .yes => {}, .yes_textrel => elf.textrel_count -= 1, @@ -1818,6 +1796,424 @@ const SymbolReloc = struct { } }; +/// A relocation targeting an arbitrary node (within a section) with a fixed addend. +/// This represents a symbol reloc against the section symbol containing the node +/// with a variable addend that changes when the target node moves. +const NodeReloc = struct { + node: MappedFile.Node.Index.Optional, + offset: u64, + target: MappedFile.Node.Index, + addend: i64, + type: NodeReloc.Type, + next: NodeReloc.Index, + prev: NodeReloc.Index, + rela_index: Section.RelaIndex.Optional, + result: enum(u8) { ok, overflowed, misaligned }, + + const Type = enum { abs32, abs64 }; + + const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + fn get(index: NodeReloc.Index, elf: *Elf) *NodeReloc { + return &elf.node_relocs.items[@backingInt(index)]; + } + }; + + fn flushMovedNode(reloc: *NodeReloc, elf: *Elf, node_vaddr: u64) void { + if (reloc.rela_index.unwrap()) |rela_index| { + assert(elf.ehdrType() == .REL); + // The node has moved, so the offset of the relocation within the section might have + // changed, so update the `offset` field of the `ElfN.Rela` entry. + elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); + } else { + assert(elf.ehdrType() != .REL); + reloc.apply(elf); + } + } + + fn flushMovedTarget(reloc: *NodeReloc, elf: *Elf, target_section_offset: u64) void { + if (reloc.rela_index.unwrap()) |rela_index| { + assert(elf.ehdrType() == .REL); + // The target has moved, so the `addend` field of the `ElfN.Rela` entry needs to be updated. + elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetAddend(elf, rela_index, target_section_offset +% @as(u64, @bitCast(reloc.addend))); + } else { + assert(elf.ehdrType() != .REL); + reloc.apply(elf); + } + } + + fn apply(reloc: *NodeReloc, elf: *Elf) void { + const node = reloc.node.unwrap() orelse return; // deleted + if (reloc.rela_index.unwrap()) |rela_index| { + assert(elf.ehdrType() == .REL); + _ = rela_index; + } else { + assert(elf.ehdrType() != .REL); + if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) { + // There's no point applying the relocation now, because it will be re-applied by + // `flushMoved` at some point anyway. + return; + } + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + if (reloc.applyInner(elf)) { + @branchHint(.likely); + reloc.result = .ok; + } else |err| switch (err) { + error.RelocationOverflow => { + reloc.result = .overflowed; + elf.overflowed_reloc_count += 1; + }, + error.RelocationMisaligned => { + reloc.result = .misaligned; + elf.misaligned_reloc_count += 1; + }, + } + } + } + fn applyInner(reloc: *const NodeReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { + const simple: SymbolReloc.Type.Simple = .{ .dest = switch (reloc.type) { + .abs32 => .@"32", + .abs64 => .@"64", + }, .cast = .unsigned, .shift = .@"0" }; + const addend: u64 = @bitCast(reloc.addend); + const target_val = elf.getNodeVAddr(reloc.target) +% addend; + const dest_slice = reloc.node.unwrap().?.slice(&elf.mf)[@intCast(reloc.offset)..]; + try simple.write(target_val, dest_slice, elf.targetEndian()); + } + + fn delete(reloc: *NodeReloc, elf: *Elf) void { + reloc.deleteOutputRel(elf); + + switch (reloc.prev) { + .none => { + const first_target_reloc = switch (elf.getNode(reloc.target)) { + else => unreachable, + .debug_shared => |ss| &elf.dwarf_shared.getPtr(ss).first_target_reloc, + .unit_frame_cie => |ui| &elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc, + .unit_debug_info_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_target_reloc, + .unit_debug_line_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_line_header_first_target_reloc, + .unit_debug_rnglists => |ui| &elf.dwarf_units[@backingInt(ui)].debug_rnglists_first_target_reloc, + .const_debug_info => |cpi| &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_target_reloc, + .global_debug_info => |gi| &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_target_reloc, + .func_debug_info => |fi| &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_target_reloc, + .decl_debug_info => |di| &elf.dwarf_decls.getPtr(di).?.debug_info_first_target_reloc, + }; + first_target_reloc.* = reloc.next; + }, + else => |prev| prev.get(elf).next = reloc.next, + } + switch (reloc.next) { + .none => {}, + else => |next| next.get(elf).prev = reloc.prev, + } + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + + reloc.* = undefined; + reloc.node = .none; + } + + /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation. + fn deleteOutputRel(reloc: *NodeReloc, elf: *Elf) void { + const rela_index = reloc.rela_index.unwrap() orelse return; + assert(elf.ehdrType() == .REL); + elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaDeleteOne(elf, rela_index); + reloc.rela_index = .none; + } +}; + +/// Identifies a single entry in the GOT. +const GotKey = union(enum) { + /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these + /// as the target machine ABI requires. + /// + /// This `u32` value exists to allow reserving multiple words with distinct keys. + reserved: u32, + + /// Value is the address of the given symbol. + symbol: Symbol.Id, + + /// Value is the signed offset of the given symbol from the TLS pointer. + tpoff: Symbol.Id, + + /// Value is the TLS module ID of the DSO we are creating. + /// + /// Used for the first of the two GOT entries generated by a TLSLD relocation. + tlsld0, + /// Value is always 0. + /// + /// Used for the second of the two GOT entries generated by a TLSLD relocation. + tlsld1, + + /// Value is the TLS module ID for the given STT_TLS symbol. + /// + /// Used for the first of the two GOT entries generated by a TLSGD relocation. + tlsgd0: Symbol.Id, + /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area. + /// + /// Used for the second of the two GOT entries generated by a TLSGD relocation. + tlsgd1: Symbol.Id, +}; + +/// A relocation targeting a particular GOT entry. +const GotReloc = struct { + /// The node containing this relocation. Possible values are: + /// * An input section + /// * A section + /// * A NAV, UAV, or lazy code/data + /// * `.none`, if this relocation was deleted (in which case it should be ignored) + node: MappedFile.Node.Index.Optional, + /// The offset of the relocation inside of `node`. + offset: u64, + target: GotKey, + addend: i64, + type: GotReloc.Type, + result: enum(u8) { ok, overflowed, misaligned }, + + /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target` + /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview. + const Type = packed struct(u16) { + fn simple(target: Target, action: Simple) GotReloc.Type { + assert(target != .special); + return .{ .target = target, .action = .{ .simple = action } }; + } + + fn special(s: Special) GotReloc.Type { + return .{ .target = .special, .action = .{ .special = s } }; + } + + target: Target, + action: packed union { + simple: Simple, + special: Special, + }, + + /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there + /// are fewer different kinds of GOT relocation. + const Target = enum(u3) { + /// This is a "special" relocation whose specific type is in the `action.special` field. + special, + + /// Absolute address of the GOT entry. + abs, + /// Offset from the relocation itself to the GOT entry ("PC-relative"). + rel, + /// Offset from the base of the GOT to the GOT entry. + offset, + }; + + const Simple = SymbolReloc.Type.Simple; + + /// Like `SymbolReloc.Special`, but for GOT relocations. + const Special = enum(u13) { + larch_pcala_hi20, + larch_pcala64_lo20, + larch_pcala64_hi12, + + sparc_op_lox10, + sparc_op_hix22, + + fn applyInner( + s: Special, + elf: *Elf, + got_vaddr: u64, + got_offset: u64, + addend: u64, + dest_vaddr: u64, + dest_slice: []u8, + ) error{ RelocationMisaligned, RelocationOverflow }!void { + switch (s) { + .larch_pcala_hi20 => { + const val = got_vaddr +% got_offset +% addend; + const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); + elf.targetStore(inst, .{ + .b0_4 = elf.targetLoad(inst).b0_4, + .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), + .b25_31 = elf.targetLoad(inst).b25_31, + }); + }, + .larch_pcala64_lo20 => { + const val = got_vaddr +% got_offset +% addend; + const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); + elf.targetStore(inst, .{ + .b0_4 = elf.targetLoad(inst).b0_4, + .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), + .b25_31 = elf.targetLoad(inst).b25_31, + }); + }, + .larch_pcala64_hi12 => { + const val = got_vaddr +% got_offset +% addend; + const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); + elf.targetStore(inst, .{ + .b0_9 = elf.targetLoad(inst).b0_9, + .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), + .b22_31 = elf.targetLoad(inst).b22_31, + }); + }, + .sparc_op_lox10 => { + const dest_ptr: *align(1) packed struct(u32) { + imm13: u13, + b13_31: u19, + } = @ptrCast(dest_slice); + elf.targetStore(dest_ptr, .{ + .imm13 = @as(u10, @truncate(got_offset)), + .b13_31 = elf.targetLoad(dest_ptr).b13_31, + }); + }, + .sparc_op_hix22 => { + const dest_ptr: *align(1) packed struct(u32) { + imm22: u22, + b22_31: u10, + } = @ptrCast(dest_slice); + elf.targetStore(dest_ptr, .{ + .imm22 = @truncate(got_offset >> 10), + .b22_31 = elf.targetLoad(dest_ptr).b22_31, + }); + }, + } + } + }; + }; + + const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + fn get(index: GotReloc.Index, elf: *Elf) *GotReloc { + return &elf.got_relocs.items[@backingInt(index)]; + } + }; + + fn apply(reloc: *GotReloc, elf: *Elf) void { + assert(elf.ehdrType() != .REL); + const node = reloc.node.unwrap() orelse return; // deleted + if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { + // There's no point applying the relocation now, because it will be re-applied by + // `flushMoved` at some point anyway. + return; + } + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + if (reloc.applyInner(elf)) { + @branchHint(.likely); + reloc.result = .ok; + } else |err| switch (err) { + error.RelocationOverflow => { + reloc.result = .overflowed; + elf.overflowed_reloc_count += 1; + }, + error.RelocationMisaligned => { + reloc.result = .misaligned; + elf.misaligned_reloc_count += 1; + }, + } + } + fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { + const node = reloc.node.unwrap().?; + const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset; + const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; + + const got_vaddr = elf.shndx.got.vaddr(elf); + const got_index: u64 = elf.got.getIndex(reloc.target).?; + const got_offset: u64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, + }; + const addend: u64 = @bitCast(reloc.addend); + + const target_val: u64 = switch (reloc.type.target) { + .abs => got_vaddr +% got_offset +% addend, + .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, + .offset => got_offset +% addend, + .special => return reloc.type.action.special.applyInner( + elf, + got_vaddr, + got_offset, + addend, + dest_vaddr, + dest_slice, + ), + }; + try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); + } + + fn delete(reloc: *GotReloc, elf: *Elf) void { + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + reloc.* = .{ + .node = .none, + .offset = undefined, + .target = undefined, + .addend = undefined, + .type = undefined, + .result = undefined, + }; + } +}; + +fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void { + const gpa = elf.base.comp.gpa; + + try elf.symtab.ensureUnusedCapacity(gpa, len); + + // If adding locals, we may need to move one global out of the way for each local. If adding + // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals + // around to keep `.dynsym` compact. Either way, the maximum is N. + try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len); + + { + // Ensure the symtab section's node is big enough + const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) { + inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym), + }; + try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_node_size); + } + + switch (kind) { + .all_local => {}, + .maybe_global => { + try elf.globals.strong_def.ensureUnusedCapacity(gpa, len); + try elf.globals.weak_def.ensureUnusedCapacity(gpa, len); + try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len); + try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len); + + try elf.node_global_symbols.ensureUnusedCapacity(gpa, len); + + if (elf.shndx.dynsym != .UNDEF) { + const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) { + inline else => |shdr, class| .{ + elf.targetLoad(&shdr.size), + @sizeOf(class.ElfN().Sym), + }, + }; + const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size)); + + const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size; + try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, dynsym_need_size); + + try elf.ensureDynsymHashCapacity(dynsym_cur_len + len); + + try elf.ensureUnusedPltCapacity(len); + } + }, + } +} + fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void { const gpa = elf.base.comp.gpa; @@ -1841,7 +2237,7 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void { // We don't need to add any buckets, but we still need to make sure the section is large // enough to fit `max_dynsym_count` chains. const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4; - try elf.shndx.hash.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size); + try elf.shndx.hash.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size); return; } // We need more buckets, so we'll have to rebuild the hash table. @@ -1853,7 +2249,7 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void { { const need_size = @sizeOf(info.Header()) + (new_nbucket + max_dynsym_count) * 4; - try elf.shndx.hash.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size); + try elf.shndx.hash.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size); } elf.mf.nodes_lock.lock(); @@ -1984,53 +2380,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { } } -fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void { - const gpa = elf.base.comp.gpa; - - try elf.symtab.ensureUnusedCapacity(gpa, len); - - // If adding locals, we may need to move one global out of the way for each local. If adding - // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals - // around to keep `.dynsym` compact. Either way, the maximum is N. - try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len); - - { - // Ensure the symtab section's node is big enough - const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) { - inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym), - }; - try Section.Index.symtab.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_node_size); - } - - switch (kind) { - .all_local => {}, - .maybe_global => { - try elf.globals.strong_def.ensureUnusedCapacity(gpa, len); - try elf.globals.weak_def.ensureUnusedCapacity(gpa, len); - try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len); - try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len); - - try elf.node_global_symbols.ensureUnusedCapacity(gpa, len); - - if (elf.shndx.dynsym != .UNDEF) { - const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) { - inline else => |shdr, class| .{ - elf.targetLoad(&shdr.size), - @sizeOf(class.ElfN().Sym), - }, - }; - const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size)); - - const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size; - try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, dynsym_need_size); - - try elf.ensureDynsymHashCapacity(dynsym_cur_len + len); - - try elf.ensureUnusedPltCapacity(len); - } - }, - } -} fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { const gpa = elf.base.comp.gpa; @@ -2044,19 +2393,19 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { // Ensure the `.plt` section's node is big enough: { const need_size: usize = plt.entry_size * (1 + need_plt_count); - try elf.shndx.plt.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size); + try elf.shndx.plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size); } // If there is a `.got.plt` section, ensure its node is big enough if (plt.got_plt) |got_plt| { const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count); - try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size); + try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size); } // If there is a `.plt.sec` section, ensure its node is big enough if (plt.plt_sec) |plt_sec| { const need_size: usize = plt_sec.entry_size * need_plt_count; - try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size); + try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size); } } /// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at @@ -2138,7 +2487,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L .other = .{ .visibility = .DEFAULT }, .shndx = opts.shndx.toSection().?, }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym); } @@ -2323,7 +2672,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ .other = .{ .visibility = opts.visibility }, .shndx = opts.shndx.toSection().?, }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(Sym, sym); } }, @@ -2359,7 +2708,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ .other = .{ .visibility = opts.visibility }, .shndx = opts.shndx.toSection().?, }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(Sym, sym); } elf.appendDynsymHashEntry(dynsym_index); @@ -2869,7 +3218,8 @@ const Symbol = struct { .abs, .pltabs => {}, } if (!reloc.type.action.simple.dest.isAddr(elf)) continue; - switch (elf.nodeWantsDsoRelocation(reloc.node)) { + const node = reloc.node.unwrap().?; + switch (elf.nodeWantsDsoRelocation(node)) { .no => continue, .yes_textrel => elf.textrel_count += 1, .yes => {}, @@ -2877,7 +3227,7 @@ const Symbol = struct { // There is capacity for a relocation because we just deleted one earlier. reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ .type = .relative(elf), - .offset = elf.getNodeVAddr(reloc.node) + reloc.offset, + .offset = elf.getNodeVAddr(node) + reloc.offset, .raw_sym_index = 0, .addend = 0, }).toOptional(); @@ -2982,6 +3332,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { + .deleted, .archive, .archive_header, .archive_input_member, @@ -2991,10 +3342,27 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { .shdr, .segment, .section, + .section_manual_size, .input_section, .copied_global, + .debug_shared, + .eh_frame_footer, + .unit_padding, + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, => unreachable, - inline .nav, .uav, .lazy_code, @@ -3005,10 +3373,9 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { return s.toTypeErased(); } pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.SymbolId { - const diags = &elf.base.comp.link_diags; return elf.lazySymbolInner(lazy) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; } fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId { @@ -3024,13 +3391,16 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol .code => .{ .text, .FUNC }, .const_data => .{ .rodata, .OBJECT }, }; - const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{}); - var name_buf: [64]u8 = undefined; - const name = std.mem.print( - &name_buf, - "__lazy_{t}_{d}", - .{ lazy.kind, @backingInt(lazy.ty) }, - ) catch unreachable; + const node = elf.addNodeAssumeCapacity( + try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}), + switch (lazy.kind) { + .code => .{ .lazy_code = @fromBackingInt(@intCast(gop.index)) }, + .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(gop.index)) }, + }, + ); + var name_buf: [std.fmt.count("__lazy_const_data_{d}", .{std.math.maxInt(u32)})]u8 = undefined; + const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch + unreachable; gop.value_ptr.* = .{ .lsi = elf.addLocalSymbolAssumeCapacity(.{ .node = .wrap(node), @@ -3043,11 +3413,7 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol .first_symbol_reloc = .none, .first_got_reloc = .none, }; - elf.nodes.appendAssumeCapacity(switch (lazy.kind) { - .code => .{ .lazy_code = @fromBackingInt(@intCast(gop.index)) }, - .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(gop.index)) }, - }); - elf.synth_prog_node.increaseEstimatedTotalItems(1); + elf.base.comp.link_prog_node.increaseEstimatedTotalItems(1); } const s: Symbol.Id = .local(gop.value_ptr.lsi); return s.toTypeErased(); @@ -3062,8 +3428,8 @@ pub const ExternSymbolOpts = struct { pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.SymbolId { const diags = &elf.base.comp.link_diags; return (elf.externSymbolInner(opts) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }).toTypeErased(); } fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id { @@ -3103,15 +3469,33 @@ pub fn addReloc( const node: MappedFile.Node.Index = Node.fromAtom(atom); const diags = &elf.base.comp.link_diags; elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) { + else => |e| return e, error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), error.UnknownRelocation => unreachable, // codegen bug error.NonStaticRelocation => unreachable, // codegen bug error.UnimplementedRelocation => unreachable, // codegen bug (asking Elf2 for a relocation it does not support) + }; +} +pub fn addNodeReloc( + elf: *Elf, + node: MappedFile.Node.Index, + offset: u64, + target: MappedFile.Node.Index, + addend: i64, + @"type": NodeReloc.Type, +) link.Error!void { + const diags = &elf.base.comp.link_diags; + elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) { else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + }; + elf.addNodeRelocAssumeCapacity(node, offset, target, addend, @"type") catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; } pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId { @@ -3129,8 +3513,8 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.Fil }); } const nmi = elf.navMapIndex(zcu, nav_index) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; const s: Symbol.Id = .local(nmi.symbol(elf)); return s.toTypeErased(); @@ -3142,8 +3526,8 @@ pub fn uavSymbol( ) link.Error!link.File.SymbolId { const diags = &elf.base.comp.link_diags; const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; const s: Symbol.Id = .local(umi.symbol(elf)); return s.toTypeErased(); @@ -3166,7 +3550,11 @@ pub fn getUavVAddr( } pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) link.Error!u64 { try elf.addReloc( - reloc_info.parent.atom_index, + switch (reloc_info.parent) { + .none => unreachable, + .atom_index => |atom_id| atom_id, + .debug_output => |debug_output| Node.toAtom(debug_output.dwarf2.info_writer.ni), + }, reloc_info.offset, target, reloc_info.addend, @@ -3183,8 +3571,8 @@ pub fn lowerUav( _ = pt; const diags = &elf.base.comp.link_diags; const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; const s: Symbol.Id = .local(umi.symbol(elf)); return s.toTypeErased(); @@ -3283,7 +3671,7 @@ const StringTable = struct { break :size .{ old_size, new_size }; }, }; - try ni.ensureMinimumSize(&elf.mf, gpa, new_size); + try ni.ensureMinimumSize(gpa, &elf.mf, new_size); const slice = ni.slice(&elf.mf)[old_size..]; @memcpy(slice[0..key.len], key); slice[key.len] = 0; @@ -3393,6 +3781,7 @@ fn create( .data = undefined, .data_rel_ro = undefined, .tls = .none, + .gnu_eh_frame = .none, }, .archive = null, .nodes = .empty, @@ -3410,6 +3799,16 @@ fn create( .tdata = .UNDEF, .rela_dyn = .UNDEF, .rela_plt = .UNDEF, + .debug_abbrev = .UNDEF, + .eh_frame_hdr = .UNDEF, + .eh_frame = .UNDEF, + .debug_frame = .UNDEF, + .debug_info = .UNDEF, + .debug_line = .UNDEF, + .debug_line_str = .UNDEF, + .debug_rnglists = .UNDEF, + .debug_str = .UNDEF, + .debug_str_offsets = .UNDEF, .init_array = .UNDEF, .fini_array = .UNDEF, .preinit_array = .UNDEF, @@ -3437,6 +3836,7 @@ fn create( .got = .empty, .plt = .empty, .plt_first_symbol_reloc = .none, + .eh_frame_hdr_first_symbol_reloc = .none, .needed = .empty, .inputs = .empty, .input_pending_index = 0, @@ -3451,15 +3851,31 @@ fn create( }), .pending_uavs = .empty, .symbol_relocs = .empty, + .node_relocs = .empty, .got_relocs = .empty, .tls_size_symbol_relocs = .empty, .section_by_name = .empty, .changed_symtab_index = .empty, .textrel_count = 0, + + .dwarf = .init(&elf.base, switch (comp.config.debug_format) { + .strip => .@"32", // for .eh_frame + .dwarf => |v| v, + .code_view => unreachable, + }), + .dwarf_shared = comptime .initFill(.{ + .first_target_reloc = .none, + }), + .dwarf_units = &.{}, + .dwarf_consts = .empty, + .dwarf_globals = .empty, + .dwarf_funcs = .empty, + .dwarf_decls = .empty, + .overflowed_reloc_count = 0, .misaligned_reloc_count = 0, + .const_prog_node = .none, - .synth_prog_node = .none, .input_prog_node = .none, }; errdefer elf.deinit(); @@ -3498,10 +3914,20 @@ pub fn deinit(elf: *Elf) void { for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); elf.pending_uavs.deinit(gpa); elf.symbol_relocs.deinit(gpa); + elf.node_relocs.deinit(gpa); elf.got_relocs.deinit(gpa); elf.tls_size_symbol_relocs.deinit(gpa); elf.section_by_name.deinit(gpa); elf.changed_symtab_index.deinit(gpa); + + elf.dwarf.deinit(); + for (elf.dwarf_units) |*dwarf_unit| dwarf_unit.debug_rnglists_symbol_relocs.deinit(gpa); + gpa.free(elf.dwarf_units); + elf.dwarf_consts.deinit(gpa); + elf.dwarf_globals.deinit(gpa); + elf.dwarf_funcs.deinit(gpa); + elf.dwarf_decls.deinit(gpa); + elf.* = undefined; } @@ -3518,11 +3944,17 @@ fn initHeaders( const gpa = comp.gpa; const is_archive = comp.config.output_mode == .Lib and comp.config.link_mode == .static; - const have_dynamic_section = switch (@"type") { + const have_dynamic = switch (@"type") { .REL => false, .EXEC => comp.config.link_mode == .dynamic, .DYN => true, }; + const have_eh_frame = machine == .X86_64 and comp.config.any_unwind_tables; + const have_debug_frame = machine == .X86_64 and switch (comp.config.debug_format) { + .strip => false, + .dwarf => !comp.config.any_unwind_tables, + .code_view => unreachable, + }; const addr_align: Alignment = switch (class) { .NONE, _ => unreachable, .@"32" => .@"4", @@ -3537,7 +3969,7 @@ fn initHeaders( // // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it // prevents alignment bugs from being hidden by your filesystem's block alignment. - const node_block_align: Alignment = elf.mf.flags.block_size; + const node_block_align = elf.mf.flags.block_size; const plt: PltInfo = .fromMachine(machine); @@ -3552,7 +3984,7 @@ fn initHeaders( shnum += 1; // .data shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata shnum += 1; // .data.rel.ro - if (have_dynamic_section) { + if (have_dynamic) { shnum += 1; // .dynamic shnum += 1; // .dynstr shnum += 1; // .dynsym @@ -3560,6 +3992,24 @@ fn initHeaders( shnum += 1; // .rela.dyn shnum += 1; // .rela.plt } + if (have_eh_frame) { + shnum += @intFromBool(@"type" != .REL); // .eh_frame_hdr + shnum += 1; // .eh_frame + } + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => { + shnum += 1; // .debug_abbrev + shnum += @intFromBool(have_debug_frame); // .debug_frame + shnum += 1; // .debug_info + shnum += 1; // .debug_line + shnum += 1; // .debug_line_str + shnum += 1; // .debug_rnglists + shnum += 1; // .debug_str + shnum += 1; // .debug_str_offsets + }, + .code_view => unreachable, + } if (@"type" != .REL) { shnum += 1; // .got shnum += @intFromBool(plt.got_plt != null); // .got.plt @@ -3574,14 +4024,15 @@ fn initHeaders( interp: u32, rodata: u32, text: u32, - data: u32, /// On most targets this is `undefined`, but on machines where JUMP_SLOT relocations write /// directly to the PLT, we place the PLT in its own segment in order to avoid making the /// general data segment RWX. plt: u32, + data: u32, tls: u32, dynamic: u32, relro: u32, + gnu_eh_frame: u32, gnu_stack: u32, }, const phnum: u32 = ph: { switch (@"type") { @@ -3622,7 +4073,7 @@ fn initHeaders( defer phnum += 1; break :phndx phnum; } else undefined, - .dynamic = if (have_dynamic_section) phndx: { + .dynamic = if (have_dynamic) phndx: { defer phnum += 1; break :phndx phnum; } else undefined, @@ -3630,6 +4081,10 @@ fn initHeaders( defer phnum += 1; break :phndx phnum; }, + .gnu_eh_frame = if (have_eh_frame) phndx: { + defer phnum += 1; + break :phndx phnum; + } else undefined, .gnu_stack = phndx: { defer phnum += 1; break :phndx phnum; @@ -3643,7 +4098,8 @@ fn initHeaders( const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header 3 + // `.elf`, `.ehdr`, and `.shdr` nodes (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node - (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node + (phnum -| 1) + // -1 because the GNU_STACK phdr does not have a `.segment` node + @intFromBool(have_eh_frame and @"type" != .REL); // eh_frame_footer try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len); try elf.shdrs.ensureTotalCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF @@ -3652,21 +4108,21 @@ fn initHeaders( try elf.symtab.ensureTotalCapacity(gpa, 1); if (is_archive) { - elf.nodes.appendAssumeCapacity(.archive); + const archive_ni = elf.addNodeAssumeCapacity(.root, .archive); - const archive_ni: MappedFile.Node.Index = .root; - - const archive_header_ni = try archive_ni.addOnlyHeaderChild(&elf.mf, gpa, .{ - // We intentionally do not set `.alignment = .@"2"` here, because the string table data - // in this node does not need to have an aligned length. (This node's offset is aligned - // regardless by virtue of it being a header.) - .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), - // The archive header uses 'next_moved' events to resize the "//" member, so that it - // absorbs all padding between `archive_header_ni` and the actual object file members. - .enable_next_moved = true, - .next_moved = true, - }); - elf.nodes.appendAssumeCapacity(.archive_header); + const archive_header_ni = elf.addNodeAssumeCapacity( + try archive_ni.addOnlyHeaderChild(gpa, &elf.mf, .{ + // We intentionally do not set `.alignment = .@"2"` here, because the string table data + // in this node does not need to have an aligned length. (This node's offset is aligned + // regardless by virtue of it being a header.) + .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), + // The archive header uses 'next_moved' events to resize the "//" member, so that it + // absorbs all padding between `archive_header_ni` and the actual object file members. + .enable_next_moved = true, + .next_moved = true, + }), + .archive_header, + ); const archive_header_slice = archive_header_ni.slice(&elf.mf); @memcpy(archive_header_slice[0..std.elf.ARMAG.len], std.elf.ARMAG); const strtab_ar_hdr: *std.elf.ar_hdr = @ptrCast(archive_header_slice[std.elf.ARMAG.len..]); @@ -3680,18 +4136,19 @@ fn initHeaders( .ar_fmag = std.elf.ARFMAG.*, }; - elf.ni.elf = try archive_ni.addOnlyFooterChild(&elf.mf, gpa, .{ + elf.ni.elf = elf.addNodeAssumeCapacity(try archive_ni.addOnlyFooterChild(gpa, &elf.mf, .{ .alignment = node_block_align.max(.@"2"), .bubbles_moved = false, .resized = true, // ensure that this node's `ar_hdr.ar_size` is updated at least once - }); - elf.nodes.appendAssumeCapacity(.elf); + }), .elf); - const elf_ar_hdr_ni = try archive_ni.addFooterChildBefore(&elf.mf, gpa, .wrap(elf.ni.elf), .{ - .alignment = .@"2", - .size = @sizeOf(std.elf.ar_hdr), - }); - elf.nodes.appendAssumeCapacity(.archive_elf_member_header); + const elf_ar_hdr_ni = elf.addNodeAssumeCapacity( + try archive_ni.addFooterChildBefore(gpa, &elf.mf, .wrap(elf.ni.elf), .{ + .alignment = .@"2", + .size = @sizeOf(std.elf.ar_hdr), + }), + .archive_elf_member_header, + ); // Must be populated before we call `populateArchiveMemberName` below. elf.archive = .{ @@ -3717,10 +4174,7 @@ fn initHeaders( defer gpa.free(zcu_member_name); // After this call returns, `elf_ar_hdr` is invalidated. try elf.populateArchiveMemberName(elf_ar_hdr, zcu_member_name); - } else { - elf.ni.elf = .root; - elf.nodes.appendAssumeCapacity(.elf); - } + } else elf.ni.elf = elf.addNodeAssumeCapacity(.root, .elf); const entsize: struct { ph: u32, sh: u32 } = switch (class) { .NONE, _ => unreachable, @@ -3736,69 +4190,65 @@ fn initHeaders( if (@"type" != .REL) { // This node will contain the ehdr, which must be at the start of the ELF file, so this // node must itself be a header of the `.elf` node. - elf.ni.rodata = try elf.ni.elf.addOnlyHeaderChild(&elf.mf, gpa, .{ + elf.ni.rodata = elf.addNodeAssumeCapacity(try elf.ni.elf.addOnlyHeaderChild(gpa, &elf.mf, .{ // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node .alignment = node_block_align.max(addr_align), .moved = true, .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata }); + }), .{ .segment = phndx.rodata }); elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata); - elf.ni.phdr = try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{ + elf.ni.phdr = elf.addNodeAssumeCapacity(try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ .size = @as(u64, phnum) * entsize.ph, .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above .moved = true, .resized = true, .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr }); + }), .{ .segment = phndx.phdr }); elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr); - elf.ni.text = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{ + elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ .alignment = node_block_align, .moved = true, .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text }); + }), .{ .segment = phndx.text }); elf.phdrs.items[phndx.text] = .wrap(elf.ni.text); - elf.ni.data = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{ + elf.ni.data = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node .alignment = node_block_align.max(addr_align), .moved = true, .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data }); + }), .{ .segment = phndx.data }); elf.phdrs.items[phndx.data] = .wrap(elf.ni.data); - if (plt.got_plt == null) { - const plt_ni = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{ + if (plt.got_plt == null) elf.phdrs.items[phndx.plt] = .wrap(elf.addNodeAssumeCapacity( + try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ .alignment = node_block_align, .moved = true, .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt }); - elf.phdrs.items[phndx.plt] = .wrap(plt_ni); - } + }), + .{ .segment = phndx.plt }, + )); - elf.ni.data_rel_ro = try elf.ni.data.addFloatingChild(&elf.mf, gpa, .{ + elf.ni.data_rel_ro = elf.addNodeAssumeCapacity(try elf.ni.data.addFloatingChild(gpa, &elf.mf, .{ // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above. .alignment = node_block_align.max(addr_align), .moved = true, .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro }); + }), .{ .segment = phndx.relro }); elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro); if (comp.config.any_non_single_threaded) { - elf.ni.tls = .wrap(try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{ - .alignment = node_block_align, - .moved = true, - .bubbles_moved = false, - })); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls }); + elf.ni.tls = .wrap(elf.addNodeAssumeCapacity( + try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ + .alignment = node_block_align, + .moved = true, + .bubbles_moved = false, + }), + .{ .segment = phndx.tls }, + )); elf.phdrs.items[phndx.tls] = elf.ni.tls; } @@ -3822,11 +4272,10 @@ fn initHeaders( .REL => elf.ni.elf, .DYN, .EXEC => elf.ni.rodata, }; - elf.ni.ehdr = try parent_ni.addOnlyHeaderChild(&elf.mf, gpa, .{ + elf.ni.ehdr = elf.addNodeAssumeCapacity(try parent_ni.addOnlyHeaderChild(gpa, &elf.mf, .{ .size = @sizeOf(ElfN.Ehdr), .alignment = addr_align, - }); - elf.nodes.appendAssumeCapacity(.ehdr); + }), .ehdr); const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf))); ehdr.ident = .{ @@ -3872,17 +4321,16 @@ fn initHeaders( ehdr.shentsize = @sizeOf(ElfN.Shdr); ehdr.shnum = 1; // Only the SHN_UNDEF shdr initially---will be incremented by `addSection` ehdr.shstrndx = std.elf.SHN_UNDEF; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); + if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); }, } - elf.ni.shdr = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{ - .size = node_block_align.forward(1 * entsize.sh), // as above, only the SHN_UNDEF initially - .alignment = addr_align.max(node_block_align), + elf.ni.shdr = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{ + .size = 1 * entsize.sh, // as above, only the null shdr initially + .alignment = addr_align, .moved = true, .resized = true, - }); - elf.nodes.appendAssumeCapacity(.shdr); + }), .shdr); switch (class) { .NONE, _ => unreachable, @@ -3925,8 +4373,7 @@ fn initHeaders( elf.ni.phdr.slice(&elf.mf)[0 .. phnum * @sizeOf(ElfN.Phdr)], )); - const ph_phdr = &phdr[phndx.phdr]; - ph_phdr.* = .{ + phdr[phndx.phdr] = .{ .type = .PHDR, .offset = 0, .vaddr = 0, @@ -3937,22 +4384,18 @@ fn initHeaders( .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()), }; - if (maybe_interp) |_| { - const ph_interp = &phdr[phndx.interp]; - ph_interp.* = .{ - .type = .INTERP, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = 1, - }; - } + if (maybe_interp) |_| phdr[phndx.interp] = .{ + .type = .INTERP, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = 1, + }; - const ph_rodata = &phdr[phndx.rodata]; - ph_rodata.* = .{ + phdr[phndx.rodata] = .{ .type = .NULL, .offset = 0, .vaddr = @intCast(base_vaddr), @@ -3963,8 +4406,7 @@ fn initHeaders( .@"align" = @intCast(page_align.toByteUnits()), }; - const ph_text = &phdr[phndx.text]; - ph_text.* = .{ + phdr[phndx.text] = .{ .type = .NULL, .offset = 0, .vaddr = @intCast(base_vaddr), @@ -3975,8 +4417,7 @@ fn initHeaders( .@"align" = @intCast(page_align.toByteUnits()), }; - const ph_data = &phdr[phndx.data]; - ph_data.* = .{ + phdr[phndx.data] = .{ .type = .NULL, .offset = 0, .vaddr = @intCast(base_vaddr), @@ -3987,50 +4428,40 @@ fn initHeaders( .@"align" = @intCast(page_align.toByteUnits()), }; - if (plt.got_plt == null) { - const ph_plt = &phdr[phndx.plt]; - ph_plt.* = .{ - .type = .NULL, - .offset = 0, - .vaddr = @intCast(base_vaddr), - .paddr = @intCast(base_vaddr), - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .W = true, .X = true }, - .@"align" = @intCast(page_align.toByteUnits()), - }; - } + if (plt.got_plt == null) phdr[phndx.plt] = .{ + .type = .NULL, + .offset = 0, + .vaddr = @intCast(base_vaddr), + .paddr = @intCast(base_vaddr), + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true, .W = true, .X = true }, + .@"align" = @intCast(page_align.toByteUnits()), + }; - if (elf.ni.tls.unwrap()) |tls_segment_ni| { - const ph_tls = &phdr[phndx.tls]; - ph_tls.* = .{ - .type = .TLS, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()), - }; - } + if (elf.ni.tls.unwrap()) |tls_segment_ni| phdr[phndx.tls] = .{ + .type = .TLS, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()), + }; - if (have_dynamic_section) { - const ph_dynamic = &phdr[phndx.dynamic]; - ph_dynamic.* = .{ - .type = .DYNAMIC, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .W = true }, - .@"align" = @intCast(addr_align.toByteUnits()), - }; - } + if (have_dynamic) phdr[phndx.dynamic] = .{ + .type = .DYNAMIC, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true, .W = true }, + .@"align" = @intCast(addr_align.toByteUnits()), + }; - const ph_relro = &phdr[phndx.relro]; - ph_relro.* = .{ + phdr[phndx.relro] = .{ .type = .GNU_RELRO, .offset = 0, .vaddr = 0, @@ -4041,8 +4472,18 @@ fn initHeaders( .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()), }; - const ph_gnu_stack = &phdr[phndx.gnu_stack]; - ph_gnu_stack.* = .{ + if (have_eh_frame) phdr[phndx.gnu_eh_frame] = .{ + .type = .GNU_EH_FRAME, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = @sizeOf(Dwarf.EhFrameHdr), + .memsz = @sizeOf(Dwarf.EhFrameHdr), + .flags = .{ .R = true }, + .@"align" = 4, + }; + + phdr[phndx.gnu_stack] = .{ .type = .GNU_STACK, .offset = 0, .vaddr = 0, @@ -4071,7 +4512,7 @@ fn initHeaders( .addralign = 0, .entsize = 0, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); + if (target_endian != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); elf.symtab.addOneAssumeCapacity().* = .{ .node = .none, @@ -4084,6 +4525,7 @@ fn initHeaders( .entsize = @sizeOf(ElfN.Sym), .node_align = node_block_align, .info = 1, // index of first non-local symbol + .manual_size = true, })); const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class)); symtab_null.* = .{ @@ -4094,7 +4536,7 @@ fn initHeaders( .other = .{ .visibility = .DEFAULT }, .shndx = std.elf.SHN_UNDEF, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null); + if (target_endian != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null); const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class)); ehdr.shstrndx = ehdr.shnum; @@ -4105,6 +4547,7 @@ fn initHeaders( .size = 1, .entsize = 1, .node_align = node_block_align, + .manual_size = true, })); Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0; @@ -4117,6 +4560,7 @@ fn initHeaders( .size = 1, .entsize = 1, .node_align = node_block_align, + .manual_size = true, })); Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0; switch (elf.shdrPtr(.symtab)) { @@ -4156,6 +4600,7 @@ fn initHeaders( .flags = .{ .WRITE = true, .ALLOC = true }, .addralign = addr_align, .entsize = @intCast(addr_align.toByteUnits()), + .manual_size = true, }); { const init_plt_size = plt.entry_size * plt.header_entries; @@ -4168,6 +4613,7 @@ fn initHeaders( .size = got_plt.header_entries * elf.targetPtrSize(), .addralign = addr_align, .entsize = @intCast(addr_align.toByteUnits()), + .manual_size = true, }); elf.shndx.plt = try elf.addSection(elf.ni.text, .{ .name = ".plt", @@ -4176,6 +4622,7 @@ fn initHeaders( .size = plt.@"align".forward(init_plt_size), .addralign = plt.@"align", .node_align = node_block_align, + .manual_size = true, }); } else { elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{ @@ -4185,6 +4632,7 @@ fn initHeaders( .size = plt.@"align".forward(init_plt_size), .addralign = plt.@"align", .node_align = node_block_align, + .manual_size = true, }); } // And the award for most annoying PLT requirement goes to SPARC, which decided that the @@ -4203,13 +4651,15 @@ fn initHeaders( .node_align = node_block_align, }); if (maybe_interp) |interp| { - const interp_ni = try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{ - .size = interp.len + 1, - .moved = true, - .resized = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp }); + const interp_ni = elf.addNodeAssumeCapacity( + try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ + .size = interp.len + 1, + .moved = true, + .resized = true, + .bubbles_moved = false, + }), + .{ .segment = phndx.interp }, + ); elf.phdrs.items[phndx.interp] = .wrap(interp_ni); const sec_interp_shndx = try elf.addSection(interp_ni, .{ @@ -4222,14 +4672,16 @@ fn initHeaders( @memcpy(sec_interp[0..interp.len], interp); sec_interp[interp.len] = 0; } - if (have_dynamic_section) { + if (have_dynamic) { assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align)); - const dynamic_ni = try elf.ni.data_rel_ro.addFloatingChild(&elf.mf, gpa, .{ - .alignment = addr_align, - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic }); + const dynamic_ni = elf.addNodeAssumeCapacity( + try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{ + .alignment = addr_align, + .moved = true, + .bubbles_moved = false, + }), + .{ .segment = phndx.dynamic }, + ); elf.phdrs.items[phndx.dynamic] = .wrap(dynamic_ni); const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{ @@ -4239,6 +4691,7 @@ fn initHeaders( .size = 1, .entsize = 1, .node_align = node_block_align, + .manual_size = true, }); dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0; elf.shndx.dynstr = dynstr_shndx; @@ -4257,6 +4710,7 @@ fn initHeaders( .addralign = addr_align, .entsize = @sizeOf(Sym), .node_align = node_block_align, + .manual_size = true, }); const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class)); dynsym_null.* = .{ @@ -4267,7 +4721,7 @@ fn initHeaders( .other = .{ .visibility = .DEFAULT }, .shndx = std.elf.SHN_UNDEF, }; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields( + if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields( Sym, dynsym_null, ); @@ -4285,6 +4739,7 @@ fn initHeaders( .addralign = addr_align, .entsize = rela_size, .node_align = node_block_align, + .manual_size = true, }); elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ .name = ".rela.plt", @@ -4295,6 +4750,7 @@ fn initHeaders( .addralign = addr_align, .entsize = rela_size, .node_align = node_block_align, + .manual_size = true, }); elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{ .name = ".dynamic", @@ -4303,6 +4759,7 @@ fn initHeaders( .link = dynstr_shndx.toSection().?, .entsize = @intCast(addr_align.toByteUnits() * 2), .addralign = addr_align, + .manual_size = true, }); switch (elf.targetDynsymHashInfo()) { inline else => |info| { @@ -4318,6 +4775,7 @@ fn initHeaders( .addralign = .fromByteUnits(@sizeOf(info.Int())), // initially: nbucket = 8 + nchain = 1 .size = @sizeOf(info.Header()) + @sizeOf(info.Int()) * (8 + 1), + .manual_size = true, }); const hash_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); const header: *info.Header() = @ptrCast(hash_slice[0..@sizeOf(info.Header())]); @@ -4386,34 +4844,78 @@ fn initHeaders( elf.plt_first_symbol_reloc = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); try elf.ensureUnusedRelocCapacity(plt_ni, 3); elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) { + else => |e| return e, error.UnknownRelocation => unreachable, error.NonStaticRelocation => unreachable, error.UnimplementedRelocation => unreachable, - else => |e| return e, }; elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) { + else => |e| return e, error.UnknownRelocation => unreachable, error.NonStaticRelocation => unreachable, error.UnimplementedRelocation => unreachable, - else => |e| return e, }; elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) { + else => |e| return e, error.UnknownRelocation => unreachable, error.NonStaticRelocation => unreachable, error.UnimplementedRelocation => unreachable, - else => |e| return e, }; }, .SPARCV9 => {}, } } + if (have_eh_frame) { + const gnu_eh_frame = elf.addNodeAssumeCapacity( + try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{ + .size = @sizeOf(Dwarf.EhFrameHdr), + .alignment = .@"4", + .moved = true, + .bubbles_moved = false, + }), + .{ .segment = phndx.gnu_eh_frame }, + ); + elf.ni.gnu_eh_frame = .wrap(gnu_eh_frame); + elf.phdrs.items[phndx.gnu_eh_frame] = elf.ni.gnu_eh_frame; + + elf.shndx.eh_frame_hdr = try elf.addSection(gnu_eh_frame, .{ + .name = ".eh_frame_hdr", + .type = .PROGBITS, + .flags = .{ .ALLOC = true }, + .size = @sizeOf(Dwarf.EhFrameHdr), + .addralign = .@"4", + }); + elf.shndx.eh_frame = try elf.addSection(elf.ni.rodata, .{ + .name = ".eh_frame", + .flags = .{ .ALLOC = true }, + .addralign = addr_align, + .node_align = elf.mf.flags.block_size, + .manual_size = true, + }); + + const eh_frame_hdr_ni = elf.shndx.eh_frame_hdr.get(elf).ni; + elf.eh_frame_hdr_first_symbol_reloc = + @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); + try elf.dwarf.genEhFrameHdr( + Node.toAtom(eh_frame_hdr_ni), + @ptrCast(@alignCast(eh_frame_hdr_ni.slice(&elf.mf))), + Symbol.Id.local(elf.shndx.eh_frame.get(elf).lsi).toTypeErased(), + ); + _ = elf.addNodeAssumeCapacity( + try elf.shndx.eh_frame.get(elf).ni.addOnlyFooterChild(gpa, &elf.mf, .{ + .size = addr_align.forward(4), + .alignment = addr_align, + }), + .eh_frame_footer, + ); + } // Populate reserved GOT words. switch (machine) { .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), .X86_64 => { try elf.got.ensureUnusedCapacity(gpa, 3); - elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { + elf.got.putAssumeCapacityNoClobber(switch (have_dynamic) { true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, false => .{ .reserved = 0 }, }, .none); @@ -4422,7 +4924,7 @@ fn initHeaders( }, .LOONGARCH, .SPARCV9 => { try elf.got.ensureUnusedCapacity(gpa, 1); - elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { + elf.got.putAssumeCapacityNoClobber(switch (have_dynamic) { true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, false => .{ .reserved = 0 }, }, .none); @@ -4567,7 +5069,7 @@ fn initHeaders( }) catch |err| switch (err) { error.MultipleDefinitions => unreachable, // no inputs are processed yet }; - if (have_dynamic_section) { + if (have_dynamic) { _ = elf.addGlobalSymbolAssumeCapacity(.{ .node = .wrap(elf.shndx.dynamic.get(elf).ni), .name = try .string(elf, "_DYNAMIC"), @@ -4583,13 +5085,57 @@ fn initHeaders( } } else { assert(maybe_interp == null); - assert(!have_dynamic_section); + assert(!have_dynamic); + if (have_eh_frame) elf.shndx.eh_frame = try elf.addSection(elf.ni.rodata, .{ + .name = ".eh_frame", + .type = if (machine == .X86_64) .X86_64_UNWIND else .NULL, + .flags = .{ .ALLOC = true }, + .addralign = addr_align, + .node_align = elf.mf.flags.block_size, + .manual_size = true, + }); } if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{ .name = ".tdata", .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, .node_align = node_block_align, }); + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => { + elf.shndx.debug_abbrev = try elf.addSection(elf.ni.elf, .{ .name = ".debug_abbrev" }); + if (have_debug_frame) elf.shndx.debug_frame = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_frame", + .addralign = addr_align, + .node_align = elf.mf.flags.block_size, + .manual_size = true, + }); + elf.shndx.debug_info = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_info", + .node_align = elf.mf.flags.block_size, + }); + elf.shndx.debug_line = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_line", + .node_align = elf.mf.flags.block_size, + }); + elf.shndx.debug_line_str = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_line_str", + .flags = .{ .MERGE = true, .STRINGS = true }, + }); + elf.shndx.debug_rnglists = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_rnglists", + .node_align = elf.mf.flags.block_size, + }); + elf.shndx.debug_str = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_str", + .flags = .{ .MERGE = true, .STRINGS = true }, + }); + elf.shndx.debug_str_offsets = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_str_offsets", + }); + }, + .code_view => unreachable, + } assert(elf.nodes.len == expected_nodes_len); assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF @@ -4599,7 +5145,7 @@ fn initHeaders( elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); } - if (have_dynamic_section) elf.dynamic = .{ + if (have_dynamic) elf.dynamic = .{ .flags = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0, .flags_1 = f: { var f: u32 = 0; @@ -4642,11 +5188,6 @@ fn initHeaders( pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { prog_node.increaseEstimatedTotalItems(4); elf.const_prog_node = prog_node.start("Constants", elf.pending_uavs.items.len); - elf.synth_prog_node = prog_node.start("Synthetics", count: { - var count: usize = 0; - for (&elf.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index; - break :count count; - }); elf.mf.update_prog_node = prog_node.start("Relocations", elf.mf.updates.items.len); elf.input_prog_node = prog_node.start("Inputs", (elf.inputs.items.len - elf.input_pending_index) + (elf.input_sections.items.len - elf.input_section_pending_index)); @@ -4657,8 +5198,6 @@ pub fn endProgress(elf: *Elf) void { elf.input_prog_node = .none; elf.mf.update_prog_node.end(); elf.mf.update_prog_node = .none; - elf.synth_prog_node.end(); - elf.synth_prog_node = .none; elf.const_prog_node.end(); elf.const_prog_node = .none; } @@ -4669,6 +5208,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { /// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data. fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { return switch (elf.getNode(ni)) { + .deleted, .archive, .archive_header, .archive_input_member, @@ -4678,18 +5218,43 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { .shdr, .segment, => unreachable, - .section => |shndx| shndx, + .section, .section_manual_size => |shndx| shndx, .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data, - => elf.getNode(ni.parent(&elf.mf).unwrap().?).section, + .debug_shared, + .eh_frame_footer, + .unit_padding, + .unit_frame, + .unit_debug_info, + .unit_debug_line, + .unit_debug_rnglists, + => switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { + else => unreachable, + .section, .section_manual_size => |shndx| shndx, + }, + .unit_frame_cie, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line_header, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => switch (elf.getNode(ni.parent(&elf.mf).unwrap().?.parent(&elf.mf).unwrap().?)) { + else => unreachable, + .section, .section_manual_size => |shndx| shndx, + }, }; } fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { return switch (elf.getNode(ni)) { + .deleted, .archive, .archive_header, .archive_input_member, @@ -4700,17 +5265,37 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { .segment, .copied_global, => unreachable, - .section => |shndx| shndx.vaddr(elf), + .section, .section_manual_size => |shndx| shndx.vaddr(elf), .input_section => |isi| isi.ptrConst(elf).vaddr, inline .nav, .uav, .lazy_code, .lazy_const_data, => |i| Symbol.Id.local(i.symbol(elf)).value(elf), + .debug_shared, + .eh_frame_footer, + .unit_padding, + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => elf.computeNodeVAddr(ni), }; } fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { - const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { + const parent_ni = ni.parent(&elf.mf).unwrap().?; + const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) { + .deleted, .archive, .archive_header, .archive_input_member, @@ -4721,14 +5306,72 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { .segment => |phndx| switch (elf.phdrSlice()) { inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), }, - .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), + .section, .section_manual_size => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), .input_section, .copied_global => unreachable, - inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), + inline .nav, + .uav, + .lazy_code, + .lazy_const_data, + => |i| Symbol.Id.local(i.symbol(elf)).value(elf), + .debug_shared, .eh_frame_footer, .unit_padding => unreachable, + .unit_frame, .unit_debug_info, .unit_debug_line => { + const section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); + break :parent_vaddr elf.getNodeShndx(parent_ni).vaddr(elf) + section_offset; + }, + .unit_frame_cie, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => unreachable, }; const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); return parent_vaddr + offset; } -fn getNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { +fn computeNodeSectionOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { + const parent_ni = ni.parent(&elf.mf).unwrap().?; + const parent_section_offset = parent_section_offset: switch (elf.getNode(parent_ni)) { + .deleted, + .archive, + .archive_header, + .archive_input_member, + .archive_elf_member_header, + .elf, + .ehdr, + .shdr, + .segment, + => unreachable, + .section, .section_manual_size => 0, + .input_section, .copied_global => unreachable, + .nav, .uav, .lazy_code, .lazy_const_data => unreachable, + .debug_shared, .eh_frame_footer, .unit_padding => unreachable, + .unit_frame, .unit_debug_info, .unit_debug_line => { + const parent_section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); + break :parent_section_offset parent_section_offset; + }, + .unit_frame_cie, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => unreachable, + }; + const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); + return parent_section_offset + offset; +} +fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset; } @@ -4736,9 +5379,16 @@ fn getNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { /// sequence of relocations, so that the caller may append the node's updated relocations. /// /// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support -/// the special-case sections '.plt' and '.dynamic'. -fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { - const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { +/// the special-case sections '.plt', '.dynamic', and '.eh_frame_hdr'. +pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { + const opts: struct { + first_symbol_reloc: ?*SymbolReloc.Index = null, + skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, + first_node_reloc: ?*NodeReloc.Index = null, + skip_node_relocs: MappedFile.Node.Index.Optional = .none, + first_got_reloc: ?*GotReloc.Index = null, + } = switch (elf.getNode(ni)) { + .deleted, .archive, .archive_header, .archive_input_member, @@ -4748,41 +5398,106 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { .shdr, .segment, .copied_global, + .debug_shared, + .eh_frame_footer, + .unit_padding, + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_line, => unreachable, // cannot contain relocs - .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) + .section, + .section_manual_size, + => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported) .input_section => |isi| .{ - &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc, - &elf.input_sections.items[@backingInt(isi)].first_got_reloc, + .first_symbol_reloc = &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc, + .first_got_reloc = &elf.input_sections.items[@backingInt(isi)].first_got_reloc, }, .nav => |nmi| .{ - &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc, - &elf.navs.values()[@backingInt(nmi)].first_got_reloc, + .first_symbol_reloc = &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc, + .first_got_reloc = &elf.navs.values()[@backingInt(nmi)].first_got_reloc, }, .uav => |umi| .{ - &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc, - null, + .first_symbol_reloc = &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc, }, inline .lazy_code, .lazy_const_data => |lmi| .{ - &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc, - &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc, + .first_symbol_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc, + .first_got_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc, + }, + .unit_debug_info_header => |ui| .{ + .first_node_reloc = &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_node_reloc, + }, + .unit_debug_info_footer => unreachable, // cannot contain relocs + .unit_debug_line_header => |ui| .{ + .first_node_reloc = &elf.dwarf_units[@backingInt(ui)].debug_line_header_first_node_reloc, + }, + .unit_debug_rnglists => unreachable, // cannot contain relocs + .const_debug_info => |cpi| .{ + .first_symbol_reloc = &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_symbol_reloc, + .first_node_reloc = &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_node_reloc, + }, + .global_debug_info => |gi| .{ + .first_symbol_reloc = &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_symbol_reloc, + .first_node_reloc = &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_node_reloc, + }, + .func_frame_fde => |fi| .{ + .first_symbol_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].frame_fde_first_symbol_reloc, + .first_node_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].frame_fde_first_node_reloc, + }, + .func_debug_info => |fi| .{ + .first_symbol_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_symbol_reloc, + .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav| + nav.lsi.index().ptr(elf).node + else + .none, + .first_node_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_node_reloc, + .skip_node_relocs = fi.get(&elf.dwarf).debug_line_ni, + }, + .func_debug_line => |fi| .{ + .first_symbol_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_line_first_symbol_reloc, + .first_node_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_line_first_node_reloc, + .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni, + }, + .decl_debug_info => |di| .{ + .first_node_reloc = &elf.dwarf_decls.getPtr(di).?.debug_info_first_node_reloc, }, }; - if (symbol_relocs.* != .none) { - for ( - elf.symbol_relocs.items[@backingInt(symbol_relocs.*)..], - @backingInt(symbol_relocs.*).., - ) |*reloc, index| { - if (reloc.node != ni) break; - reloc.delete(elf, @fromBackingInt(@intCast(index))); + if (opts.first_symbol_reloc) |ptr| { + if (ptr.* != .none) { + for (elf.symbol_relocs.items[@backingInt(ptr.*)..], @backingInt(ptr.*)..) |*reloc, index| { + if (reloc.node != ni.toOptional()) { + if (reloc.node == .none) continue; + if (reloc.node == opts.skip_symbol_relocs) continue; + break; + } + reloc.delete(elf, @fromBackingInt(@intCast(index))); + } } + ptr.* = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); } - symbol_relocs.* = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); - if (got_relocs) |ptr| { + if (opts.first_node_reloc) |ptr| { + if (ptr.* != .none) { + for (elf.node_relocs.items[@backingInt(ptr.*)..]) |*reloc| { + if (reloc.node != ni.toOptional()) { + if (reloc.node == .none) continue; + if (reloc.node == opts.skip_node_relocs) continue; + break; + } + reloc.delete(elf); + } + } + ptr.* = @fromBackingInt(@intCast(elf.node_relocs.items.len)); + } + + if (opts.first_got_reloc) |ptr| { if (ptr.* != .none) { for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| { - if (reloc.node != ni.toOptional()) break; + if (reloc.node != ni.toOptional()) { + if (reloc.node == .none) continue; + break; + } reloc.delete(elf); } } @@ -4796,29 +5511,42 @@ fn flushMovedNodeRelocs( elf: *Elf, node: MappedFile.Node.Index, node_vaddr: u64, - first_symbol_reloc: SymbolReloc.Index, - first_got_reloc: GotReloc.Index, + opts: struct { + first_symbol_reloc: SymbolReloc.Index = .none, + skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, + first_node_reloc: NodeReloc.Index = .none, + skip_node_relocs: MappedFile.Node.Index.Optional = .none, + first_got_reloc: GotReloc.Index = .none, + }, ) void { - if (first_symbol_reloc != .none) { - for (elf.symbol_relocs.items[@backingInt(first_symbol_reloc)..]) |*reloc| { - if (reloc.node != node) break; - if (reloc.rela_index.unwrap()) |rela_index| { - // The node has moved, so the offset of the relocation within the section might have - // changed, so update the `offset` field of the `ElfN.Rela` entry. - reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); + if (opts.first_symbol_reloc != .none) { + for (elf.symbol_relocs.items[@backingInt(opts.first_symbol_reloc)..]) |*reloc| { + if (reloc.node != node.toOptional()) { + if (reloc.node == .none) continue; + if (reloc.node == opts.skip_symbol_relocs) continue; + break; } - // This is not just the inverse of the above condition, because if `reloc` is relative - // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we - // still need to call `SymbolReloc.apply` to update that relocation's addend. - if (elf.ehdrType() != .REL) { - reloc.apply(elf); + reloc.flushMovedNode(elf, node_vaddr); + } + } + + if (opts.first_node_reloc != .none) { + for (elf.node_relocs.items[@backingInt(opts.first_node_reloc)..]) |*reloc| { + if (reloc.node != node.toOptional()) { + if (reloc.node == .none) continue; + if (reloc.node == opts.skip_node_relocs) continue; + break; } + reloc.flushMovedNode(elf, node_vaddr); } } - if (first_got_reloc != .none) { - for (elf.got_relocs.items[@backingInt(first_got_reloc)..]) |*reloc| { - if (reloc.node != node.toOptional()) break; + if (opts.first_got_reloc != .none) { + for (elf.got_relocs.items[@backingInt(opts.first_got_reloc)..]) |*reloc| { + if (reloc.node != node.toOptional()) { + if (reloc.node == .none) continue; + break; + } reloc.apply(elf); } } @@ -5088,13 +5816,12 @@ const ShdrPtr = union(std.elf.CLASS) { @"64": *std.elf.Elf64.Shdr, }; fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr { - const raw_slice = elf.ni.shdr.slice(&elf.mf); + const slice = elf.ni.shdr.slice(&elf.mf); switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| { - const shdrs_len = elf.shdrs.items.len + 1; // +1 for SHN_UNDEF const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast( - raw_slice[0 .. shdrs_len * @sizeOf(class.ElfN().Shdr)], + slice[0 .. @sizeOf(class.ElfN().Shdr) * (1 + elf.shdrs.items.len)], )); const shdr_ptr = &shdr_slice[@backingInt(shndx)]; return @unionInit(ShdrPtr, @tagName(class), shdr_ptr); @@ -5226,7 +5953,7 @@ fn mapInputSection(elf: *Elf, opts: struct { } switch (elf.targetLoad(&shdr.type)) { - .NULL, .PROGBITS => {}, + .NULL, .PROGBITS, .X86_64_UNWIND => {}, else => return error.SectionTypeConflict, } @@ -5270,6 +5997,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node })) |shndx| { break :section shndx; } else |err| switch (err) { + else => |e| return e, error.StripSection, error.TlsSectionUnavailable, error.UnsupportedSectionFlags, @@ -5277,7 +6005,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node error.SectionFlagsConflict, => {}, // fall back to default behavior below - else => |e| return e, } } if (elf.base.comp.config.any_non_single_threaded and nav.resolved.?.@"threadlocal") { @@ -5294,15 +6021,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node .@"fn" => a: { const mod = zcu.navFileScope(nav_index).mod.?; const target = &mod.resolved_target.result; - const min = target_util.minFunctionAlignment(target); break :a .fromIp(switch (nav.resolved.?.@"align") { - else => |a| a.maxStrict(min), + else => |a| a.maxStrict(target_util.minFunctionAlignment(target)), .none => switch (mod.optimize_mode) { - .debug, - .safe, - .fast, - => target_util.defaultFunctionAlignment(target), - .small => min, + .debug, .safe, .fast => target_util.defaultFunctionAlignment(target), + .small => target_util.minFunctionAlignment(target), }.maxStrict(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)), }); }, @@ -5312,9 +6035,9 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node }, }; try shndx.ensureAligned(elf, alignment); - const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{ + const node = elf.addNodeAssumeCapacity(try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ .alignment = alignment, - }); + }), .{ .nav = nmi }); nav_gop.value_ptr.* = .{ .lsi = elf.addLocalSymbolAssumeCapacity(.{ .node = .wrap(node), @@ -5327,7 +6050,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node .first_symbol_reloc = .none, .first_got_reloc = .none, }; - elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); } return nmi; } @@ -5356,16 +6078,12 @@ fn uavMapIndex( if (!uav_gop.found_existing) { const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs try shndx.ensureAligned(elf, resolved_align); - const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{ + const node = elf.addNodeAssumeCapacity(try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ .moved = true, // see assert at end of `genUav` .alignment = resolved_align, - }); - var name_buf: [32]u8 = undefined; - const name = std.mem.print( - &name_buf, - "__anon_{d}", - .{@backingInt(uav_val)}, - ) catch unreachable; + }), .{ .uav = umi }); + var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined; + const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable; uav_gop.value_ptr.* = .{ .lsi = elf.addLocalSymbolAssumeCapacity(.{ .node = .wrap(node), @@ -5377,15 +6095,14 @@ fn uavMapIndex( }), .first_symbol_reloc = .none, }; - elf.nodes.appendAssumeCapacity(.{ .uav = umi }); elf.const_prog_node.increaseEstimatedTotalItems(1); elf.pending_uavs.appendAssumeCapacity(umi); } else { const node = uav_gop.value_ptr.lsi.index().ptr(elf).node.unwrap().?; - const shndx = elf.getNode(node.parent(&elf.mf).unwrap().?).section; + const shndx = elf.getNodeShndx(node); try shndx.ensureAligned(elf, resolved_align); if (resolved_align.order(node.alignment(&elf.mf)).compare(.gt)) { - try node.realign(&elf.mf, gpa, resolved_align); + try node.realign(gpa, &elf.mf, resolved_align); } } return umi; @@ -5518,9 +6235,9 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load return error.BadMagic; } } - var strtab: std.Io.Writer.Allocating = .init(gpa); + var strtab: Io.Writer.Allocating = .init(gpa); defer strtab.deinit(); - while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| { + while (r.takeStruct(std.elf.ar_hdr, .native)) |header| { if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG)) return diags.failParse(path, "bad file magic", .{}); const offset = fr.logicalPos(); @@ -5530,8 +6247,8 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load strtab.clearRetainingCapacity(); try strtab.ensureTotalCapacityPrecise(size); r.streamExact(&strtab.writer, size) catch |err| switch (err) { + else => |e| return e, error.WriteFailed => return error.OutOfMemory, - else => |e| return e, }; continue; } @@ -5562,14 +6279,14 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load } try fr.seekTo(std.mem.alignForward(u64, offset + size, 2)); } else |err| switch (err) { + else => |e| return e, error.EndOfStream => if (!fr.atEnd()) return error.EndOfStream, - else => |e| return e, } } fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) { return .{ .data = member }; } -fn memberStringEscape(member: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void { +fn memberStringEscape(member: ?[]const u8, w: *Io.Writer) Io.Writer.Error!void { try w.print("({f})", .{std.zig.fmtString(member orelse return)}); } fn loadObject( @@ -5614,11 +6331,13 @@ fn loadObject( }; try elf.nodes.ensureUnusedCapacity(gpa, 1); - const new_member_ni = try archive.ni.addFooterChildBefore(&elf.mf, gpa, first_member_oni, .{ - .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size), - .alignment = .@"2", - }); - elf.nodes.appendAssumeCapacity(.{ .archive_input_member = input_index }); + const new_member_ni = elf.addNodeAssumeCapacity( + try archive.ni.addFooterChildBefore(gpa, &elf.mf, first_member_oni, .{ + .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size), + .alignment = .@"2", + }), + .{ .archive_input_member = input_index }, + ); input.extra = .{ .node = new_member_ni }; elf.input_prog_node.increaseEstimatedTotalItems(1); @@ -5717,18 +6436,19 @@ fn loadObject( for (sections[1..]) |*section| { if (section.shdr.name >= shstrtab.len) continue; const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); + if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; const opts: struct { shndx: Section.Index, - has_file_bits: bool, node_fixed: bool, } = switch (section.shdr.type) { else => continue, - .PROGBITS, .NOBITS => opts: { + .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { const shndx = elf.mapInputSection(.{ .name = name, .flags = section.shdr.flags.shf, .entsize = section.shdr.entsize, }) catch |err| switch (err) { + else => |e| return e, error.StripSection => continue, error.TlsSectionUnavailable => return diags.failParse( path, @@ -5759,7 +6479,6 @@ fn loadObject( "flags of section '{s}' conflict with other inputs", .{name}, ), - else => |e| return e, }; if (section.shdr.flags.shf.COMPRESSED) { // SHF_COMPRESSED is only allowed on non-alloc sections. @@ -5776,7 +6495,6 @@ fn loadObject( } break :opts .{ .shndx = shndx, - .has_file_bits = section.shdr.type == .PROGBITS, // For well-known sections, we know that it's fine to have e.g. random // padding, so there's no need to make the sections fixed. For custom // sections, however, we do want fixed nodes to avoid padding. @@ -5819,7 +6537,6 @@ fn loadObject( } break :shndx shndx.*; }, - .has_file_bits = true, // This node must be fixed to prevent padding from being added between different // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. .node_fixed = true, @@ -5834,28 +6551,26 @@ fn loadObject( .alignment = need_align, .moved = true, // see assert at end of `flushInputSection` }; - const ni = if (opts.node_fixed) ni: { - const shndx_ni = opts.shndx.get(elf).ni; - const after_oni: MappedFile.Node.Index.Optional = after: { - const last_ni = shndx_ni.last(&elf.mf).unwrap() orelse break :after .none; - break :after switch (last_ni.position(&elf.mf)) { - .header => .wrap(last_ni), - .footer, .floating => .none, + const ni = elf.addNodeAssumeCapacity( + if (opts.node_fixed) ni: { + const shndx_ni = opts.shndx.get(elf).ni; + const after_oni: MappedFile.Node.Index.Optional = after: { + const last_ni = shndx_ni.last(&elf.mf).unwrap() orelse break :after .none; + break :after switch (last_ni.position(&elf.mf)) { + .header => .wrap(last_ni), + .footer, .floating => .none, + }; }; - }; - break :ni try shndx_ni.addHeaderChildAfter(&elf.mf, gpa, after_oni, add_node_opts); - } else ni: { - break :ni try opts.shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, add_node_opts); - }; - elf.nodes.appendAssumeCapacity(.{ - .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)), - }); + break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts); + } else try opts.shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, add_node_opts), + .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) }, + ); section.isi = @fromBackingInt(@intCast(elf.input_sections.items.len)); elf.input_sections.addOneAssumeCapacity().* = .{ .input = input_index, .file_location = .{ .offset = fl.offset + section.shdr.offset, - .size = if (opts.has_file_bits) section.shdr.size else 0, + .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size, }, // The section vaddr is initially 0, because the symbol addresses are // zero-based. This will eventually be updated by `flushMoved`. @@ -6043,6 +6758,7 @@ fn loadObject( rel.addend, rt, ) catch |err| switch (err) { + else => |e| return e, error.UnknownRelocation => diags.addParseError( path, "unknown relocation type '{f}'", @@ -6058,7 +6774,6 @@ fn loadObject( "TODO(Elf2): unimplemented relocation type '{f}'", .{rt.fmt(elf)}, ), - else => |e| return e, }; } }, @@ -6102,7 +6817,7 @@ fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_n // We set the size of the archive header node exactly, because we want padding bytes to go into // the root `.archive` node. That way, those bytes could still be used to grow the string table // if necessary, but they could also be used for new archive members. - try archive_header_ni.resizeLeaf(&elf.mf, gpa, old_archive_header_size + member_name.len + 2); + try archive_header_ni.resizeLeaf(gpa, &elf.mf, old_archive_header_size + member_name.len + 2); const dest_slice = archive_header_ni.slice(&elf.mf)[@intCast(old_archive_header_size)..]; @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name); @@ -6254,8 +6969,12 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars // We have a copy relocation for this global, but the amount of space we // reserved for it could be too small or underaligned! try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment); - try copied_global.node.resizeLeaf(&elf.mf, gpa, gop.value_ptr.alignment.forward(gop.value_ptr.size)); - try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment); + try copied_global.node.resizeLeaf( + gpa, + &elf.mf, + gop.value_ptr.alignment.forward(gop.value_ptr.size), + ); + try copied_global.node.realign(gpa, &elf.mf, gop.value_ptr.alignment); const global_ptr = elf.globalByName(name).?; switch (elf.symPtr(global_ptr.symtab_index)) { inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)), @@ -6407,6 +7126,7 @@ fn createInitFiniArraySection( .type = @"type", .flags = .{ .WRITE = true, .ALLOC = true }, .node_align = addr_align, + .manual_size = true, }); elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); try elf.ensureUnusedSymbolCapacity(2, .maybe_global); @@ -6454,8 +7174,9 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { fn prelinkInner(elf: *Elf) Error!void { const comp = elf.base.comp; const gpa = comp.gpa; + if (comp.zcu) |_| self_hosted_codegen: { + if (comp.config.use_llvm) break :self_hosted_codegen; - if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == .root) { // We're using self-hosted codegen---add an input representing the Zig "object". try elf.ensureUnusedSymbolCapacity(1, .all_local); try elf.inputs.ensureUnusedCapacity(gpa, 1); @@ -6475,9 +7196,228 @@ fn prelinkInner(elf: *Elf) Error!void { .extra = .{ .file_symbol = zcu_file_symbol }, }; elf.input_pending_index += 1; + + try elf.nodes.ensureUnusedCapacity(gpa, 5 + 4); + + switch (elf.shndx.debug_abbrev) { + .UNDEF => {}, + else => |debug_abbrev_shndx| elf.dwarf.debug_abbrev.ni = .wrap(elf.addNodeAssumeCapacity( + try debug_abbrev_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}), + .{ .debug_shared = .debug_abbrev }, + )), + } + switch (elf.shndx.debug_line_str) { + .UNDEF => {}, + else => |debug_line_str_shndx| elf.dwarf.debug_line_str.ni = + .wrap(elf.addNodeAssumeCapacity( + try debug_line_str_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}), + .{ .debug_shared = .debug_line_str }, + )), + } + switch (elf.shndx.debug_str) { + .UNDEF => {}, + else => |debug_str_shndx| elf.dwarf.debug_str.ni = .wrap(elf.addNodeAssumeCapacity( + try debug_str_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}), + .{ .debug_shared = .debug_str }, + )), + } + switch (elf.shndx.debug_str_offsets) { + .UNDEF => {}, + else => |debug_str_offsets_shndx| elf.dwarf.debug_str_offsets.ni = + .wrap(elf.addNodeAssumeCapacity( + try debug_str_offsets_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}), + .{ .debug_shared = .debug_str_offsets }, + )), + } + + for ([5]Section.Index{ + elf.shndx.eh_frame, + elf.shndx.debug_frame, + elf.shndx.debug_info, + elf.shndx.debug_line, + elf.shndx.debug_rnglists, + }) |debug_shndx| { + if (debug_shndx == .UNDEF) continue; + const debug_ni = debug_shndx.get(elf).ni; + const frame_format = debug_shndx.debugFrameFormat(elf); + const unit_padding_ni = elf.addNodeAssumeCapacity( + try debug_ni.addHeaderChildAfter(gpa, &elf.mf, last_header_oni: { + var last_header_oni = debug_ni.last(&elf.mf); + while (last_header_oni.unwrap()) |last_header_ni| + switch (last_header_ni.position(&elf.mf)) { + .header => break, + .footer => last_header_oni = last_header_ni.prev(&elf.mf), + .floating => unreachable, + }; + break :last_header_oni last_header_oni; + }, .{ + .alignment = if (frame_format) |_| switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"4", + .@"64" => .@"8", + } else .@"1", + .next_moved = true, + .enable_next_moved = true, + }), + .unit_padding, + ); + var debug_nw: MappedFile.Node.Writer = undefined; + unit_padding_ni.writer(gpa, &elf.mf, &debug_nw); + defer debug_nw.deinit(); + (if (frame_format) |format| + elf.dwarf.genDebugFrameCie(&debug_nw.interface, null, format) + else + elf.dwarf.genUnitPadding(&debug_nw.interface)) catch |err| switch (err) { + error.WriteFailed => return debug_nw.err.?, + }; + } } } +pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void { + elf.zcuFilesReadyInner(zcu) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), + }; +} +fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void { + const gpa = zcu.gpa; + const units_len = zcu.module_roots.count(); + if (elf.dwarf_units.len == 0) { + @branchHint(.unlikely); + try elf.dwarf.initUnits(gpa, units_len); + elf.dwarf_units = try gpa.alloc(dwarf_relocs.Unit, zcu.module_roots.count()); + @memset(elf.dwarf_units, .{ + .frame_cie_first_target_reloc = .none, + .debug_info_header_first_target_reloc = .none, + .debug_info_header_first_node_reloc = .none, + .debug_line_header_first_target_reloc = .none, + .debug_line_header_first_node_reloc = .none, + .debug_rnglists_first_target_reloc = .none, + .debug_rnglists_symbol_relocs = .empty, + }); + } + if (!try elf.dwarf.updateUnits(zcu)) return; + try elf.nodes.ensureUnusedCapacity(gpa, 5 * units_len); + for (0..units_len) |unit_index| { + const ui: Dwarf.Unit.Index = @fromBackingInt(@intCast(unit_index)); + const unit = ui.get(&elf.dwarf); + if (!unit.alive) continue; + switch (elf.shndx.debug_info) { + .UNDEF => {}, + else => |debug_info_shndx| { + const debug_info_ni = unit.debug_info_ni.unwrap() orelse debug_info_ni: { + const debug_info_ni = elf.addNodeAssumeCapacity( + try debug_info_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ + .alignment = elf.mf.flags.block_size, + .enable_next_moved = true, + }), + .{ .unit_debug_info = ui }, + ); + unit.debug_info_ni = .wrap(debug_info_ni); + break :debug_info_ni debug_info_ni; + }; + if (unit.debug_info_header_ni == .none) unit.debug_info_header_ni = .wrap( + elf.addNodeAssumeCapacity(try debug_info_ni.addOnlyHeaderChild(gpa, &elf.mf, .{ + .next_moved = true, + .enable_next_moved = true, + }), .{ .unit_debug_info_header = ui }), + ); + if (unit.debug_info_footer_ni == .none) unit.debug_info_footer_ni = .wrap( + elf.addNodeAssumeCapacity(try debug_info_ni.addOnlyFooterChild(gpa, &elf.mf, .{ + .size = comptime Dwarf.uleb128Size(@backingInt(Dwarf.AbbrevCode.null)) * 2, + }), .{ .unit_debug_info_footer = ui }), + ); + }, + } + switch (elf.shndx.debug_line) { + .UNDEF => {}, + else => |debug_line_shndx| { + const debug_line_ni = unit.debug_line_ni.unwrap() orelse debug_line_ni: { + const debug_line_ni = elf.addNodeAssumeCapacity( + try debug_line_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ + .alignment = elf.mf.flags.block_size, + .enable_next_moved = true, + }), + .{ .unit_debug_line = ui }, + ); + unit.debug_line_ni = .wrap(debug_line_ni); + break :debug_line_ni debug_line_ni; + }; + if (unit.debug_line_header_ni == .none) unit.debug_line_header_ni = .wrap( + elf.addNodeAssumeCapacity(try debug_line_ni.addOnlyHeaderChild(gpa, &elf.mf, .{ + // Idle tasks are going to try to keep this up to date before we are able to + // write out the full header, so just reserve space for them to do so. + .size = elf.dwarf.unitLengthSize(), + .enable_next_moved = true, + }), .{ .unit_debug_line_header = ui }), + ); + }, + } + switch (elf.shndx.debug_rnglists) { + .UNDEF => {}, + else => |debug_rnglists_shndx| { + const debug_rnglists_ni = unit.debug_rnglists_ni.unwrap() orelse debug_rnglists_ni: { + const debug_rnglists_ni = elf.addNodeAssumeCapacity( + try debug_rnglists_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ + .next_moved = true, + .enable_next_moved = true, + }), + .{ .unit_debug_rnglists = ui }, + ); + unit.debug_rnglists_ni = .wrap(debug_rnglists_ni); + break :debug_rnglists_ni debug_rnglists_ni; + }; + + var drh_nw: MappedFile.Node.Writer = undefined; + debug_rnglists_ni.writer(gpa, &elf.mf, &drh_nw); + defer drh_nw.deinit(); + elf.dwarf.genDebugRnglistsHeader(unit, &drh_nw) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return drh_nw.err.?, + }; + }, + } + } + for (0..units_len) |unit_index| { + const ui: Dwarf.Unit.Index = @fromBackingInt(@intCast(unit_index)); + const unit = ui.get(&elf.dwarf); + if (unit.debug_info_header_ni == .none) continue; + var dih_nw: MappedFile.Node.Writer = undefined; + const debug_info_header_ni = unit.debug_info_header_ni.unwrap().?; + debug_info_header_ni.writer(gpa, &elf.mf, &dih_nw); + defer dih_nw.deinit(); + elf.resetNodeRelocs(debug_info_header_ni); + elf.dwarf.genDebugInfoHeader(zcu, ui.mod(&elf.dwarf), unit, &dih_nw) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dih_nw.err.?, + }; + } +} + +fn flushFiles(elf: *Elf) Error!void { + const gpa = elf.base.comp.gpa; + if (elf.shndx.debug_line != .UNDEF) for (elf.dwarf.units) |*unit| { + if (!unit.cleanDebugLineHeaderChanged()) continue; + assert(unit.alive); + const debug_line_header_ni = unit.debug_line_header_ni.unwrap().?; + try debug_line_header_ni.parent(&elf.mf).unwrap().?.nextMoved(gpa, &elf.mf); + try debug_line_header_ni.moved(gpa, &elf.mf); + try debug_line_header_ni.nextMoved(gpa, &elf.mf); + var dlh_nw: MappedFile.Node.Writer = undefined; + debug_line_header_ni.writer(gpa, &elf.mf, &dlh_nw); + defer dlh_nw.deinit(); + elf.resetNodeRelocs(debug_line_header_ni); + elf.dwarf.genDebugLineHeader(unit, &dlh_nw, elf.base.comp.zcu.?) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dlh_nw.err.?, + }; + }; +} + fn prepareDynamic(elf: *Elf) Error!void { const comp = elf.base.comp; @@ -6500,7 +7440,7 @@ fn prepareDynamic(elf: *Elf) Error!void { const dynamic_size = dynamic_len * 2 * elf.targetPtrSize(); - try elf.shndx.dynamic.get(elf).ni.resizeLeaf(&elf.mf, comp.gpa, dynamic_size); + try elf.shndx.dynamic.get(elf).ni.resizeLeaf(comp.gpa, &elf.mf, dynamic_size); switch (elf.shdrPtr(elf.shndx.dynamic)) { inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)), } @@ -6610,7 +7550,7 @@ fn flushDynamic(elf: *Elf) void { dynamic_index += 9; assert(dynamic_index == dynamic_entries.len); - if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| + if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry| std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); }, } @@ -6626,6 +7566,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { addralign: Alignment = .@"1", entsize: std.elf.Word = 0, node_align: Alignment = .@"1", + manual_size: bool = false, }) Error!Section.Index { switch (opts.type) { .NULL => assert(opts.size == 0), @@ -6639,7 +7580,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { const gpa = elf.base.comp.gpa; try elf.nodes.ensureUnusedCapacity(gpa, 1); try elf.shdrs.ensureUnusedCapacity(gpa, 1); - if (opts.flags.ALLOC) try elf.ensureUnusedSymbolCapacity(1, .all_local); + const want_symbol = opts.flags.ALLOC or switch (opts.type) { + .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL, + else => false, + }; + if (want_symbol) try elf.ensureUnusedSymbolCapacity(1, .all_local); const shstrtab_entry = try elf.string(.shstrtab, opts.name); const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) { @@ -6669,32 +7614,38 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { break :shndx .{ @fromBackingInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) }; }, }; - try elf.ni.shdr.ensureMinimumSize(&elf.mf, gpa, new_shdr_size); + try elf.ni.shdr.ensureMinimumSize(gpa, &elf.mf, new_shdr_size); const parent_ni = switch (elf.ehdrType()) { .REL => elf.ni.elf, .EXEC, .DYN => segment_ni, }; assert(opts.addralign.check(opts.size)); - const ni = try parent_ni.addFloatingChild(&elf.mf, gpa, .{ + const ni = elf.addNodeAssumeCapacity(try parent_ni.addFloatingChild(gpa, &elf.mf, .{ .size = opts.node_align.forward(opts.size), .alignment = opts.addralign.max(opts.node_align), .resized = opts.size > 0, + .bubbles_moved = opts.flags.ALLOC, + }), switch (opts.manual_size) { + false => .{ .section = shndx }, + true => .{ .section_manual_size = shndx }, }); const addr = elf.computeNodeVAddr(ni); - const lsi: Symbol.LocalIndex = if (opts.flags.ALLOC) elf.addLocalSymbolAssumeCapacity(.{ - .node = .wrap(ni), - .name = .empty, - .value = addr, - .size = 0, - .type = .SECTION, - .shndx = shndx, - }) else .null; - elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela = switch (opts.type) { - .REL => unreachable, - .RELA => .{ .free_head = .none }, - else => .{ .shndx = .UNDEF }, - } }); - elf.nodes.appendAssumeCapacity(.{ .section = shndx }); + elf.shdrs.appendAssumeCapacity(.{ + .lsi = if (want_symbol) elf.addLocalSymbolAssumeCapacity(.{ + .node = ni.toOptional(), + .name = .empty, + .value = addr, + .size = 0, + .type = .SECTION, + .shndx = shndx, + }) else .null, + .ni = ni, + .rela = switch (opts.type) { + .REL => unreachable, + .RELA => .{ .free_head = .none }, + else => .{ .shndx = .UNDEF }, + }, + }); switch (elf.shdrPtr(shndx)) { inline else => |shdr, class| { shdr.* = .{ @@ -6702,14 +7653,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { .type = opts.type, .flags = .{ .shf = opts.flags }, .addr = @intCast(addr), - .offset = @intCast(elf.getNodeElfOffset(ni)), + .offset = @intCast(elf.computeNodeElfOffset(ni)), .size = @intCast(opts.size), .link = opts.link, .info = opts.info, .addralign = @intCast(opts.addralign.toByteUnits()), .entsize = opts.entsize, }; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr); + if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr); }, } return shndx; @@ -6719,6 +7670,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) if (len == 0) return; const gpa = elf.base.comp.gpa; try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); + try elf.node_relocs.ensureUnusedCapacity(gpa, len); try elf.got_relocs.ensureUnusedCapacity(gpa, len); const class = elf.identClass(); switch (elf.ehdrType()) { @@ -6749,6 +7701,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), }, .node_align = elf.mf.flags.block_size, + .manual_size = true, }); elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {}); shndx.get(elf).rela.shndx = rela_shndx; @@ -6763,7 +7716,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) .NONE, _ => unreachable, inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr), }; - try elf.shndx.got.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_got_size); + try elf.shndx.got.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_got_size); if (elf.shndx.dynamic != .UNDEF) { try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries); @@ -6795,17 +7748,12 @@ fn addRelocAssumeCapacity( .addend = addend, }); const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); - const next: SymbolReloc.Index = next: { - const target_ptr = target.index(elf).ptr(elf); - const next = target_ptr.first_target_reloc; - target_ptr.first_target_reloc = ri; - break :next next; - }; - if (next != .none) { - next.get(elf).prev = ri; - } + const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; + const next = first_target_reloc.*; + first_target_reloc.* = ri; + if (next != .none) next.get(elf).prev = ri; elf.symbol_relocs.appendAssumeCapacity(.{ - .node = node, + .node = node.toOptional(), .offset = offset, .type = undefined, .target = target, @@ -6816,7 +7764,6 @@ fn addRelocAssumeCapacity( .result = .ok, }); }, - .DYN, .EXEC => switch (elf.ehdrMachine()) { .AARCH64 => switch (@"type".AARCH64) { .NONE => {}, @@ -7239,14 +8186,12 @@ fn addSymbolRelocAssumeCapacity( }; const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); - const target_ptr = target.index(elf).ptr(elf); - const next = target_ptr.first_target_reloc; - target_ptr.first_target_reloc = ri; - if (next != .none) { - next.get(elf).prev = ri; - } + const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; + const next = first_target_reloc.*; + first_target_reloc.* = ri; + if (next != .none) next.get(elf).prev = ri; elf.symbol_relocs.appendAssumeCapacity(.{ - .node = node, + .node = node.toOptional(), .offset = offset, .target = target, .addend = addend, @@ -7263,6 +8208,103 @@ fn addSymbolRelocAssumeCapacity( // Actually apply the new relocation! ri.get(elf).apply(elf); } +fn addNodeRelocAssumeCapacity( + elf: *Elf, + node: MappedFile.Node.Index, + offset: u64, + target: MappedFile.Node.Index, + addend: i64, + @"type": NodeReloc.Type, +) Error!void { + const shndx = elf.getNodeShndx(target); + assert(!shndx.flags(elf).ALLOC); // not yet needed so not implemented + const first_target_reloc = switch (elf.getNode(target)) { + else => unreachable, + .debug_shared => |ss| &elf.dwarf_shared.getPtr(ss).first_target_reloc, + .unit_frame_cie => |ui| &elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc, + .unit_debug_info_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_target_reloc, + .unit_debug_line_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_line_header_first_target_reloc, + .unit_debug_rnglists => |ui| &elf.dwarf_units[@backingInt(ui)].debug_rnglists_first_target_reloc, + .const_debug_info => |cpi| &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_target_reloc, + .global_debug_info => |gi| &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_target_reloc, + .func_debug_info => |fi| &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_target_reloc, + .decl_debug_info => |di| &elf.dwarf_decls.getPtr(di).?.debug_info_first_target_reloc, + }; + const next = first_target_reloc.*; + const ri: NodeReloc.Index = @fromBackingInt(@intCast(elf.node_relocs.items.len)); + first_target_reloc.* = ri; + if (next != .none) next.get(elf).prev = ri; + switch (elf.ehdrType()) { + .REL => { + const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; + const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ + .type = switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = switch (@"type") { + .abs32 => .ABS32, + .abs64 => .ABS64, + } }, + .LOONGARCH => .{ .LARCH = switch (@"type") { + .abs32 => .@"32", + .abs64 => .@"64", + } }, + .PPC64 => .{ .PPC64 = switch (@"type") { + .abs32 => .ADDR32, + .abs64 => .ADDR64, + } }, + .RISCV => .{ .RISCV = switch (@"type") { + .abs32 => .@"32", + .abs64 => .@"64", + } }, + .SPARCV9 => .{ .SPARC = switch (@"type") { + .abs32 => .UA32, + .abs64 => .UA64, + } }, + .X86_64 => .{ .X86_64 = switch (@"type") { + .abs32 => .@"32", + .abs64 => .@"64", + } }, + }, + // This field needs to equal the offset into the section, which is *not* necessarily + // the same thing as our `offset`, which is the offset into `node`. We could compute + // the section offset now, but there's no point, because `flushMovedNodeRelocs` will + // eventually do it for us anyway, so just init to 0. + .offset = 0, + .raw_sym_index = @backingInt(switch (shndx.get(elf).lsi) { + .null => unreachable, + else => |lsi| lsi.index(), + }), + .addend = 0, + }); + elf.node_relocs.appendAssumeCapacity(.{ + .node = node.toOptional(), + .offset = offset, + .type = undefined, + .target = target, + .addend = addend, + .next = next, + .prev = .none, + .rela_index = rela_index.toOptional(), + .result = .ok, + }); + }, + .DYN, .EXEC => { + elf.node_relocs.appendAssumeCapacity(.{ + .node = node.toOptional(), + .offset = offset, + .target = target, + .addend = addend, + .type = @"type", + .next = next, + .prev = .none, + .rela_index = .none, + .result = .ok, + }); + + // Actually apply the new relocation! + ri.get(elf).apply(elf); + }, + } +} fn addGotRelocAssumeCapacity( elf: *Elf, node: MappedFile.Node.Index, @@ -7273,6 +8315,7 @@ fn addGotRelocAssumeCapacity( ) void { assert(elf.ehdrType() != .REL); switch (elf.getNode(node)) { + .deleted, .archive, .archive_header, .archive_input_member, @@ -7282,8 +8325,26 @@ fn addGotRelocAssumeCapacity( .shdr, .segment, .copied_global, + .debug_shared, + .eh_frame_footer, + .unit_padding, + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, => unreachable, // cannot contain relocs, .section, + .section_manual_size, .uav, => unreachable, // cannot contain GOT relocs .input_section, @@ -7519,14 +8580,13 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool { try Section.Index.data.ensureAligned(elf, dso_global.alignment); try elf.nodes.ensureUnusedCapacity(gpa, 1); - const node = try Section.Index.data.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{ + const node = try Section.Index.data.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ .size = dso_global.alignment.forward(dso_global.size), .alignment = dso_global.alignment, }); errdefer comptime unreachable; const vaddr = elf.computeNodeVAddr(node); - elf.nodes.appendAssumeCapacity(.{ .copied_global = global_name }); const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ .type = .copy(elf), .offset = vaddr, @@ -7555,10 +8615,12 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool { } pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { - const diags = &elf.base.comp.link_diags; elf.updateNavInner(pt, nav_index) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), }; } fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { @@ -7568,11 +8630,14 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) const nav = ip.getNav(nav_index); if (ip.indexToKey(nav.resolved.?.value) == .@"extern") return; - if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) return; + if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) { + if (elf.ehdrMachine() != .X86_64) return; + const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?; + return if (!mod.strip) elf.dwarf.updateComptimeNav(pt, nav_index); + } const nmi = try elf.navMapIndex(zcu, nav_index); const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be // called to apply the NAV's new relocations. @@ -7580,8 +8645,9 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) { var nw: MappedFile.Node.Writer = undefined; - ni.writer(&elf.mf, gpa, &nw); + ni.writer(gpa, &elf.mf, &nw); defer nw.deinit(); + elf.resetNodeRelocs(ni); codegen.generateSymbol( &elf.base, pt, @@ -7589,8 +8655,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) &nw.interface, .{ .atom_index = Node.toAtom(ni) }, ) catch |err| switch (err) { + else => |e| return e, error.WriteFailed => return nw.err.?, - else => |e| return e, }; switch (elf.symPtr(nmi.symbol(elf).index())) { inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), @@ -7599,6 +8665,143 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs. try elf.genPending(pt); + try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf }); +} + +pub fn updateContainerType( + elf: *Elf, + pt: Zcu.PerThread, + ty: InternPool.Index, + success: bool, +) link.Error!void { + elf.updateContainerTypeInner(pt, ty, success) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), + }; +} +pub fn updateContainerTypeInner( + elf: *Elf, + pt: Zcu.PerThread, + ty: InternPool.Index, + success: bool, +) Error!void { + switch (elf.base.comp.config.debug_format) { + .strip => {}, + .dwarf => { + try elf.dwarf.const_pool.updateContainerType(pt, .{ .elf2 = elf }, ty, success); + try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf }); + }, + .code_view => unreachable, + } + if (!success) return; + var lazy_it = elf.lazy.iterator(); + while (lazy_it.next()) |lazy| if (lazy.value.map.getIndex(ty)) |lmi| { + if (lazy.value.pending_index <= lmi) continue; + // This type has changed on this incremental update, so update the lazy code/data. + try elf.genLazy(pt, .{ .kind = lazy.key, .index = @intCast(lmi) }); + }; +} + +pub fn addConst( + elf: *Elf, + _: Zcu.PerThread, + cpi: link.ConstPool.Index, + val: InternPool.Index, +) link.Error!void { + switch (elf.base.comp.config.debug_format) { + .strip => {}, + .dwarf => { + const gpa = elf.base.comp.gpa; + try elf.nodes.ensureUnusedCapacity(gpa, 1); + try elf.dwarf.consts.ensureUnusedCapacity(gpa, 1); + try elf.dwarf_consts.ensureUnusedCapacity(gpa, 1); + try elf.dwarf.addConst(cpi, val, &addConstNode); + }, + .code_view => unreachable, + } +} +fn addConstNode(lf: *link.File, ui: Dwarf.Unit.Index, cpi: link.ConstPool.Index) link.Error!MappedFile.Node.Index { + const elf = lf.cast(.elf2).?; + const unit = ui.get(&elf.dwarf); + const debug_info_ni = elf.addNodeAssumeCapacity( + unit.debug_info_ni.unwrap().?.addFloatingChild(lf.comp.gpa, &elf.mf, .{ + .enable_next_moved = true, + }) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return lf.comp.link_diags.fail("failed to write output file: {t}", .{ + elf.mf.io_err.?, + }), + }, + .{ .const_debug_info = cpi }, + ); + elf.dwarf_consts.putAssumeCapacityNoClobber(cpi, .{ + .debug_info_first_target_reloc = .none, + .debug_info_first_symbol_reloc = .none, + .debug_info_first_node_reloc = .none, + }); + return debug_info_ni; +} + +pub fn updateConst( + elf: *Elf, + pt: Zcu.PerThread, + cpi: link.ConstPool.Index, + val: InternPool.Index, +) link.Error!void { + switch (val) { + .anyerror_type => {}, // handled in `updateErrorData` instead + else => try elf.updateConstInner(pt, cpi, val, .complete), + } +} +fn updateConstInner( + elf: *Elf, + pt: Zcu.PerThread, + cpi: link.ConstPool.Index, + val: InternPool.Index, + complete: enum { incomplete, complete }, +) link.Error!void { + switch (elf.base.comp.config.debug_format) { + .strip => {}, + .dwarf => { + { + switch (pt.zcu.intern_pool.indexToKey(val)) { + else => {}, + .func => |func| { + const fi = try elf.dwarf.getFunc(func.owner_nav); + switch (fi.get(&elf.dwarf).state) { + .unresolved => {}, + .resolved => return, + } + }, + } + const gpa = elf.base.comp.gpa; + const debug_info_ni = Dwarf.Const.get(cpi, &elf.dwarf).debug_info_ni.unwrap().?; + try debug_info_ni.moved(gpa, &elf.mf); + var di_nw: MappedFile.Node.Writer = undefined; + debug_info_ni.writer(gpa, &elf.mf, &di_nw); + defer di_nw.deinit(); + elf.resetNodeRelocs(debug_info_ni); + switch (complete) { + .incomplete => try elf.dwarf.updateConstIncomplete(pt, &di_nw, val), + .complete => try elf.dwarf.updateConst(pt, &di_nw, val), + } + } + try elf.genPending(pt); + }, + .code_view => unreachable, + } +} + +pub fn updateConstIncomplete( + elf: *Elf, + pt: Zcu.PerThread, + cpi: link.ConstPool.Index, + val: InternPool.Index, +) link.Error!void { + return elf.updateConstInner(pt, cpi, val, .incomplete); } pub fn updateFunc( @@ -7607,10 +8810,12 @@ pub fn updateFunc( func_index: InternPool.Index, mir: *const codegen.AnyMir, ) link.Error!void { - const diags = &elf.base.comp.link_diags; elf.updateFuncInner(pt, func_index, mir) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), }; } fn updateFuncInner( @@ -7627,8 +8832,8 @@ fn updateFuncInner( const nmi = try elf.navMapIndex(zcu, func.owner_nav); log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) }); - const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); + const lsi = nmi.symbol(elf); + const ni = lsi.index().ptr(elf).node.unwrap().?; // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be // called to apply the NAV's new relocations. @@ -7636,8 +8841,145 @@ fn updateFuncInner( { var nw: MappedFile.Node.Writer = undefined; - ni.writer(&elf.mf, gpa, &nw); + ni.writer(gpa, &elf.mf, &nw); defer nw.deinit(); + var debug_output_buf: Dwarf.WipNav.Debug = undefined; + const debug_output: link.File.DebugInfoOutput, const dwarf_func = debug_output: { + if (elf.ehdrMachine() != .X86_64) break :debug_output .{ .none, undefined }; + const dwarf = &elf.dwarf; + const src_inst = nav.srcInst(ip); + const mod = zcu.fileByIndex(src_inst.resolveFile(ip)).mod.?; + if (mod.strip and mod.unwind_tables == .none) break :debug_output .{ .none, undefined }; + + try elf.nodes.ensureUnusedCapacity(gpa, 4); + const dwarf_fi = try dwarf.getFunc(func.owner_nav); + + const wip_nav = &debug_output_buf.wip_nav; + wip_nav.* = .{ + .dwarf = dwarf, + .unit = dwarf.getUnit(mod), + .func = func_index, + .func_si = Symbol.Id.local(lsi).toTypeErased(), + .cfi = .{ + .loc = 0, + .cfa = dwarf.frame.header.initial_instructions[0].def_cfa, + }, + .frame_format = switch (mod.unwind_tables) { + .none => .debug_frame, + .sync, .async => .eh_frame, + }, + .fde_writer = undefined, + .frame_func_length = undefined, + }; + const unit = wip_nav.unit.get(dwarf); + + const frame_align: Alignment = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"4", + .@"64" => .@"8", + }; + const frame_ni = unit.frame_ni.unwrap() orelse frame_ni: { + const frame_ni = elf.addNodeAssumeCapacity(try switch (wip_nav.frame_format) { + .debug_frame => elf.shndx.debug_frame, + .eh_frame => elf.shndx.eh_frame, + }.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{ + .alignment = frame_align.max(elf.mf.flags.block_size), + .enable_next_moved = true, + }), .{ .unit_frame = wip_nav.unit }); + unit.frame_ni = .wrap(frame_ni); + break :frame_ni frame_ni; + }; + if (unit.cie_ni == .none) { + const cie_ni = elf.addNodeAssumeCapacity( + try frame_ni.addOnlyHeaderChild(gpa, &elf.mf, .{ + .alignment = frame_align, + .next_moved = true, + .enable_next_moved = true, + }), + .{ .unit_frame_cie = wip_nav.unit }, + ); + unit.cie_ni = .wrap(cie_ni); + var cie_nw: MappedFile.Node.Writer = undefined; + cie_ni.writer(gpa, &elf.mf, &cie_nw); + defer cie_nw.deinit(); + dwarf.genDebugFrameCie(&cie_nw.interface, switch (elf.ehdrMachine()) { + else => unreachable, + .X86_64 => .x86_64, + }, wip_nav.frame_format) catch |err| switch (err) { + error.WriteFailed => return cie_nw.err.?, + }; + } + const dwarf_func = dwarf_fi.get(dwarf); + const fde_ni = if (dwarf_func.fde_ni.unwrap()) |fde_ni| fde_ni: { + try fde_ni.moved(gpa, &elf.mf); + try fde_ni.nextMoved(gpa, &elf.mf); + break :fde_ni fde_ni; + } else fde_ni: { + const fde_ni = elf.addNodeAssumeCapacity(try frame_ni.addFloatingChild(gpa, &elf.mf, .{ + .alignment = frame_align, + .moved = true, + .next_moved = true, + .enable_next_moved = true, + }), .{ .func_frame_fde = dwarf_fi }); + dwarf_func.fde_ni = .wrap(fde_ni); + break :fde_ni fde_ni; + }; + fde_ni.writer(gpa, &elf.mf, &wip_nav.fde_writer); + + if (mod.strip) break :debug_output .{ .{ .eh_frame = wip_nav }, dwarf_func }; + + const debug = &debug_output_buf; + debug.pt = pt; + debug.any_children = false; + debug.blocks = .empty; + dwarf_func.state = .resolved; + + const debug_info_ni = dwarf_func.debug_info_ni.unwrap().?; + try dwarf.decls.put(zcu.comp.gpa, src_inst, .{ + .debug_info_ni = debug_info_ni.toOptional(), + }); + try debug_info_ni.moved(gpa, &elf.mf); + try debug_info_ni.nextMoved(gpa, &elf.mf); + debug_info_ni.writer(gpa, &elf.mf, &debug.info_writer); + + const debug_line_ni = dwarf_func.debug_line_ni.unwrap() orelse debug_line_ni: { + const debug_line_ni = elf.addNodeAssumeCapacity( + try unit.debug_line_ni.unwrap().?.addFloatingChild(gpa, &elf.mf, .{ + .moved = true, + .next_moved = true, + .enable_next_moved = true, + }), + .{ .func_debug_line = dwarf_fi }, + ); + dwarf_func.debug_line_ni = .wrap(debug_line_ni); + break :debug_line_ni debug_line_ni; + }; + debug_line_ni.writer(gpa, &elf.mf, &debug.line_writer); + + break :debug_output .{ .{ .dwarf2 = debug }, dwarf_func }; + }; + defer switch (debug_output) { + .dwarf => unreachable, + inline .eh_frame, .dwarf2 => |dwarf| dwarf.deinit(), + .none => {}, + }; + switch (debug_output) { + .dwarf => unreachable, + .eh_frame => |wip_nav| { + elf.resetNodeRelocs(dwarf_func.fde_ni.unwrap().?); + try wip_nav.genDebugFrameHeader(); + }, + .dwarf2 => |debug| { + elf.resetNodeRelocs(dwarf_func.fde_ni.unwrap().?); + try debug.wip_nav.genDebugFrameHeader(); + elf.resetNodeRelocs(dwarf_func.debug_line_ni.unwrap().?); + try debug.startDebugLine(); + elf.resetNodeRelocs(dwarf_func.debug_info_ni.unwrap().?); + try debug.startFuncDebugInfo(); + }, + .none => {}, + } + elf.resetNodeRelocs(ni); codegen.emitFunction( &elf.base, pt, @@ -7645,29 +8987,101 @@ fn updateFuncInner( Node.toAtom(ni), mir, &nw.interface, - .none, + debug_output, ) catch |err| switch (err) { - error.WriteFailed => return nw.err.?, else => |e| return e, + error.WriteFailed => if (nw.err) |e| return e, }; + const func_length = nw.interface.end; switch (elf.symPtr(nmi.symbol(elf).index())) { - inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), + inline else => |sym| elf.targetStore(&sym.size, @intCast(func_length)), + } + switch (debug_output) { + .dwarf => unreachable, + .eh_frame => |wip_nav| wip_nav.finishDebugFrameFde(func_length), + .dwarf2 => |debug| { + try debug.finishFunc(func_length); + const unit = debug.wip_nav.unit.get(debug.wip_nav.dwarf); + { + var dr_nw: MappedFile.Node.Writer = undefined; + unit.debug_rnglists_ni.unwrap().?.writer(gpa, &elf.mf, &dr_nw); + defer dr_nw.deinit(); + const first_symbol_reloc = elf.symbol_relocs.items.len; + debug.wip_nav.dwarf.genDebugRnglists( + unit, + &dr_nw, + debug.wip_nav.func_si, + func_length, + ) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => return dr_nw.err.?, + }; + const symbol_relocs = &elf.dwarf_units[@backingInt(debug.wip_nav.unit)] + .debug_rnglists_symbol_relocs; + try symbol_relocs.ensureUnusedCapacity(gpa, elf.symbol_relocs.items.len - + first_symbol_reloc); + for (first_symbol_reloc..elf.symbol_relocs.items.len) |symbol_ri| + symbol_relocs.putAssumeCapacityNoClobber( + @fromBackingInt(@intCast(symbol_ri)), + {}, + ); + } + debug.wip_nav.finishDebugFrameFde(func_length); + if (func.analysisUnordered(ip).inferred_error_set) { + const ies = ip.getIfExists(.{ .inferred_error_set_type = func_index }).?; + if (elf.dwarf.const_pool.getIfExists(ies)) |cpi| + try elf.updateConstInner(pt, cpi, ies, .complete); + } + }, + .none => {}, } } // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs. try elf.genPending(pt); + try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf }); +} + +pub fn updateLineNumber( + elf: *Elf, + _: Zcu.PerThread, + inst: InternPool.TrackedInst.Index, + line: u32, +) void { + elf.dwarf.updateLineNumber(&elf.mf, inst, line); +} + +pub fn lostTracking( + elf: *Elf, + _: Zcu.PerThread, + inst: InternPool.TrackedInst.Index, +) link.Error!void { + const di = elf.dwarf.getDeclIfExists(inst) orelse return; + const decl_ni = di.get(&elf.dwarf).debug_info_ni.unwrap() orelse return; + const comp = elf.base.comp; + var di_nw: MappedFile.Node.Writer = undefined; + decl_ni.writer(comp.gpa, &elf.mf, &di_nw); + defer di_nw.deinit(); + elf.resetNodeRelocs(decl_ni); + elf.dwarf.lostTracking(&di_nw) catch |err| switch (err) { + else => |e| return e, + error.WriteFailed => unreachable, + }; + decl_ni.resizeLeaf(comp.gpa, &elf.mf, di_nw.interface.end) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{ + elf.mf.io_err.?, + }), + }; } pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void { - const diags = &elf.base.comp.link_diags; - elf.genLazy(pt, .{ + if (elf.lazy.getPtr(.const_data).map.getIndex(.anyerror_type)) |lmi| try elf.genLazyInner(pt, .{ .kind = .const_data, - .index = @intCast(elf.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return), - }) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), - else => |e| return e, - }; + .index = @intCast(lmi), + }); + if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi| + try elf.updateConstInner(pt, cpi, .anyerror_type, .complete); } pub fn flush( @@ -7677,8 +9091,11 @@ pub fn flush( prog_node: std.Progress.Node, ) link.Error!void { elf.flushInner(arena, tid, prog_node) catch |err| switch (err) { - error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), }; } fn flushInner( @@ -7694,6 +9111,8 @@ fn flushInner( const sub_prog_node = prog_node.start("ELF Flush", 0); defer sub_prog_node.end(); + try elf.flushFiles(); + if (comp.config.output_mode == .Exe) { var any_undef = false; for (elf.globals.strong_undef.keys()) |name| { @@ -7708,8 +9127,13 @@ fn flushInner( while (try elf.idle(tid)) {} + assert(elf.pending_uavs.items.len == 0); + assert(elf.dwarf.const_pool.pending.items.len == 0); + // We've done the final `idle` loop, so everything is at its final place in the file. We have a // few more things to check and write now that addresses and offsets are finalized. + elf.mf.nodes_lock.lock(); + defer elf.mf.nodes_lock.unlock(); if (elf.overflowed_reloc_count > 0) { diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count}); @@ -7753,20 +9177,19 @@ fn flushInner( if (elf.options.enable_link_snapshots) elf.dumpStderr(tid) catch |err| - return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err}); + return diags.fail("dumping link snapshot failed: {t}", .{err}); } pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { - const comp = elf.base.comp; - const diags = &comp.link_diags; - + // This function is called non-deterministically, and so must not affect the layout of any nodes. elf.mf.nodes_lock.lock(); defer elf.mf.nodes_lock.unlock(); + const comp = elf.base.comp; + const diags = &comp.link_diags; + assert(elf.pending_uavs.items.len == 0); - for (&elf.lazy.values) |*lazy| { - assert(lazy.pending_index == lazy.map.count()); - } + assert(elf.dwarf.const_pool.pending.items.len == 0); task: { if (elf.input_pending_index < elf.inputs.items.len) { @@ -7775,8 +9198,8 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(ii.node(elf))); defer sub_prog_node.end(); elf.flushInput(ii) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; break :task; } @@ -7786,8 +9209,8 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf))); defer sub_prog_node.end(); elf.flushInputSection(isi) catch |err| switch (err) { - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; break :task; } @@ -7885,18 +9308,18 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { break :task; } - while (elf.mf.updates.pop()) |ni| { + while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) { + if (ni.pendingDelete(&elf.mf)) continue; const clean_moved = ni.cleanMoved(&elf.mf); const clean_resized = ni.cleanResized(&elf.mf); const clean_next_moved = ni.cleanNextMoved(&elf.mf); - if (clean_moved or clean_resized or clean_next_moved) { - const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni)); - defer sub_prog_node.end(); - if (clean_moved) try elf.flushMoved(ni); - if (clean_resized) try elf.flushResized(ni); - if (clean_next_moved) try elf.flushNextMoved(ni); - break :task; - } else elf.mf.update_prog_node.completeOne(); + if (!clean_moved and !clean_resized and !clean_next_moved) continue; + const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni)); + defer sub_prog_node.end(); + if (clean_moved) try elf.flushMoved(ni); + if (clean_resized) try elf.flushResized(ni); + if (clean_moved or clean_resized or clean_next_moved) try elf.flushPadding(ni); + break :task; } } if (elf.input_sections.items.len > elf.input_section_pending_index) return true; @@ -7915,62 +9338,119 @@ fn idleProgNode( var name: [std.Progress.Node.max_name_len]u8 = undefined; return prog_node.start(name: switch (node) { else => |tag| @tagName(tag), - .section => |shndx| shndx.name(elf).slice(elf), .archive_input_member => |ii| std.mem.print(&name, "{f}{f}", .{ ii.path(elf).fmtEscapeString(), fmtMemberString(ii.member(elf)), }) catch &name, + .section, .section_manual_size => |shndx| shndx.name(elf).slice(elf), .input_section => |isi| { const ii = isi.input(elf); break :name std.mem.print(&name, "{f}{f} {s}", .{ ii.path(elf).fmtEscapeString(), fmtMemberString(ii.member(elf)), - elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf), + elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), }) catch &name; }, .nav => |nmi| { const ip = &elf.base.comp.zcu.?.intern_pool; - break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip); + break :name ip.getNav(nmi.nav(elf)).fqn.toSlice(ip); }, .uav => |umi| std.mem.print(&name, "{f}", .{ Value.fromInterned(umi.uavValue(elf)).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }), }) catch &name, + .debug_shared => |ss| switch (ss) { + .debug_abbrev => "debug info abbrevs", + .debug_str, .debug_str_offsets => "debug info strings", + .debug_line_str => "line info strings", + }, + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line, + .unit_debug_line_header, + .unit_debug_rnglists, + => |ui, tag| std.mem.print(&name, "{s} info for {s}", .{ + switch (tag) { + else => unreachable, + .unit_frame, .unit_frame_cie => "unwind", + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_rnglists, + => "debug", + .unit_debug_line, .unit_debug_line_header => "line", + }, + ui.mod(&elf.dwarf).fully_qualified_name, + }) catch &name, + .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) { + .generic_poison_type => "anytype", + else => |val| std.mem.print(&name, "debug info for {f}", .{ + Value.fromInterned(val).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }), + }) catch &name, + }, + .global_debug_info => |gi| { + const ip = &elf.base.comp.zcu.?.intern_pool; + break :name std.mem.print(&name, "debug info for {f}", .{ + ip.getNav(gi.nav(&elf.dwarf)).fqn.fmt(ip), + }) catch &name; + }, + .func_frame_fde, .func_debug_info, .func_debug_line => |fi, tag| { + const ip = &elf.base.comp.zcu.?.intern_pool; + break :name std.mem.print(&name, "{s} info for {f}", .{ + switch (tag) { + else => unreachable, + .func_frame_fde => "unwind", + .func_debug_info => "debug", + .func_debug_line => "line", + }, + ip.getNav(fi.nav(&elf.dwarf)).fqn.fmt(ip), + }) catch &name; + }, + .decl_debug_info => |di| { + const comp = elf.base.comp; + const zcu = comp.zcu.?; + break :name std.mem.print(&name, "debug info for {f}", .{ + zcu.fileByIndex(di.srcInst(&elf.dwarf).resolveFile(&zcu.intern_pool)).path.fmt(comp), + }) catch &name; + }, }, 0); } -fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void { - const zcu = elf.base.comp.zcu.?; - pending: while (true) { - if (elf.pending_uavs.pop()) |umi| { - var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined; - const prog_name = std.mem.print(&prog_name_buf, "{f}", .{ - Value.fromInterned(umi.uavValue(elf)).fmtValue(pt), - }) catch &prog_name_buf; - const prog_node = elf.const_prog_node.start(prog_name, 0); - defer prog_node.end(); - try elf.genUav(pt, umi); - continue :pending; - } - var lazy_it = elf.lazy.iterator(); - while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) { - const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index }; - lazy.value.pending_index += 1; - const lazy_ty: Type = .fromInterned(lmr.lazySymbol(elf).ty); - var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined; - const prog_name: []const u8 = switch (lazy_ty.zigTypeTag(zcu)) { - .@"enum" => std.mem.print(&prog_name_buf, "@tagName({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf, - .error_set => switch (lmr.kind) { - .code => std.mem.print(&prog_name_buf, "@errorCast({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf, - .const_data => "@errorName", - }, - else => unreachable, - }; - const prog_node = elf.synth_prog_node.start(prog_name, 0); - defer prog_node.end(); - try elf.genLazy(pt, lmr); - continue :pending; - }; - break; +fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void { + while (elf.pending_uavs.pop()) |umi| { + var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined; + const prog_name = std.mem.print(&prog_name_buf, "{f}", .{ + Value.fromInterned(umi.uavValue(elf)).fmtValue(pt), + }) catch &prog_name_buf; + const prog_node = elf.const_prog_node.start(prog_name, 0); + defer prog_node.end(); + try elf.genUav(pt, umi); + } + var lazy_it = elf.lazy.iterator(); + while (lazy_it.next()) |lazy| while (lazy.value.pending_index < lazy.value.map.count()) { + try elf.genLazy(pt, .{ .kind = lazy.key, .index = lazy.value.pending_index }); + lazy.value.pending_index += 1; + }; + switch (elf.base.comp.config.debug_format) { + .strip => {}, + .dwarf => { + const gpa = elf.base.comp.gpa; + while (true) { + const pending = elf.dwarf.pending_decl; + if (pending.instance_val == .none) break; + elf.dwarf.pending_decl = .{ .di = undefined, .instance_val = .none }; + const debug_info_ni = pending.di.get(&elf.dwarf).debug_info_ni.unwrap().?; + try debug_info_ni.moved(gpa, &elf.mf); + var di_nw: MappedFile.Node.Writer = undefined; + debug_info_ni.writer(gpa, &elf.mf, &di_nw); + defer di_nw.deinit(); + elf.resetNodeRelocs(debug_info_ni); + try elf.dwarf.genDecl(pt, &di_nw, pending.instance_val); + } + }, + .code_view => unreachable, } } @@ -7978,17 +9458,17 @@ fn genUav( elf: *Elf, pt: Zcu.PerThread, umi: Node.UavMapIndex, -) Error!void { +) link.Error!void { const comp = elf.base.comp; const gpa = comp.gpa; const uav_val = umi.uavValue(elf); const ni = umi.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); var nw: MappedFile.Node.Writer = undefined; - ni.writer(&elf.mf, gpa, &nw); + ni.writer(gpa, &elf.mf, &nw); defer nw.deinit(); + elf.resetNodeRelocs(ni); codegen.generateSymbol( &elf.base, pt, @@ -7996,8 +9476,11 @@ fn genUav( &nw.interface, .{ .atom_index = Node.toAtom(ni) }, ) catch |err| switch (err) { - error.WriteFailed => return nw.err.?, else => |e| return e, + error.WriteFailed => switch (nw.err.?) { + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + }, }; switch (elf.symPtr(umi.symbol(elf).index())) { inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), @@ -8007,13 +9490,29 @@ fn genUav( assert(ni.hasMoved(&elf.mf)); } -fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { +fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) link.Error!void { + const lazy = lmr.lazySymbol(elf); + if (lazy.ty == .anyerror_type) return; + const lazy_ty: Type = .fromInterned(lazy.ty); + var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined; + const prog_name: []const u8 = switch (lazy_ty.zigTypeTag(pt.zcu)) { + .@"enum" => std.mem.print(&prog_name_buf, "@tagName({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf, + .error_set => switch (lmr.kind) { + .code => std.mem.print(&prog_name_buf, "@errorCast({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf, + .const_data => "@errorName(anyerror)", + }, + else => unreachable, + }; + const prog_node = elf.base.comp.link_prog_node.start(prog_name, 0); + defer prog_node.end(); + try elf.genLazyInner(pt, lmr); +} +fn genLazyInner(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) link.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const lazy = lmr.lazySymbol(elf); const ni = lmr.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually // be called to apply the lazy node's new relocations. @@ -8021,8 +9520,9 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { var required_alignment: InternPool.Alignment = .none; var nw: MappedFile.Node.Writer = undefined; - ni.writer(&elf.mf, gpa, &nw); + ni.writer(gpa, &elf.mf, &nw); defer nw.deinit(); + elf.resetNodeRelocs(ni); codegen.generateLazySymbol( &elf.base, pt, @@ -8032,8 +9532,14 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { .none, .{ .atom_index = Node.toAtom(ni) }, ) catch |err| switch (err) { - error.WriteFailed => return nw.err.?, else => |e| return e, + error.WriteFailed => return switch (nw.err.?) { + else => |e| return e, + error.MappedFileIo => return elf.base.comp.link_diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), + }, }; switch (elf.symPtr(lmr.symbol(elf).index())) { inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), @@ -8104,18 +9610,18 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void { fr.seekTo(file_loc.offset) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{ - elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf), + elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), path.fmtEscapeString(), fmtMemberString(ii.member(elf)), e, }), }; var nw: MappedFile.Node.Writer = undefined; - isi.node(elf).writer(&elf.mf, gpa, &nw); + isi.node(elf).writer(gpa, &elf.mf, &nw); defer nw.deinit(); const n_bytes = nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) catch |err| switch (err) { error.ReadFailed => return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{ - elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf), + elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), path.fmtEscapeString(), fmtMemberString(ii.member(elf)), fr.err orelse (fr.seek_err orelse fr.size_err.?), @@ -8123,7 +9629,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void { error.WriteFailed => return nw.err.?, }; if (n_bytes != file_loc.size) return diags.fail("failed to read input section '{s}' from \"{f}{f}\": unexpected eof", .{ - elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf), + elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), path.fmtEscapeString(), fmtMemberString(ii.member(elf)), }); @@ -8133,7 +9639,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void { } fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { - const elf_offset = elf.getNodeElfOffset(ni); + const elf_offset = elf.computeNodeElfOffset(ni); switch (elf.getNode(ni)) { else => unreachable, .ehdr => assert(elf_offset == 0), @@ -8155,7 +9661,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { elf.flushElfOffset(child_ni); } }, - .section => |shndx| switch (elf.shdrPtr(shndx)) { + .section, .section_manual_size => |shndx| switch (elf.shdrPtr(shndx)) { inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)), }, } @@ -8166,17 +9672,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void defer trace.end(); switch (elf.getNode(ni)) { - .archive => unreachable, - .archive_header => unreachable, - - .archive_input_member, - .archive_elf_member_header, - .elf, - => { + .deleted => unreachable, + .archive, .archive_header => unreachable, + .archive_input_member, .archive_elf_member_header, .elf => { assert(elf.archive != null); return; }, - .ehdr, .shdr => elf.flushElfOffset(ni), .segment => |phndx| { elf.flushElfOffset(ni); @@ -8194,6 +9695,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void .INTERP, .PHDR, .TLS, + .GNU_EH_FRAME, .GNU_RELRO, => { const new_vaddr = elf.computeNodeVAddr(ni); @@ -8204,14 +9706,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void }, } }, - .section => |shndx| { + .section, .section_manual_size => |shndx| { elf.flushElfOffset(ni); const addr = elf.computeNodeVAddr(ni); const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { - inline else => |shdr| .{ - elf.targetLoad(&shdr.addr), - elf.targetLoad(&shdr.flags).shf, - }, + inline else => |shdr| .{ elf.targetLoad(&shdr.addr), elf.targetLoad(&shdr.flags).shf }, }; if (flags.ALLOC) { @@ -8225,10 +9724,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void var name = first_name; while (name != .empty) { const old_sym_addr = Symbol.Id.global(name).value(elf); - Symbol.Id.global(name).flushMoved( - elf, - old_sym_addr - old_addr + addr, - ); + Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr); name = elf.globalByName(name).?.next_in_node; } } @@ -8246,12 +9742,18 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void reloc.apply(elf); } } else if (shndx == elf.shndx.plt) { - elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none); + elf.flushMovedNodeRelocs(ni, addr, .{ + .first_symbol_reloc = elf.plt_first_symbol_reloc, + }); elf.flushMovedPltSection(.plt, old_addr, addr); } else if (shndx == elf.shndx.got_plt) { elf.flushMovedPltSection(.got_plt, old_addr, addr); } else if (shndx == elf.shndx.plt_sec) { elf.flushMovedPltSection(.plt_sec, old_addr, addr); + } else if (shndx == elf.shndx.eh_frame_hdr) { + elf.flushMovedNodeRelocs(ni, addr, .{ + .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc, + }); } }, .input_section => |isi| { @@ -8298,12 +9800,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void } } - elf.flushMovedNodeRelocs( - ni, - new_section_addr, - isi.ptrConst(elf).first_symbol_reloc, - isi.ptrConst(elf).first_got_reloc, - ); + elf.flushMovedNodeRelocs(ni, new_section_addr, .{ + .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc, + .first_got_reloc = isi.ptrConst(elf).first_got_reloc, + }); }, .copied_global => |global_name| { const copied_global = elf.copied_globals.getPtr(global_name) orelse { @@ -8318,7 +9818,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void Symbol.Id.global(global_name).flushMoved(elf, new_addr); }, - inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| { + inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| { const new_addr = elf.computeNodeVAddr(ni); Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr); if (elf.node_global_symbols.get(ni)) |first_name| { @@ -8329,12 +9829,173 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void name = elf.globalByName(name).?.next_in_node; } } - elf.flushMovedNodeRelocs( - ni, - new_addr, - mi.firstSymbolReloc(elf), - mi.firstGotReloc(elf), - ); + elf.flushMovedNodeRelocs(ni, new_addr, .{ + .first_symbol_reloc = mi.firstSymbolReloc(elf), + .skip_symbol_relocs = switch (tag) { + else => comptime unreachable, + .nav => if (elf.dwarf.getFuncIfExists(mi.nav(elf))) |dwarf_fi| + dwarf_fi.get(&elf.dwarf).debug_info_ni + else + .none, + .uav, .lazy_code, .lazy_const_data => .none, + }, + .first_got_reloc = mi.firstGotReloc(elf), + }); + }, + .debug_shared => |ss| { + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + }, + .eh_frame_footer, .unit_padding, .unit_frame, .unit_debug_info, .unit_debug_line => {}, + .unit_frame_cie => |ui| { + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + }, + .unit_debug_info_header => |ui| { + const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_unit.debug_info_header_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc, + }); + }, + .unit_debug_info_footer => {}, + .unit_debug_line_header => |ui| { + const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_unit.debug_line_header_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc, + }); + }, + .unit_debug_rnglists => |ui| { + const dwarf_unit = &elf.dwarf_units[@backingInt(ui)]; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_unit.debug_rnglists_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + const node_vaddr = elf.computeNodeVAddr(ni); + for (dwarf_unit.debug_rnglists_symbol_relocs.keys()) |symbol_ri| { + const symbol_reloc = symbol_ri.get(elf); + assert(symbol_reloc.node.unwrap().? == ni); + symbol_reloc.flushMovedNode(elf, node_vaddr); + } + }, + .const_debug_info => |cpi| { + const dwarf_const = &elf.dwarf_consts.get(cpi).?; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_const.debug_info_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc, + .first_node_reloc = dwarf_const.debug_info_first_node_reloc, + }); + }, + .global_debug_info => |gi| { + const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)]; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_global.debug_info_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc, + .first_node_reloc = dwarf_global.debug_info_first_node_reloc, + }); + }, + .func_frame_fde => |fi| { + const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; + const zcu = elf.base.comp.zcu.?; + const mod = zcu.navFileScope(fi.nav(&elf.dwarf)).mod.?; + switch (mod.unwind_tables) { + .none => {}, + .sync, .async => { + const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); + elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset); + }, + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc, + .first_node_reloc = dwarf_func.frame_fde_first_node_reloc, + }); + }, + .func_debug_info => |fi| { + const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_func.debug_info_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc, + .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav| + nav.lsi.index().ptr(elf).node + else + .none, + .first_node_reloc = dwarf_func.debug_info_first_node_reloc, + .skip_node_relocs = fi.get(&elf.dwarf).debug_line_ni, + }); + }, + .func_debug_line => |fi| { + const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc, + .first_node_reloc = dwarf_func.debug_line_first_node_reloc, + .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni, + }); + }, + .decl_debug_info => |di| { + const dwarf_decl = &elf.dwarf_decls.get(di).?; + const target_section_offset = elf.computeNodeSectionOffset(ni); + var target_ri = dwarf_decl.debug_info_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.flushMovedTarget(elf, target_section_offset); + target_ri = target_reloc.next; + } + elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{ + .first_node_reloc = dwarf_decl.debug_info_first_node_reloc, + }); }, } try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); @@ -8496,6 +10157,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo _, const size = ni.location(&elf.mf).resolve(&elf.mf); switch (elf.getNode(ni)) { + .deleted => unreachable, .archive, .archive_header => {}, .archive_input_member => unreachable, .archive_elf_member_header => unreachable, @@ -8523,7 +10185,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL); try elf.allocateSegmentLoadAddress(phndx); }, - .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => { + .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => { elf.targetStore(&ph.memsz, @intCast(size)); }, .TLS => { @@ -8558,39 +10220,47 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo inline else => |shdr| { switch (elf.targetLoad(&shdr.type)) { else => unreachable, - .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), - - .INIT_ARRAY, - .FINI_ARRAY, - .PREINIT_ARRAY, - .STRTAB, - .SYMTAB, - .DYNAMIC, - .REL, - .RELA, - .DYNSYM, - .HASH, - => return, - } - if (shndx != elf.shndx.plt and - shndx != elf.shndx.got and - shndx != elf.shndx.got_plt) - { - elf.targetStore(&shdr.size, @intCast(size)); + .X86_64_UNWIND => {}, } + elf.targetStore(&shdr.size, @intCast(size)); }, }, - .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {}, + .section_manual_size, + .input_section, + .copied_global, + .nav, + .uav, + .lazy_code, + .lazy_const_data, + .debug_shared, + .eh_frame_footer, + .unit_padding, + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => {}, } } -fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { +fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { const trace = tracy.trace(@src()); defer trace.end(); switch (elf.getNode(ni)) { + .deleted => unreachable, .archive, .archive_input_member, .archive_elf_member_header, @@ -8599,13 +10269,17 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! .shdr, .segment, .section, + .section_manual_size, .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data, - => unreachable, + .debug_shared, + .eh_frame_footer, + .unit_debug_info_footer, + => {}, .archive_header => { const archive = &elf.archive.?; @@ -8633,6 +10307,190 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! error.NoSpaceLeft => archive.strtab_member_too_big = true, } }, + .unit_padding, + .unit_frame_cie, + .unit_debug_info_header, + .unit_debug_line_header, + .unit_debug_rnglists, + .const_debug_info, + .global_debug_info, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => |_, tag| { + const offset, const size = ni.location(&elf.mf).resolve(&elf.mf); + const parent_ni = ni.parent(&elf.mf).unwrap().?; + const slice = slice: { + if (ni.next(&elf.mf).unwrap()) |next_ni| switch (next_ni.position(&elf.mf)) { + .header => unreachable, + .footer => {}, + .floating => { + const parent_slice = parent_ni.slicePadding(&elf.mf); + const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf); + break :slice parent_slice[@intCast(offset)..@intCast(next_offset)]; + }, + }; + switch (tag) { + else => unreachable, + .unit_padding, .unit_debug_rnglists => { + const parent_slice = parent_ni.slicePadding(&elf.mf); + const frame_shndx = elf.getNodeShndx(parent_ni); + const frame_format = frame_shndx.debugFrameFormat(elf) orelse + break :slice parent_slice[@intCast(offset)..]; + const footer_size = elf.debugFrameFooterSize(frame_format); + @memset(parent_slice[@intCast(offset + size)..][0..footer_size], 0); + frame_shndx.setSize(elf, offset + size + footer_size); + break :slice parent_slice[@intCast(offset)..][0..@intCast(size)]; + }, + .unit_frame_cie, .func_frame_fde => { + const parent_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); + const frame_ni = parent_ni.parent(&elf.mf).unwrap().?; + const frame_slice = frame_ni.slicePadding(&elf.mf); + const frame_shndx = elf.getNode(frame_ni).section_manual_size; + const frame_format = frame_shndx.debugFrameFormat(elf).?; + if (parent_ni.next(&elf.mf).unwrap()) |parent_next_ni| { + switch (parent_next_ni.position(&elf.mf)) { + .header => unreachable, + .footer => {}, + .floating => { + const parent_next_offset, _ = + parent_next_ni.location(&elf.mf).resolve(&elf.mf); + const slice = frame_slice[@intCast( + parent_offset + offset, + )..@intCast(parent_next_offset)]; + var fw: Io.Writer = .fixed(slice[@intCast(size)..]); + elf.dwarf.genDebugFrameCie( + &fw, + null, + frame_format, + ) catch |err| switch (err) { + error.WriteFailed => break :slice slice, + }; + elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len); + break :slice slice[0..@intCast(size)]; + }, + } + } + const footer_size = elf.debugFrameFooterSize(frame_format); + @memset( + frame_slice[@intCast(parent_offset + offset + size)..][0..footer_size], + 0, + ); + frame_shndx.setSize(elf, parent_offset + offset + size + footer_size); + break :slice frame_slice[@intCast(parent_offset + offset)..][0..@intCast(size)]; + }, + .unit_debug_info_header, + .unit_debug_line_header, + .const_debug_info, + .global_debug_info, + .func_debug_info, + .func_debug_line, + .decl_debug_info, + => { + const parent_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); + const debug_ni = parent_ni.parent(&elf.mf).unwrap().?; + const debug_slice = debug_ni.slicePadding(&elf.mf); + var fw: Io.Writer = .fixed(buffer: { + if (parent_ni.next(&elf.mf).unwrap()) |parent_next_ni| { + switch (parent_next_ni.position(&elf.mf)) { + .header => unreachable, + .footer => {}, + .floating => { + const parent_next_offset, _ = + parent_next_ni.location(&elf.mf).resolve(&elf.mf); + break :buffer debug_slice[@intCast( + parent_offset, + )..@intCast(parent_next_offset)]; + }, + } + } + break :buffer debug_slice[@intCast(parent_offset)..]; + }); + fw.end = @intCast(offset + size); + switch (tag) { + else => unreachable, + .unit_debug_info_header, + .const_debug_info, + .global_debug_info, + .func_debug_info, + .decl_debug_info, + => for (0..2) |_| fw.writeUleb128(@backingInt(Dwarf.AbbrevCode.null)) catch + unreachable, + .unit_debug_line_header, .func_debug_line => {}, + } + const unit_padding_offset = fw.end; + const unit_padding = fw.unusedCapacitySlice(); + elf.dwarf.genUnitPadding(&fw) catch |err| switch (err) { + error.WriteFailed => { + fw.end = unit_padding_offset; + elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len); + switch (tag) { + else => unreachable, + .unit_debug_info_header, + .const_debug_info, + .global_debug_info, + .func_debug_info, + .decl_debug_info, + => { + comptime assert( + Dwarf.uleb128Size(@backingInt(Dwarf.AbbrevCode.null)) == 1, + ); + @memset( + fw.unusedCapacitySlice(), + @backingInt(Dwarf.AbbrevCode.null), + ); + }, + .unit_debug_line_header, + .func_debug_line, + => Dwarf.genDebugLinePadding(&fw, fw.unusedCapacityLen()) catch + unreachable, + } + return; + }, + }; + elf.dwarf.updateUnitLength(fw.buffer, unit_padding_offset); + elf.dwarf.updateUnitLength(unit_padding, unit_padding.len); + return; + }, + } + }; + var fw: Io.Writer = .fixed(slice[@intCast(size)..]); + switch (tag) { + else => unreachable, + .unit_padding => elf.dwarf.updateUnitLength(slice, slice.len), + .unit_frame_cie, .func_frame_fde => { + elf.dwarf.updateUnitLength(slice, slice.len); + @memset(fw.buffer, std.dwarf.CFA.nop); + }, + .unit_debug_info_header, + .const_debug_info, + .global_debug_info, + .func_debug_info, + .decl_debug_info, + => elf.dwarf.genDebugInfoPadding(&fw, fw.buffer.len) catch unreachable, + .unit_debug_line_header, + .func_debug_line, + => Dwarf.genDebugLinePadding(&fw, fw.buffer.len) catch unreachable, + .unit_debug_rnglists => { + elf.dwarf.genUnitPadding(&fw) catch |err| switch (err) { + error.WriteFailed => { + elf.dwarf.updateUnitLength(slice, slice.len); + @memset(fw.buffer, std.dwarf.RLE.end_of_list); + return; + }, + }; + elf.dwarf.updateUnitLength(slice, size); + elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len); + }, + } + }, + .unit_frame, .unit_debug_info, .unit_debug_line => { + var last_ni = ni.last(&elf.mf).unwrap() orelse return; + while (last_ni.position(&elf.mf) == .footer) + last_ni = last_ni.prev(&elf.mf).unwrap() orelse return; + try last_ni.nextMoved(elf.base.comp.gpa, &elf.mf); + }, } } @@ -8987,11 +10845,10 @@ pub fn updateExports( pt: Zcu.PerThread, export_indices: []const Zcu.Export.Index, ) link.Error!void { - const diags = &elf.base.comp.link_diags; for (export_indices) |export_index| { elf.updateExportInner(pt, export_index) catch |err| switch (err) { else => |e| return e, - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; } } @@ -9068,17 +10925,19 @@ fn updateExportInner( }; } -fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) !void { +fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void { const comp = elf.base.comp; const io = comp.io; var buffer: [512]u8 = undefined; const stderr = try io.lockStderr(&buffer, null); defer io.unlockStderr(); const w = &stderr.file_writer.interface; - _ = try elf.dump(w, tid); + _ = elf.dump(w, tid) catch |err| switch (err) { + error.WriteFailed => return stderr.file_writer.err.?, + }; } -pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult { +pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult { if (elf.options.enable_link_snapshots) { try elf.printNode(tid, w, .root, 0); return .enabled; @@ -9118,22 +10977,22 @@ pub fn printNode( try w.writeByte(')'); }, }, - .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}), + .section, .section_manual_size => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}), .input_section => |isi| { const ii = isi.input(elf); try w.print("({f}{f}, {s})", .{ ii.path(elf).fmtEscapeString(), fmtMemberString(ii.member(elf)), - elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf), + elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf), }); }, .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}), .nav => |nmi| { const zcu = elf.base.comp.zcu.?; const ip = &zcu.intern_pool; - const nav = ip.getNav(nmi.navIndex(elf)); + const nav = ip.getNav(nmi.nav(elf)); try w.print("({f}, {f})", .{ - Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }), + Type.fromInterned(nav.resolved.?.type).fmt(.{ .zcu = zcu, .tid = tid }), nav.fqn.fmt(ip), }); }, @@ -9151,19 +11010,66 @@ pub fn printNode( .tid = tid, }), }), + .debug_shared => |ss| try w.print("({})", .{ss}), + .unit_frame, + .unit_frame_cie, + .unit_debug_info, + .unit_debug_info_header, + .unit_debug_info_footer, + .unit_debug_line, + .unit_debug_line_header, + .unit_debug_rnglists, + => |ui| try w.print("({s})", .{ui.mod(&elf.dwarf).fully_qualified_name}), + .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) { + .generic_poison_type => try w.writeAll("(anytype)"), + else => |val| try w.print("({f})", .{ + Value.fromInterned(val).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }), + }), + }, + .global_debug_info => |gi| { + const zcu = elf.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(gi.nav(&elf.dwarf)); + try w.writeByte('('); + if (nav.resolved) |resolved| try w.print("{f}, ", .{ + Type.fromInterned(resolved.type).fmt(.{ .zcu = zcu, .tid = tid }), + }); + try w.print("{f})", .{nav.fqn.fmt(ip)}); + }, + .func_frame_fde, .func_debug_info, .func_debug_line => |fi| { + const zcu = elf.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(fi.nav(&elf.dwarf)); + try w.writeByte('('); + if (nav.resolved) |resolved| try w.print("{f}, ", .{ + Type.fromInterned(resolved.type).fmt(.{ .zcu = zcu, .tid = tid }), + }); + try w.print("{f})", .{nav.fqn.fmt(ip)}); + }, + .decl_debug_info => |di| { + const comp = elf.base.comp; + const zcu = comp.zcu.?; + const ip = &zcu.intern_pool; + const src_inst = di.srcInst(&elf.dwarf); + try w.print("({f}, ", .{zcu.fileByIndex(src_inst.resolveFile(ip)).path.fmt(comp)}); + if (src_inst.resolve(ip)) |inst| try w.print("%{d}", .{inst}) else try w.writeAll("lost"); + try w.writeByte(')'); + }, } { const mf_node = &elf.mf.nodes.items[@backingInt(ni)]; const off, const size = mf_node.location().resolve(&elf.mf); - try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}\n", .{ + try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}{s}{s}\n", .{ @backingInt(ni), off, size, mf_node.flags.alignment.toByteUnits(), mf_node.flags.position, - if (mf_node.flags.moved) " moved" else "", - if (mf_node.flags.next_moved) " next_moved" else "", + if (mf_node.flags.bubbles_moved) " bubbles_moved" else "", + if (mf_node.flags.resized) " moved" else "", if (mf_node.flags.resized) " resized" else "", + if (mf_node.flags.enable_next_moved) " enable_next_moved" else "", + if (mf_node.flags.next_moved) " next_moved" else "", if (mf_node.flags.has_content) " has_content" else "", }); } @@ -9176,26 +11082,30 @@ pub fn printNode( } return; } - const file_loc = ni.fileLocation(&elf.mf, false); - var address = file_loc.offset; - if (file_loc.size == 0) { - try w.splatByteAll(' ', indent + 1); - try w.print("{x:0>8}\n", .{address}); - return; - } + const start_address: usize, const end_address: usize = file_loc: { + const file_loc = ni.fileLocation(&elf.mf, false); + break :file_loc .{ @intCast(file_loc.offset), @intCast(file_loc.offset + file_loc.size) }; + }; + var address = start_address; const line_len = 0x10; - var line_it = std.mem.window( - u8, - elf.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)], - line_len, - line_len, - ); - while (line_it.next()) |line_bytes| : (address += line_len) { + while (true) : (address = @min(std.mem.alignForward(usize, address + 1, line_len), end_address)) { try w.splatByteAll(' ', indent + 1); - try w.print("{x:0>8} ", .{address}); - for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte}); - try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1); - for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.'); + try w.print("{x:0>8}", .{address}); + if (address == end_address) break try w.writeByte('\n'); + try w.splatByteAll(' ', 2); + const start_byte_address = std.mem.alignBackward(usize, address, line_len); + const end_byte_address = start_byte_address + line_len; + for (start_byte_address..end_byte_address) |byte_address| + if (byte_address < start_address or byte_address >= end_address) + try w.splatByteAll(' ', 3) + else + try w.print("{x:0>2} ", .{elf.mf.memory_map.memory[byte_address]}); + try w.writeByte(' '); + for (start_byte_address..@min(end_address, end_byte_address)) |byte_address| + try w.writeByte(if (byte_address < start_address or byte_address >= end_address) ' ' else char: { + const byte = elf.mf.memory_map.memory[byte_address]; + break :char if (std.ascii.isPrint(byte)) byte else '.'; + }); try w.writeByte('\n'); } } @@ -9209,7 +11119,7 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error // Align the actual node const seg_ni = elf.phdrs.items[phndx].unwrap().?; if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) { - try seg_ni.realign(&elf.mf, gpa, min_align); + try seg_ni.realign(gpa, &elf.mf, min_align); } // Update the phdr `@"align"` field if necessary switch (elf.phdrSlice()) { @@ -9240,6 +11150,21 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error } } +pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) MappedFile.Node.Index { + if (elf.nodes.len - @backingInt(ni) > 0) { + assert(elf.getNode(ni) == .deleted); + elf.nodes.set(@backingInt(ni), node); + } else elf.nodes.appendAssumeCapacity(node); + return ni; +} + +fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocator.Error!void { + const ni = node.unwrap().?; + try ni.delete(elf.base.comp.gpa, &elf.mf); + elf.nodes.set(@backingInt(ni), .deleted); + node.* = .none; +} + /// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a /// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`. fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0a2c0c9c50c14065f77e166fac72b68ef6820bb0..72e34b943e1d565e6c3360cfd017b4d9c7ba3f5e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3095,8 +3095,8 @@ pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) lin return self.getZigObject().?.updateNav(self, pt, nav); } -pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { - return self.getZigObject().?.updateLineNumber(pt, ti_id); +pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, inst: InternPool.TrackedInst.Index, line: u32) link.Error!void { + return self.getZigObject().?.updateLineNumber(pt, inst, line); } pub fn updateExports( @@ -5503,4 +5503,3 @@ const Value = @import("../Value.zig"); const UnwindInfo = @import("MachO/UnwindInfo.zig"); const WeakBind = bind.WeakBind; const ZigObject = @import("MachO/ZigObject.zig"); -const dev = @import("../dev.zig"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index a79fd6d95f28e77da73b609719111766892d1718..33da74bb70a2bc5ac82d053f223a6dfe6f673c52 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -853,7 +853,7 @@ fn resolveRelocInner( const x86_64 = struct { fn relaxGotLoad(self: Atom, code: []u8, rel: Relocation, macho_file: *MachO) ResolveError!void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const t = &macho_file.base.comp.root_mod.resolved_target.result; const diags = &macho_file.base.comp.link_diags; const old_inst = disassemble(code) orelse return error.RelaxFail; @@ -879,7 +879,7 @@ const x86_64 = struct { } fn relaxTlv(code: []u8, t: *const std.Target) error{RelaxFail}!void { - dev.check(.x86_64_backend); + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const old_inst = disassemble(code) orelse return error.RelaxFail; switch (old_inst.encoding.mnemonic) { .mov => { diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 6f66764847e91d8264701aa9bba95266fcbd2ae5..3c45dc419db5b1fc86b47eeffeb5c438c36ea38e 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -3,6 +3,7 @@ const Object = @This(); const trace = @import("../../tracy.zig").trace; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); +const dev = @import("../../dev.zig"); const Dwarf = @import("Dwarf.zig"); const File = @import("file.zig").File; const MachO = @import("../MachO.zig"); @@ -2826,6 +2827,7 @@ const x86_64 = struct { handle: File.Handle, macho_file: *MachO, ) !void { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const comp = macho_file.base.comp; const io = comp.io; const gpa = comp.gpa; @@ -2938,6 +2940,7 @@ const x86_64 = struct { } fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64, is_extern: bool) !Relocation.Type { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); switch (rel_type) { .X86_64_RELOC_UNSIGNED => { if (rel.r_pcrel == 1) return error.Pcrel; @@ -2995,6 +2998,7 @@ const aarch64 = struct { handle: File.Handle, macho_file: *MachO, ) !void { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); const comp = macho_file.base.comp; const io = comp.io; const gpa = comp.gpa; @@ -3131,6 +3135,7 @@ const aarch64 = struct { } fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64, is_extern: bool) !Relocation.Type { + dev.checkAny(&.{ .llvm_backend, .aarch64_backend }); switch (rel_type) { .ARM64_RELOC_UNSIGNED => { if (rel.r_pcrel == 1) return error.Pcrel; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 0ff8358077b1c7a7d636a3164bdfe4d677cdcd97..93a76ca63a92db3d6528b70f827ddc93a1c89022 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -647,14 +647,11 @@ pub fn getNavVAddr( }, }); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(sym_index)), + .target_off = reloc_info.addend, + }), } return vaddr; } @@ -686,14 +683,11 @@ pub fn getUavVAddr( }, }); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(sym_index)), + .target_off = reloc_info.addend, + }), } return vaddr; } @@ -1418,11 +1412,11 @@ fn updateLazySymbol( try macho_file.pwriteAll(code, file_offset); } -pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { +pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) link.Error!void { if (self.dwarf) |*dwarf| { const comp = dwarf.bin_file.comp; const diags = &comp.link_diags; - dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { + dwarf.updateLineNumber(pt.zcu, ti_id, line) catch |err| switch (err) { error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), }; @@ -1750,6 +1744,7 @@ const TlvInitializerTable = std.array_hash_map.Auto(Atom.Index, TlvInitializer); const x86_64 = struct { fn writeTrampolineCode(source_addr: u64, target_addr: u64, buf: *[max_trampoline_len]u8) ![]u8 { + dev.checkAny(&.{ .llvm_backend, .x86_64_backend }); const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)) - 5; var bytes = [_]u8{ 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32 @@ -1764,6 +1759,7 @@ const x86_64 = struct { const assert = std.debug.assert; const builtin = @import("builtin"); const codegen = @import("../../codegen.zig"); +const dev = @import("../../dev.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.link); const macho = std.macho; diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 937b0989a8d40dabcf74ea8c920ac47a2a5d1f67..22b6f529accf3ba22eac77e9574677140260bfcd 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -362,7 +362,7 @@ pub const Node = extern struct { } /// Adds a floating child node to `parent_ni`. Returns the index of the new child. - pub fn addFloatingChild(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, opts: AddOptions) Error!Node.Index { + pub fn addFloatingChild(parent_ni: Node.Index, gpa: Allocator, mf: *MappedFile, opts: AddOptions) Error!Node.Index { return mf.addNode(gpa, .{ .add_options = opts, .position = .floating, @@ -373,11 +373,11 @@ pub const Node = extern struct { /// Adds a header child node to `parent_ni`. Returns the index of the new child. /// /// Asserts that `parent_ni` has no existing header children. - pub fn addOnlyHeaderChild(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, opts: AddOptions) Error!Node.Index { + pub fn addOnlyHeaderChild(parent_ni: Node.Index, gpa: Allocator, mf: *MappedFile, opts: AddOptions) Error!Node.Index { if (parent_ni.first(mf).unwrap()) |first_ni| { assert(first_ni.position(mf) != .header); // `parent_ni` already has a header child } - return parent_ni.addHeaderChildAfter(mf, gpa, .none, opts); + return parent_ni.addHeaderChildAfter(gpa, mf, .none, opts); } /// Adds a header child node to `parent_ni`. Returns the index of the new child. /// @@ -386,7 +386,7 @@ pub const Node = extern struct { /// /// Otherwise, asserts that `prev_oni` is a header node and a child of `parent_ni`, and /// places the new child node immediately after `prev_oni`. - pub fn addHeaderChildAfter(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, prev_oni: Node.Index.Optional, opts: AddOptions) Error!Node.Index { + pub fn addHeaderChildAfter(parent_ni: Node.Index, gpa: Allocator, mf: *MappedFile, prev_oni: Node.Index.Optional, opts: AddOptions) Error!Node.Index { return mf.addNode(gpa, .{ .add_options = opts, .position = .header, @@ -397,11 +397,11 @@ pub const Node = extern struct { /// Adds a footer child node to `parent_ni`. Returns the index of the new child. /// /// Asserts that `parent_ni` has no existing footer children. - pub fn addOnlyFooterChild(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, opts: AddOptions) Error!Node.Index { + pub fn addOnlyFooterChild(parent_ni: Node.Index, gpa: Allocator, mf: *MappedFile, opts: AddOptions) Error!Node.Index { if (parent_ni.last(mf).unwrap()) |last_ni| { assert(last_ni.position(mf) != .footer); // `parent_ni` already has a footer child } - return parent_ni.addFooterChildBefore(mf, gpa, .none, opts); + return parent_ni.addFooterChildBefore(gpa, mf, .none, opts); } /// Adds a footer child node to `parent_ni`. Returns the index of the new child. /// @@ -410,7 +410,7 @@ pub const Node = extern struct { /// /// Otherwise, asserts that `next_oni` is a footer node and a child of `parent_ni`, and /// places the new child node immediately before `next_oni`. - pub fn addFooterChildBefore(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, next_oni: Node.Index.Optional, opts: AddOptions) Error!Node.Index { + pub fn addFooterChildBefore(parent_ni: Node.Index, gpa: Allocator, mf: *MappedFile, next_oni: Node.Index.Optional, opts: AddOptions) Error!Node.Index { const prev_oni: Node.Index.Optional = prev: { const next_ni = next_oni.unwrap() orelse { break :prev parent_ni.last(mf); @@ -473,8 +473,8 @@ pub const Node = extern struct { fn setNext( ni: Node.Index, gpa: Allocator, + mf: *MappedFile, next_ni: Node.Index.Optional, - mf: *MappedFile, ) Allocator.Error!void { const next_ptr = &ni.get(mf).next; if (next_ptr.* == next_ni) return; @@ -514,12 +514,10 @@ pub const Node = extern struct { return node_moved.*; } pub fn movedAssumeCapacity(ni: Node.Index, mf: *MappedFile) void { + const node = ni.get(mf); + if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf); if (ni.hasMoved(mf)) return; - const node = ni.get(mf); node.flags.moved = true; - if (node.prev.unwrap()) |prev_ni| { - prev_ni.nextMovedAssumeCapacity(mf); - } if (node.flags.resized or node.flags.next_moved) return; mf.updates.appendAssumeCapacity(ni); mf.update_prog_node.increaseEstimatedTotalItems(1); @@ -571,7 +569,7 @@ pub const Node = extern struct { return ni.get(mf).flags.alignment; } - fn setLocation(ni: Node.Index, mf: *MappedFile, gpa: Allocator, offset: u64, size: u64) Allocator.Error!void { + fn setLocation(ni: Node.Index, gpa: Allocator, mf: *MappedFile, offset: u64, size: u64) Allocator.Error!void { try mf.large.ensureUnusedCapacity(gpa, 2); try mf.updates.ensureUnusedCapacity(gpa, 2); const node = ni.get(mf); @@ -636,15 +634,38 @@ pub const Node = extern struct { return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; } + pub fn slicePadding(ni: Node.Index, mf: *const MappedFile) []u8 { + const file_loc = ni.fileLocation(mf, false); + return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; + } + pub fn sliceConst(ni: Node.Index, mf: *const MappedFile) []const u8 { const file_loc = ni.fileLocation(mf, false); return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; } + pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void { + const node = ni.get(mf); + assert(node.first == .none and node.last == .none); // has children + mf.removeNodesFromChildList(gpa, ni, ni); + const updated = node.flags.moved or node.flags.resized or node.flags.next_moved; + node.* = undefined; + node.next = ni.toOptional(); + if (!updated) assert(ni.pendingDelete(mf)); + } + + pub fn pendingDelete(ni: Node.Index, mf: *MappedFile) bool { + const node = ni.get(mf); + if (node.next != ni.toOptional()) return false; + node.next = mf.free_ni; + mf.free_ni = ni.toOptional(); + return true; + } + /// Ensures that the size of `ni` is at least `min_size`. Valid for any node. /// /// Applies `growth_factor` if necessary (so the caller should *not* apply `growth_factor`). - pub fn ensureMinimumSize(ni: Node.Index, mf: *MappedFile, gpa: Allocator, min_size: u64) Error!void { + pub fn ensureMinimumSize(ni: Node.Index, gpa: Allocator, mf: *MappedFile, min_size: u64) Error!void { _, const current_size = ni.location(mf).resolve(mf); if (current_size >= min_size) return; const new_size = ni.alignment(mf).forward(min_size +| min_size / growth_factor); @@ -660,7 +681,7 @@ pub const Node = extern struct { /// Asserts that `ni` is a leaf node, i.e. has no children. /// /// Asserts that `size` is aligned to `ni.alignment(mf)`. - pub fn resizeLeaf(ni: Node.Index, mf: *MappedFile, gpa: Allocator, size: u64) Error!void { + pub fn resizeLeaf(ni: Node.Index, gpa: Allocator, mf: *MappedFile, size: u64) Error!void { assert(ni.first(mf) == .none); // The alignment of `size` is asserted by `shrinkLeafNode` and `growNode`. _, const old_size = ni.location(mf).resolve(mf); @@ -680,17 +701,12 @@ pub const Node = extern struct { /// If the node's current offset or size is not sufficiently aligned, it will be moved /// and/or resized to match the new alignment. The node's size may be increased by any /// amount, as if `ensureMinimumSize` were used. - pub fn realign( - ni: Node.Index, - mf: *MappedFile, - gpa: Allocator, - new_alignment: Alignment, - ) Error!void { + pub fn realign(ni: Node.Index, gpa: Allocator, mf: *MappedFile, new_alignment: Alignment) Error!void { try mf.realignNode(gpa, ni, new_alignment); mf.updateWriters(); } - pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: Allocator, w: *Writer) void { + pub fn writer(ni: Node.Index, gpa: Allocator, mf: *MappedFile, w: *Writer) void { w.* = .{ .gpa = gpa, .mf = mf, @@ -820,7 +836,7 @@ pub const Node = extern struct { ) Io.Writer.Error!void { _ = preserve; const w: *Writer = @fieldParentPtr("interface", interface); - w.ni.ensureMinimumSize(w.mf, w.gpa, interface.end + unused_capacity) catch |err| { + w.ni.ensureMinimumSize(w.gpa, w.mf, interface.end + unused_capacity) catch |err| { w.err = err; return error.WriteFailed; }; @@ -992,7 +1008,7 @@ fn shrinkLeafNode( }, }; try mf.ensureTotalCapacityPrecise(@intCast(new_size)); - try ni.setLocation(mf, gpa, old_offset, new_size); + try ni.setLocation(gpa, mf, old_offset, new_size); return; }; @@ -1000,7 +1016,7 @@ fn shrinkLeafNode( .header => { const shift = old_size - new_size; - try ni.setLocation(mf, gpa, old_offset, new_size); + try ni.setLocation(gpa, mf, old_offset, new_size); // We need to shift backwards all header nodes following us. const next_header_ni = ni.next(mf).unwrap() orelse return; @@ -1009,7 +1025,7 @@ fn shrinkLeafNode( var header_ni = next_header_ni; while (true) { const old_header_off, const old_header_size = header_ni.location(mf).resolve(mf); - try header_ni.setLocation(mf, gpa, old_header_off - shift, old_header_size); + try header_ni.setLocation(gpa, mf, old_header_off - shift, old_header_size); const next_ni = header_ni.next(mf).unwrap() orelse break; if (next_ni.position(mf) != .header) break; @@ -1034,13 +1050,13 @@ fn shrinkLeafNode( ); }, .floating => { - try ni.setLocation(mf, gpa, old_offset, new_size); + try ni.setLocation(gpa, mf, old_offset, new_size); }, .footer => { const shift = old_size - new_size; const new_offset = old_offset + shift; - try ni.setLocation(mf, gpa, new_offset, new_size); + try ni.setLocation(gpa, mf, new_offset, new_size); const prev_footers_size = prev_footers_size: { // We need to shift forwards all footer nodes preceding us. @@ -1054,7 +1070,7 @@ fn shrinkLeafNode( var footer_ni = prev_footer_ni; while (true) { const old_footer_off, const old_footer_size = footer_ni.location(mf).resolve(mf); - try footer_ni.setLocation(mf, gpa, old_footer_off + shift, old_footer_size); + try footer_ni.setLocation(gpa, mf, old_footer_off + shift, old_footer_size); const prev_ni = footer_ni.prev(mf).unwrap() orelse break; if (prev_ni.position(mf) != .footer) break; @@ -1137,7 +1153,7 @@ fn growNode( }, }; try mf.ensureTotalCapacityPrecise(@intCast(new_size)); - try ni.setLocation(mf, gpa, old_offset, new_size); + try ni.setLocation(gpa, mf, old_offset, new_size); if (grow_options.move_footers) { // We need to move any footers to be at the *new* end of the file. if (ni.firstFooter(mf).unwrap()) |first_footer_ni| { @@ -1152,7 +1168,7 @@ fn growNode( var cur_ni = first_footer_ni; while (true) { const old_footer_offset, const footer_size = cur_ni.location(mf).resolve(mf); - try cur_ni.setLocation(mf, gpa, old_footer_offset + (new_size - old_size), footer_size); + try cur_ni.setLocation(gpa, mf, old_footer_offset + (new_size - old_size), footer_size); cur_ni = cur_ni.next(mf).unwrap() orelse break; } } @@ -1211,8 +1227,8 @@ fn growNode( while (true) { const old_sub_footer_offset, const sub_footer_size = cur_ni.location(mf).resolve(mf); try cur_ni.setLocation( - mf, gpa, + mf, old_sub_footer_offset + (new_size - old_size), sub_footer_size, ); @@ -1227,8 +1243,8 @@ fn growNode( assert(moved_header_ni.position(mf) == .header); const moved_header_offset, const moved_header_size = moved_header_ni.location(mf).resolve(mf); try moved_header_ni.setLocation( - mf, gpa, + mf, moved_header_offset - old_size + new_size, moved_header_size, ); @@ -1237,7 +1253,7 @@ fn growNode( } // Finally, update our own size: - try ni.setLocation(mf, gpa, old_offset, new_size); + try ni.setLocation(gpa, mf, old_offset, new_size); return; }, .floating => { @@ -1365,8 +1381,8 @@ fn growNode( // Update our own offset and size: try ni.setLocation( - mf, gpa, + mf, node.location().resolve(mf)[0] - shift, new_size, ); @@ -1377,7 +1393,7 @@ fn growNode( var footer_oni = first_sub_footer_oni; while (footer_oni.unwrap()) |footer_ni| : (footer_oni = footer_ni.next(mf)) { const old_footer_offset, const footer_size = footer_ni.location(mf).resolve(mf); - try footer_ni.setLocation(mf, gpa, old_footer_offset + shift, footer_size); + try footer_ni.setLocation(gpa, mf, old_footer_offset + shift, footer_size); } } @@ -1392,7 +1408,7 @@ fn growNode( while (footer_ni != ni) : (footer_ni = footer_ni.next(mf).unwrap().?) { moved_has_content = moved_has_content or footer_ni.get(mf).flags.has_content; const old_footer_offset, const footer_size = footer_ni.location(mf).resolve(mf); - try footer_ni.setLocation(mf, gpa, old_footer_offset - shift, footer_size); + try footer_ni.setLocation(gpa, mf, old_footer_offset - shift, footer_size); } } @@ -1444,8 +1460,8 @@ fn growNode( } try ni.setLocation( - mf, gpa, + mf, node.location().resolve(mf)[0], actual_new_size, ); @@ -1461,7 +1477,7 @@ fn growNode( assert(footer_ni.position(mf) == .footer); moved_has_content = moved_has_content or footer_ni.get(mf).flags.has_content; const footer_old_offset: u64, const footer_size: u64 = footer_ni.location(mf).resolve(mf); - try footer_ni.setLocation(mf, gpa, footer_old_offset + shift, footer_size); + try footer_ni.setLocation(gpa, mf, footer_old_offset + shift, footer_size); } } @@ -1472,7 +1488,7 @@ fn growNode( assert(footer_ni.position(mf) == .footer); moved_has_content = moved_has_content or footer_ni.get(mf).flags.has_content; const footer_old_offset: u64, const footer_size: u64 = footer_ni.location(mf).resolve(mf); - try footer_ni.setLocation(mf, gpa, footer_old_offset + shift, footer_size); + try footer_ni.setLocation(gpa, mf, footer_old_offset + shift, footer_size); } } @@ -1544,7 +1560,7 @@ fn growFloatingNodeWithAlignment( break :grow_in_place; // the parent is not big enough } // Great, we can grow this node without changing its offset or moving any siblings. - try ni.setLocation(mf, gpa, old_offset, new_size); + try ni.setLocation(gpa, mf, old_offset, new_size); if (grow_options.move_footers) { // If we have any footers, we need to move them to the end of our new size, and update // their offsets accordingly. @@ -1554,7 +1570,7 @@ fn growFloatingNodeWithAlignment( while (true) { footers_have_content = footers_have_content or cur_ni.get(mf).flags.has_content; const old_footer_offset, const footer_size = cur_ni.location(mf).resolve(mf); - try cur_ni.setLocation(mf, gpa, old_footer_offset + (new_size - old_size), footer_size); + try cur_ni.setLocation(gpa, mf, old_footer_offset + (new_size - old_size), footer_size); cur_ni = cur_ni.next(mf).unwrap() orelse break; } if (footers_have_content) { @@ -1691,7 +1707,7 @@ fn growFloatingNodeWithAlignment( footers_have_content = footers_have_content or cur_ni.get(mf).flags.has_content; const old_footer_offset, const footer_size = cur_ni.location(mf).resolve(mf); // Our footers' offsets must change to be at the end of our new size. - try cur_ni.setLocation(mf, gpa, old_footer_offset + (new_size - old_size), footer_size); + try cur_ni.setLocation(gpa, mf, old_footer_offset + (new_size - old_size), footer_size); cur_ni = cur_ni.next(mf).unwrap() orelse break; } @@ -1718,7 +1734,7 @@ fn growFloatingNodeWithAlignment( assert(!footers_have_content); } - try ni.setLocation(mf, gpa, new_loc.offset, new_size); + try ni.setLocation(gpa, mf, new_loc.offset, new_size); if (new_loc.prev != ni.toOptional()) { // We're potentially in a different place in `parent_ni`'s child list, so remove and re-add ourselves. @@ -1918,11 +1934,11 @@ fn growNodeViaInsertRange( if (cur_ni == .root) { try mf.ensureTotalCapacityPrecise(@intCast(this_old_size + range_size)); } - try cur_ni.setLocation(mf, gpa, this_offset, this_old_size + range_size); + try cur_ni.setLocation(gpa, mf, this_offset, this_old_size + range_size); while (cur_ni.next(mf).unwrap()) |next_ni| { const next_old_offset, const next_size = next_ni.location(mf).resolve(mf); - try next_ni.setLocation(mf, gpa, next_old_offset + range_size, next_size); + try next_ni.setLocation(gpa, mf, next_old_offset + range_size, next_size); cur_ni = next_ni; } @@ -1935,7 +1951,7 @@ fn growNodeViaInsertRange( var footer_ni = first_footer_ni; while (true) { const old_footer_offset, const footer_size = footer_ni.location(mf).resolve(mf); - try footer_ni.setLocation(mf, gpa, old_footer_offset + range_size, footer_size); + try footer_ni.setLocation(gpa, mf, old_footer_offset + range_size, footer_size); footer_ni = footer_ni.next(mf).unwrap() orelse break; } } @@ -2103,7 +2119,7 @@ fn ensureAdditionalHeaderCapacity( const old_offset, const old_size = cur_ni.location(mf).resolve(mf); const new_offset = old_offset - moving_offset + dest_offset; assert(cur_ni.alignment(mf).check(new_offset)); - try cur_ni.setLocation(mf, gpa, new_offset, old_size); + try cur_ni.setLocation(gpa, mf, new_offset, old_size); if (cur_ni == last_moving_ni) break; cur_ni = cur_ni.next(mf).unwrap().?; } @@ -2146,7 +2162,7 @@ fn removeNodesFromChildList( if (prev_oni.unwrap()) |prev_ni| { assert(prev_ni.next(mf).unwrap().? == first_remove_ni); - try prev_ni.setNext(gpa, next_oni, mf); + try prev_ni.setNext(gpa, mf, next_oni); } else { assert(parent_ni.first(mf).unwrap().? == first_remove_ni); parent_ni.get(mf).first = next_oni; @@ -2185,11 +2201,11 @@ fn addNodesToChildListBefore( }; first_add_ni.get(mf).prev = prev_oni; - try last_add_ni.setNext(gpa, next_oni, mf); + try last_add_ni.setNext(gpa, mf, next_oni); if (prev_oni.unwrap()) |prev_ni| { assert(prev_ni.next(mf) == next_oni); - try prev_ni.setNext(gpa, .wrap(first_add_ni), mf); + try prev_ni.setNext(gpa, mf, .wrap(first_add_ni)); } else { assert(parent_ni.first(mf) == next_oni); parent_ni.get(mf).first = .wrap(first_add_ni); @@ -2630,7 +2646,7 @@ fn fuzzOneNodeOperations(_: void, smith: *std.testing.Smith) anyerror!void { for (1..n) |_| cur_ni = cur_ni.next(&mf).unwrap().?; break :prev_oni .wrap(cur_ni); }; - const new_ni = try parent_ni.addHeaderChildAfter(&mf, gpa, prev_oni, .{ + const new_ni = try parent_ni.addHeaderChildAfter(gpa, &mf, prev_oni, .{ .size = size, .alignment = alignment, }); @@ -2638,7 +2654,7 @@ fn fuzzOneNodeOperations(_: void, smith: *std.testing.Smith) anyerror!void { break :new_ni new_ni; }, - .floating => try parent_ni.addFloatingChild(&mf, gpa, .{ + .floating => try parent_ni.addFloatingChild(gpa, &mf, .{ .size = size, .alignment = alignment, }), @@ -2652,7 +2668,7 @@ fn fuzzOneNodeOperations(_: void, smith: *std.testing.Smith) anyerror!void { for (1..n) |_| cur_ni = cur_ni.prev(&mf).unwrap().?; break :next_oni .wrap(cur_ni); }; - const new_ni = try parent_ni.addFooterChildBefore(&mf, gpa, next_oni, .{ + const new_ni = try parent_ni.addFooterChildBefore(gpa, &mf, next_oni, .{ .size = size, .alignment = alignment, }); @@ -2686,13 +2702,13 @@ fn fuzzOneNodeOperations(_: void, smith: *std.testing.Smith) anyerror!void { if (ni.first(&mf) == .none and smith.value(bool)) { // Since this is a leaf node, we can use `resizeLeaf`. const new_size = alignment.forward(smith.valueWeighted(u64, initial_size_weights)); - try ni.resizeLeaf(&mf, gpa, new_size); + try ni.resizeLeaf(gpa, &mf, new_size); if (new_size == 0) { node_info.initialized = false; } } else { const min_size = alignment.forward(smith.valueWeighted(u64, initial_size_weights)); - try ni.ensureMinimumSize(&mf, gpa, min_size); + try ni.ensureMinimumSize(gpa, &mf, min_size); } if (ni.first(&mf) == .none) { @@ -2718,7 +2734,7 @@ fn fuzzOneNodeOperations(_: void, smith: *std.testing.Smith) anyerror!void { const new_alignment = smith.valueWeighted(Alignment, alignment_weights); if (new_alignment.compare(.gt, ni.alignment(&mf))) { _, const old_size = ni.location(&mf).resolve(&mf); - try ni.realign(&mf, gpa, new_alignment); + try ni.realign(gpa, &mf, new_alignment); if (ni.first(&mf) == .none and nodes.get(ni).?.initialized) { const slice = ni.slice(&mf); @memmove(slice[slice.len - 4 ..][0..4], slice[old_size - 4 ..][0..4]); diff --git a/src/link/Spork8.zig b/src/link/Spork8.zig index 3487387c55b2d52580494793ce34c5e11a36a764..6f925fbc71fb092a11c4fa3b8d99c0e406c6e436 100644 --- a/src/link/Spork8.zig +++ b/src/link/Spork8.zig @@ -18,7 +18,6 @@ const Mir = @import("../codegen/spork8/Mir.zig"); const link = @import("../link.zig"); const Compilation = @import("../Compilation.zig"); const Liveness = @import("../Air/Liveness.zig"); -const dev = @import("../dev.zig"); const Value = @import("../Value.zig"); base: link.File, @@ -89,7 +88,6 @@ pub fn updateFunc( func_index: InternPool.Index, any_mir: *const codegen.AnyMir, ) !void { - dev.check(.spork8_backend); // This linker implementation only works with `std.lang.CompilerBackend.zsf_spork8`. const mir = &any_mir.spork8; const zcu = pt.zcu; @@ -168,10 +166,11 @@ pub fn updateNav(spork8: *Spork8, pt: Zcu.PerThread, nav_index: InternPool.Nav.I log.debug("updateNav {f}", .{nav.fqn.fmt(ip)}); } -pub fn updateLineNumber(spork8: *Spork8, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { +pub fn updateLineNumber(spork8: *Spork8, pt: Zcu.PerThread, inst: InternPool.TrackedInst.Index, line: u32) !void { _ = spork8; _ = pt; - _ = ti_id; + _ = inst; + _ = line; } pub fn deleteExport( diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 42111676dc8081af73ecea74ffe55fa6c5f426c2..4532b75a647013c1f15f19aebceae5a094b510ab 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -38,7 +38,6 @@ const Dwarf = @import("Dwarf.zig"); const InternPool = @import("../InternPool.zig"); const Zcu = @import("../Zcu.zig"); const codegen = @import("../codegen.zig"); -const dev = @import("../dev.zig"); const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const wasi_libc = @import("../libs/wasi_libc.zig"); @@ -1374,11 +1373,7 @@ pub const GlobalImport = extern struct { .__tls_base => @tagName(Unpacked.__tls_base), .__tls_size => @tagName(Unpacked.__tls_size), .object_global => |i| i.name(wasm).slice(wasm), - inline .uav_obj, .uav_exe => |i| std.mem.print( - buf, - "__anon_{d}", - .{@backingInt(i.key(wasm).*)}, - ) catch unreachable, + inline .uav_obj, .uav_exe => |i| std.mem.print(buf, "__anon_{d}", .{i}) catch unreachable, .nav_obj => |i| i.name(wasm), .nav_exe => |i| i.name(wasm), }; @@ -1997,11 +1992,7 @@ pub const ObjectDataImport = extern struct { .__heap_base => @tagName(.__heap_base), .__heap_end => @tagName(.__heap_end), .__wasm_first_page_end => @tagName(.__wasm_first_page_end), - inline .uav_exe, .uav_obj => |i| std.mem.print( - buf, - "__anon_{d}", - .{@backingInt(i.key(wasm).*)}, - ) catch unreachable, + inline .uav_exe, .uav_obj => |i| std.mem.print(buf, "__anon_{d}", .{i}) catch unreachable, inline .nav_exe, .nav_obj => |i| i.name(wasm), }; } @@ -3583,8 +3574,6 @@ pub fn updateFunc( func_index: InternPool.Index, any_mir: *const codegen.AnyMir, ) !void { - dev.check(.wasm_backend); - // This linker implementation only works with codegen backend `.stage2_wasm`. const mir = &any_mir.wasm; const zcu = pt.zcu; @@ -3736,11 +3725,11 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index } } -pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { +pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) link.Error!void { const comp = wasm.base.comp; const diags = &comp.link_diags; if (wasm.dwarf) |*dw| { - dw.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { + dw.updateLineNumber(pt.zcu, ti_id, line) catch |err| switch (err) { error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), }; diff --git a/src/main.zig b/src/main.zig index 4584c950abd7855d781492de48e25204a8de10c1..ea09d77f914ff36cea3d6e84738a84b0e189f093 100644 --- a/src/main.zig +++ b/src/main.zig @@ -36,7 +36,7 @@ const Module = @import("Module.zig"); test { _ = @import("codegen.zig"); - _ = @import("link/MappedFile.zig"); + _ = link.MappedFile; } const thread_stack_size = 60 << 20; diff --git a/src/print_zir.zig b/src/print_zir.zig index ea82384d5057241deca5637307e016633173c7a3..59d56c72c0657339ee3ede6fb99667b710586116 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1452,6 +1452,8 @@ const Writer = struct { self.parent_decl_node = struct_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; + try stream.print(":{d}:{d} ", .{ struct_decl.src_line + 1, struct_decl.src_column + 1 }); + const fields_hash = self.code.getAssociatedSrcHash(inst).?; try stream.print("hash({x}) ", .{&fields_hash}); @@ -1514,6 +1516,8 @@ const Writer = struct { self.parent_decl_node = union_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; + try stream.print(":{d}:{d} ", .{ union_decl.src_line + 1, union_decl.src_column + 1 }); + const fields_hash = self.code.getAssociatedSrcHash(inst).?; try stream.print("hash({x}) ", .{&fields_hash}); @@ -1590,6 +1594,8 @@ const Writer = struct { self.parent_decl_node = enum_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; + try stream.print(":{d}:{d} ", .{ enum_decl.src_line + 1, enum_decl.src_column + 1 }); + const fields_hash = self.code.getAssociatedSrcHash(inst).?; try stream.print("hash({x}) ", .{&fields_hash}); @@ -1637,6 +1643,8 @@ const Writer = struct { self.parent_decl_node = opaque_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; + try stream.print(":{d}:{d} ", .{ opaque_decl.src_line + 1, opaque_decl.src_column + 1 }); + try stream.print("{s}, ", .{@tagName(opaque_decl.name_strategy)}); try self.writeCaptures(stream, opaque_decl.captures, opaque_decl.capture_names); try stream.writeAll(", "); @@ -2216,10 +2224,10 @@ const Writer = struct { try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) }); }, } + try stream.print(":{d}:{d}", .{ decl.src_line + 1, decl.src_column + 1 }); + const src_hash = self.code.getAssociatedSrcHash(inst).?; - try stream.print(" line({d}) column({d}) hash({x})", .{ - decl.src_line, decl.src_column, &src_hash, - }); + try stream.print(" hash({x})", .{&src_hash}); { if (decl.type_body) |b| { diff --git a/src/target.zig b/src/target.zig index 5edf18e9581df4d786b515f6b5a2bfdf33e34561..5439374628c8f5d29c68a1e6aa55ad272e02e920 100644 --- a/src/target.zig +++ b/src/target.zig @@ -2,6 +2,7 @@ const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; +const dev = @import("dev.zig"); const Type = @import("Type.zig"); const AddressSpace = std.lang.AddressSpace; const Alignment = @import("InternPool.zig").Alignment; @@ -855,7 +856,10 @@ pub fn functionPointerMask(target: *const std.Target) ?u64 { pub fn supportsTailCall(target: *const std.Target, backend: std.lang.CompilerBackend) bool { switch (backend) { - .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), + .stage2_llvm => { + dev.check(.llvm_backend); + return @import("codegen/llvm.zig").supportsTailCall(target); + }, .stage2_c => return true, else => return false, } diff --git a/test/incremental/change_reify_struct_field_type b/test/incremental/change_reify_struct_field_type new file mode 100644 index 0000000000000000000000000000000000000000..bc157bd68364af5eda2d806a2d83b24479bdb843 --- /dev/null +++ b/test/incremental/change_reify_struct_field_type @@ -0,0 +1,22 @@ +#update=initial version +#file=main.zig +fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{struct { tag: Enum }}, &.{.{}})) Enum { + return s.field.tag; +} +pub fn main(init: std.process.Init) !void { + try std.Io.File.stdout().writeStreamingAll(init.io, @tagName(getEnum(.{ .field = .{ .tag = .foo } }))); +} +const Enum = enum { foo, bar }; +const std = @import("std"); +#expect_stdout="foo" +#update=change field type +#file=main.zig +fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{Enum}, &.{.{}})) Enum { + return s.field; +} +pub fn main(init: std.process.Init) !void { + try std.Io.File.stdout().writeStreamingAll(init.io, @tagName(getEnum(.{ .field = .bar }))); +} +const Enum = enum { foo, bar }; +const std = @import("std"); +#expect_stdout="bar" diff --git a/test/incremental/no_change_preserves_tag_names b/test/incremental/no_change_preserves_tag_names deleted file mode 100644 index 138bd919e3d176d11556c37341b51cfba3fa0440..0000000000000000000000000000000000000000 --- a/test/incremental/no_change_preserves_tag_names +++ /dev/null @@ -1,18 +0,0 @@ -#update=initial version -#file=main.zig -const std = @import("std"); -var some_enum: enum { first, second } = .first; -const io = std.Io.Threaded.global_single_threaded.io(); -pub fn main() !void { - try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); -} -#expect_stdout="first" -#update=no change -#file=main.zig -const std = @import("std"); -var some_enum: enum { first, second } = .first; -const io = std.Io.Threaded.global_single_threaded.io(); -pub fn main() !void { - try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); -} -#expect_stdout="first" diff --git a/test/incremental/tag_name b/test/incremental/tag_name new file mode 100644 index 0000000000000000000000000000000000000000..17954435c15786ddf98bbd576c1a2ae346ab14aa --- /dev/null +++ b/test/incremental/tag_name @@ -0,0 +1,27 @@ +#update=initial version +#file=main.zig +const std = @import("std"); +var some_enum: enum { first, second } = .first; +const io = std.Io.Threaded.global_single_threaded.io(); +pub fn main() !void { + try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); +} +#expect_stdout="first" +#update=no change +#file=main.zig +const std = @import("std"); +var some_enum: enum { first, second } = .first; +const io = std.Io.Threaded.global_single_threaded.io(); +pub fn main() !void { + try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); +} +#expect_stdout="first" +#update=swap fields +#file=main.zig +const std = @import("std"); +var some_enum: enum { second, first } = .first; +const io = std.Io.Threaded.global_single_threaded.io(); +pub fn main() !void { + try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); +} +#expect_stdout="first" diff --git a/test/src/Debugger.zig b/test/src/Debugger.zig index 6a8082248547bdaf7f24d70fcebb236ee5035733..5e28c48a7e3772817f46e4cdae7af21d157945b1 100644 --- a/test/src/Debugger.zig +++ b/test/src/Debugger.zig @@ -1,6 +1,7 @@ b: *std.Build, options: Options, root_step: *std.Build.Step, +test_matrix: []const TestTarget, pub const Options = struct { test_filters: []const []const u8, @@ -12,19 +13,20 @@ pub const Options = struct { skip_libc: bool, }; -pub const Target = struct { - resolved: std.Build.ResolvedTarget, +pub const TestTarget = struct { + target: std.Target.Query, optimize_mode: std.builtin.Optimize = .debug, link_libc: ?bool = null, single_threaded: ?bool = null, pic: ?bool = null, - test_name_suffix: []const u8, + linker: LinkerImpl, }; -pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { +pub const LinkerImpl = enum { default, old, new }; + +pub fn addTests(db: *Debugger) void { db.addLldbTest( "basic", - target, &.{ .{ .path = "basic.zig", @@ -179,10 +181,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "identifiers", - target, &.{ .{ .path = "identifiers.zig", @@ -214,10 +216,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "types", - target, &.{ .{ .path = "types.zig", @@ -282,10 +284,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "pointers", - target, &.{ .{ .path = "pointers.zig", @@ -419,10 +421,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "strings", - target, &.{ .{ .path = "strings.zig", @@ -495,10 +497,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "enums", - target, &.{ .{ .path = "enums.zig", @@ -557,10 +559,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{ .skip_new_linker = true }, // passes, but prints errors ); db.addLldbTest( "errors", - target, &.{ .{ .path = "errors.zig", @@ -627,10 +629,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{ .skip_new_linker = true }, ); db.addLldbTest( "optionals", - target, &.{ .{ .path = "optionals.zig", @@ -681,10 +683,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 2 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "unions", - target, &.{ .{ .path = "unions.zig", @@ -766,10 +768,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{ .skip_new_linker = true }, // passes, but prints errors ); db.addLldbTest( "storage", - target, &.{ .{ .path = "storage.zig", @@ -866,10 +868,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{ .skip_new_linker = true }, ); db.addLldbTest( "if_blocks", - target, &.{ .{ .path = "if_blocks.zig", @@ -908,10 +910,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "switch_blocks", - target, &.{ .{ .path = "switch_blocks.zig", @@ -953,10 +955,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "step_single_stmt_loops", - target, &.{ .{ .path = "step_single_stmt_loops.zig", @@ -1371,10 +1373,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) frame variable --show-all-children x \\(u32) x = 12 }, + .{}, ); db.addLldbTest( "inline_call", - target, &.{ .{ .path = "root0.zig", @@ -1944,10 +1946,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\ frame #1: inline_call`m1pfi(m1pai=89) at mod1.zig:23:15 \\ frame #2: inline_call`root0.main at root0.zig:41:15 }, + .{ .skip_new_linker = true }, ); db.addLldbTest( "link_object", - target, &.{ .{ .path = "main.zig", @@ -1983,10 +1985,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 2 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{}, ); db.addLldbTest( "hash_map", - target, &.{ .{ .path = "main.zig", @@ -2052,10 +2054,10 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{ .skip_new_linker = true }, ); db.addLldbTest( "multi_array_list", - target, &.{ .{ .path = "main.zig", @@ -2306,22 +2308,26 @@ pub fn addTestsForTarget(db: *Debugger, target: *const Target) void { \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, + .{ .skip_new_linker = true }, ); } const File = struct { import: ?[]const u8 = null, path: []const u8, source: []const u8 }; +const TestOptions = struct { + skip_new_linker: bool = false, +}; + fn addGdbTest( db: *Debugger, name: []const u8, - target: *const Target, files: []const File, commands: []const u8, expected_output: []const []const u8, + options: TestOptions, ) void { db.addTest( name, - target, files, &.{}, &.{ @@ -2331,24 +2337,22 @@ fn addGdbTest( }, "set remotetimeout 0", commands, - &.{ - "--args", - }, + &.{"--args"}, expected_output, + options, ); } fn addLldbTest( db: *Debugger, name: []const u8, - target: *const Target, files: []const File, commands: []const u8, expected_output: []const []const u8, + options: TestOptions, ) void { db.addTest( name, - target, files, &.{.{ "LANG", "C.UTF-8" }}, // affects output formatting &.{ @@ -2358,10 +2362,9 @@ fn addLldbTest( }, "settings set plugin.process.gdb-remote.packet-timeout 0", commands, - &.{ - "--", - }, + &.{"--"}, expected_output, + options, ); } @@ -2373,7 +2376,6 @@ const success = 99; fn addTest( db: *Debugger, name: []const u8, - target: *const Target, files: []const File, env: []const struct { []const u8, []const u8 }, db_argv1: []const []const u8, @@ -2381,57 +2383,85 @@ fn addTest( commands: []const u8, db_argv2: []const []const u8, expected_output: []const []const u8, + options: TestOptions, ) void { if (db.options.test_filters.len > 0) { for (db.options.test_filters) |test_filter| { if (std.mem.find(u8, name, test_filter) != null) break; } else return; } - if (db.options.test_target_filters.len > 0) { - const triple_txt = target.resolved.query.zigTriple(db.b.allocator) catch @panic("OOM"); - for (db.options.test_target_filters) |filter| { - if (std.mem.find(u8, triple_txt, filter) != null) break; - } else return; - } - const files_wf = db.b.addWriteFiles(); - const mod = db.b.createModule(.{ - .target = target.resolved, - .root_source_file = files_wf.add(files[0].path, files[0].source), - .optimize = target.optimize_mode, - .link_libc = target.link_libc, - .single_threaded = target.single_threaded, - .pic = target.pic, - .strip = false, - }); + const wf = db.b.addWriteFiles(); + const root_source_file = wf.add(files[0].path, files[0].source); + var imports: std.array_hash_map.String(*std.Build.Module) = .empty; for (files[1..]) |file| { - const path = files_wf.add(file.path, file.source); - if (file.import) |import| mod.addImport(import, db.b.createModule(.{ + const path = wf.add(file.path, file.source); + if (file.import) |import| imports.putNoClobber(db.b.allocator, import, db.b.createModule(.{ .root_source_file = path, - })); + })) catch @panic("OOM"); } - - const exe = db.b.addExecutable(.{ - .name = name, - .root_module = mod, - .use_llvm = false, - .use_lld = false, - }); - - const commands_wf = db.b.addWriteFiles(); - const run = std.Build.Step.Run.create(db.b, db.b.fmt("run {s} {s}", .{ name, target.test_name_suffix })); - for (env) |env_var| run.setEnvironmentVariable(env_var[0], env_var[1]); - run.addArgs(db_argv1); - run.addFileArg(commands_wf.add( + const commands_file = wf.add( db.b.fmt("{s}.cmd", .{name}), db.b.fmt("{s}\n\n{s}\n\nquit {d}\n", .{ db_commands, commands, success }), - )); - run.addArgs(db_argv2); - run.addArtifactArg(exe); - for (expected_output) |expected| run.addCheck(.{ .expect_stdout_match = db.b.fmt("{s}\n", .{expected}) }); - run.addCheck(.{ .expect_term = .{ .exited = success } }); - run.setStdIn(.{ .bytes = "" }); - db.root_step.dependOn(&run.step); + ); + + for (db.test_matrix) |test_target| { + if (options.skip_new_linker and test_target.linker == .new) continue; + + const resolved_target = db.b.resolveTargetQuery(test_target.target); + + const target_str = db.b.fmt("{s}{s}{s}", .{ + resolved_target.query.zigTriple(db.b.allocator) catch @panic("OOM"), + switch (test_target.linker) { + .default, .old => "", + .new => "-new-linker", + }, + if (test_target.pic == true) "-pic" else "", + }); + + if (db.options.test_target_filters.len > 0) { + for (db.options.test_target_filters) |filter| { + if (std.mem.find(u8, target_str, filter) != null) break; + } else continue; + } + + const mod = db.b.createModule(.{ + .target = resolved_target, + .root_source_file = root_source_file, + .optimize = test_target.optimize_mode, + .link_libc = test_target.link_libc, + .single_threaded = test_target.single_threaded, + .pic = test_target.pic, + .strip = false, + }); + for (imports.keys(), imports.values()) |import_name, import_mod| + mod.addImport(import_name, import_mod); + + const exe = db.b.addExecutable(.{ + .name = name, + .root_module = mod, + .use_llvm = false, + .use_lld = false, + }); + exe.use_new_linker = switch (test_target.linker) { + .default => null, + .old => false, + .new => true, + }; + + const run = std.Build.Step.Run.create(db.b, db.b.fmt("run {s} {s}", .{ name, target_str })); + for (env) |env_var| run.setEnvironmentVariable(env_var[0], env_var[1]); + run.addArgs(db_argv1); + run.addFileArg(commands_file); + run.addArgs(db_argv2); + run.addArtifactArg(exe); + for (expected_output) |expected| run.addCheck(.{ + .expect_stdout_match = db.b.fmt("{s}\n", .{expected}), + }); + run.addCheck(.{ .expect_term = .{ .exited = success } }); + run.setStdIn(.{ .bytes = "" }); + db.root_step.dependOn(&run.step); + } } const Debugger = @This(); diff --git a/test/src/ErrorTrace.zig b/test/src/ErrorTrace.zig index 15b8e7f46dafc0bf4c155fdf66cf85e7896848cd..43ca8776026c82022f3d3de03160113e88f286a8 100644 --- a/test/src/ErrorTrace.zig +++ b/test/src/ErrorTrace.zig @@ -36,6 +36,7 @@ pub const CaseParameters = struct { optimize: OptimizeMode = .debug, use_llvm: ?bool = null, use_lld: ?bool = null, + use_new_linker: ?bool = null, // This is intended for targets that, for any reason, shouldn't be run as part of a normal test // invocation. This could be because of a slow backend, requiring a newer LLVM version, being @@ -256,6 +257,14 @@ pub const param_sets = [_]CaseParameters{ .abi = .none, }, }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .use_new_linker = true, + }, .{ .target = .{ .cpu_arch = .x86_64, @@ -265,6 +274,15 @@ pub const param_sets = [_]CaseParameters{ .use_llvm = true, .use_lld = true, }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .use_llvm = true, + .use_new_linker = true, + }, .{ .target = .{ .cpu_arch = .x86_64, @@ -439,18 +457,23 @@ pub fn addCase(self: *ErrorTrace, case: Case) void { }; const backend_string = if (params.use_llvm == true) - "-llvm" + " llvm" else if (params.use_llvm == false) - "-selfhosted" + " selfhosted" else ""; - const annotated_case_name = b.fmt("check {s} ({s}{s}{t}{s})", .{ + const annotated_case_name = b.fmt("check {s} ({s} {t}{s}{s})", .{ case.name, - triple orelse "", - if (triple != null) " " else "", + triple orelse "native", params.optimize, backend_string, + if (params.use_new_linker == true) + " new_linker" + else if (params.use_lld == true) + " lld" + else + "", }); if (self.options.test_filters.len > 0) { for (self.options.test_filters) |test_filter| { @@ -472,6 +495,7 @@ pub fn addCase(self: *ErrorTrace, case: Case) void { .use_llvm = params.use_llvm, .use_lld = params.use_lld, }); + exe.use_new_linker = params.use_new_linker; exe.bundle_ubsan_rt = false; const run = b.addRunArtifact(exe); diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index 51d5355fa00bd85d5f43a0cba2b59bb82febbd1f..e6897214daf5d11e642855d30853c4f58d076a10 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -38,6 +38,7 @@ pub const CaseParameters = struct { link_libc: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, + use_new_linker: ?bool = null, pie: ?bool = null, /// To enable this coverage, one of two things needs to happen: /// * The compiler needs to gain the ability to strip only debug info (not symbols) @@ -752,6 +753,14 @@ pub const param_sets = [_]CaseParameters{ .abi = .none, }, }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .use_new_linker = true, + }, .{ .target = .{ .cpu_arch = .x86_64, @@ -761,6 +770,15 @@ pub const param_sets = [_]CaseParameters{ .use_llvm = true, .use_lld = true, }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .use_llvm = true, + .use_new_linker = true, + }, .{ .target = .{ .cpu_arch = .x86_64, @@ -1175,9 +1193,14 @@ fn addCaseInstance( const annotated_case_name = b.fmt("check {s} ({s}{s}{s}{s}{s}{s}{s}{s}{s})", .{ name, - triple orelse "", - if (triple != null) " " else "", + triple orelse "native", backend_string, + if (params.use_new_linker == true) + " new_linker" + else if (params.use_lld == true) + " lld" + else + "", if (params.pie == true) " pie" else "", if (params.link_libc == true) " libc" else "", if (params.linkage) |linkage| switch (linkage) { @@ -1210,6 +1233,7 @@ fn addCaseInstance( .use_llvm = params.use_llvm, .use_lld = params.use_lld, }); + exe.use_new_linker = params.use_new_linker; exe.linkage = params.linkage; exe.pie = params.pie; exe.bundle_ubsan_rt = false; diff --git a/test/tests.zig b/test/tests.zig index 41e54a82fc7020b6b728f1a6ade49592c1332d9a..7ecc7d93e22f5ba209ce8961c21c32a4f8112893 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2303,6 +2303,45 @@ const incremental_targets = &[_]IncrementalTarget{ }, }; +const debugger_matrix: []const DebuggerContext.TestTarget = &.{ + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .pic = false, + .linker = .old, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .pic = true, + .linker = .old, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .pic = false, + .linker = .new, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .pic = true, + .linker = .new, + }, +}; + fn compatible32bitArch(host: *const std.Target) ?std.Target.Cpu.Arch { return switch (host.os.tag) { .freebsd => switch (host.cpu.arch) { @@ -3323,25 +3362,9 @@ pub fn addDebuggerTests(b: *std.Build, options: DebuggerContext.Options) ?*Step .b = b, .options = options, .root_step = step, + .test_matrix = debugger_matrix, }; - context.addTestsForTarget(&.{ - .resolved = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }), - .pic = false, - .test_name_suffix = "x86_64-linux", - }); - context.addTestsForTarget(&.{ - .resolved = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }), - .pic = true, - .test_name_suffix = "x86_64-linux-pic", - }); + context.addTests(); return step; } @@ -3416,16 +3439,17 @@ pub fn addIncrementalTests( if (options.skip_llvm and test_target.backend == .llvm) continue; - const triple_txt = resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"); + const target_str = b.fmt("{s}-{t}", .{ + resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"), + test_target.backend, + }); if (options.test_target_filters.len > 0) { for (options.test_target_filters) |filter| { - if (std.mem.find(u8, triple_txt, filter) != null) break; + if (std.mem.find(u8, target_str, filter) != null) break; } else continue; } - const target_str = b.fmt("{s}-{t}", .{ triple_txt, test_target.backend }); - const run = b.addRunArtifact(incr_check); run.setName(b.fmt("incr-check {s} '{s}'", .{ target_str, entry.basename })); diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 49a2fae873d33b40a9eaf573fda416f6085840eb..21c24e9048203c80a5562f6c3b47fce31cb2e80d 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -17,6 +17,7 @@ const usage = \\Debug Options: \\ --preserve-tmp \\ --debug-log foo + \\ --debug-link-snapshot ; pub const std_options: std.Options = .{ @@ -60,6 +61,7 @@ pub fn main(init: std.process.Init) !void { var quiet: bool = false; var debug_log_args: std.ArrayList([]const u8) = .empty; + var debug_link_snapshot = false; var arg_it = try init.minimal.args.iterateAllocator(arena); _ = arg_it.skip(); @@ -77,6 +79,8 @@ pub fn main(init: std.process.Init) !void { arena, arg_it.next() orelse badUsage("expected arg after --debug-log", .{}), ); + } else if (std.mem.eql(u8, arg, "--debug-link-snapshot")) { + debug_link_snapshot = true; } else if (std.mem.eql(u8, arg, "--preserve-tmp")) { preserve_tmp = true; } else if (std.mem.eql(u8, arg, "-fqemu")) { @@ -173,6 +177,9 @@ pub fn main(init: std.process.Init) !void { for (debug_log_args.items) |arg| { try child_args.appendSlice(arena, &.{ "--debug-log", arg }); } + if (debug_link_snapshot) { + try child_args.append(arena, "--debug-link-snapshot"); + } for (case.modules) |mod| { try child_args.appendSlice(arena, &.{ "--dep", mod.name }); }