From 6b185e94b95932ff7ac0cbdf9efeea5bfc2e4a6b Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Wed, 11 Feb 2026 15:05:33 +0000 Subject: [PATCH] compiler: make Dwarf and self-hosted x86_64 happy Introduces a small abstraction, `link.DebugConstPool`, to deal with lowering type/value information into debug info when it may not be known until type resolution (which in some cases will *never* happen). It is currently only used by self-hosted DWARF logic, but it will also be of use to the LLVM backend (which is my next focus). --- src/Compilation.zig | 75 +- src/Zcu/PerThread.zig | 492 +---------- src/codegen.zig | 37 +- src/link.zig | 46 +- src/link/DebugConstPool.zig | 287 +++++++ src/link/Dwarf.zig | 1550 +++++++++++++++-------------------- src/link/Elf.zig | 3 +- src/link/Elf/ZigObject.zig | 3 +- 8 files changed, 1029 insertions(+), 1464 deletions(-) create mode 100644 src/link/DebugConstPool.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index ab5d067a52..f28fb09df0 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -956,8 +956,7 @@ pub const RcSourceFile = struct { const Job = union(enum) { /// Given the generated AIR for a function, put it onto the code generation queue. - /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that - /// all types are resolved before the linker task is queued. + /// MLUGG TODO: because type resolution is no longer necessary, we can remove this now /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately. /// Before queueing this `Job`, increase the estimated total item count for both /// `comp.zcu.?.codegen_prog_node` and `comp.link_prog_node`. @@ -967,17 +966,10 @@ const Job = union(enum) { air: Air, }, /// Queue a `link.ZcuTask` to emit this non-function `Nav` into the output binary. - /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that - /// all types are resolved before the linker task is queued. + /// MLUGG TODO: because type resolution is no longer necessary, we can remove this now /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. link_nav: InternPool.Nav.Index, - /// Queue a `link.ZcuTask` to emit debug information for this container type. - /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that - /// all types are resolved before the linker task is queued. - /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. - /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. - link_type: InternPool.Index, /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. update_line_number: InternPool.TrackedInst.Index, /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed. @@ -5058,26 +5050,6 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v var owned_air: ?Air = func.air; defer if (owned_air) |*air| air.deinit(gpa); - { - const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); - defer pt.deactivate(); - pt.resolveAirTypesForCodegen(&owned_air.?) catch |err| switch (err) { - error.OutOfMemory, - error.Canceled, - => |e| return e, - - error.AnalysisFail => { - // Type resolution failed, making codegen of this function impossible. This - // is a transitive failure, but it doesn't need recording, because this - // function semantically depends on the failed type, so when it is changed - // the function will be updated. - zcu.codegen_prog_node.completeOne(); - comp.link_prog_node.completeOne(); - return; - }, - }; - } - // Some linkers need to refer to the AIR. In that case, the linker is not running // concurrently, so we'll just keep ownership of the AIR for ourselves instead of // letting the codegen job destroy it. @@ -5101,51 +5073,10 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v } } assert(nav.status == .fully_resolved); - { - const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); - defer pt.deactivate(); - pt.resolveValueTypesForCodegen(zcu.navValue(nav_index)) catch |err| switch (err) { - error.OutOfMemory, - error.Canceled, - => |e| return e, - - error.AnalysisFail => { - // Type resolution failed, making codegen of this `Nav` impossible. This is - // a transitive failure, but it doesn't need recording, because this `Nav` - // semantically depends on the failed type, so when it is changed the value - // of the `Nav` will be updated. - comp.link_prog_node.completeOne(); - return; - }, - }; - } try comp.link_queue.enqueueZcu(comp, tid, .{ .link_nav = nav_index }); }, - .link_type => |ty| { - const zcu = comp.zcu.?; - if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa); - { - const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); - defer pt.deactivate(); - pt.resolveTypeForCodegen(.fromInterned(ty)) catch |err| switch (err) { - error.OutOfMemory, - error.Canceled, - => |e| return e, - - error.AnalysisFail => { - // Type resolution failed, making codegen of this type impossible. This is - // a transitive failure, but it doesn't need recording, because this type - // semantically depends on the failed type, so when it is changed the type - // will be updated appropriately. - comp.link_prog_node.completeOne(); - return; - }, - }; - } - try comp.link_queue.enqueueZcu(comp, tid, .{ .link_type = ty }); - }, .update_line_number => |tracked_inst| { - try comp.link_queue.enqueueZcu(comp, tid, .{ .update_line_number = tracked_inst }); + try comp.link_queue.enqueueZcu(comp, tid, .{ .debug_update_line_number = tracked_inst }); }, .analyze_unit => |unit| { const tracy_trace = traceNamed(@src(), "analyze_unit"); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 194b6fd283..babf66ee72 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -998,10 +998,10 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu try sema.flushExports(); } -/// Ensures that the layout of the given `struct` or `union` type is fully up-to-date, performing -/// re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!) or union. Returns -/// `error.AnalysisFail` if an analysis error is encountered during type resolution; the caller is -/// free to ignore this, since the error is already registered. +/// Ensures that the layout of the given `struct`, `union`, or `enum` type is fully up-to-date, +/// performing re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!), union, or +/// enum type. Returns `error.AnalysisFail` if an analysis error is encountered during type +/// resolution; the caller is free to ignore this, since the error is already registered. pub fn ensureTypeLayoutUpToDate( pt: Zcu.PerThread, ty: Type, @@ -1012,7 +1012,8 @@ pub fn ensureTypeLayoutUpToDate( defer tracy.end(); const zcu = pt.zcu; - const gpa = zcu.gpa; + const comp = zcu.comp; + const gpa = comp.gpa; const anal_unit: AnalUnit = .wrap(.{ .type_layout = ty.toIntern() }); @@ -1021,7 +1022,7 @@ pub fn ensureTypeLayoutUpToDate( assert(!zcu.analysis_in_progress.contains(anal_unit)); const was_outdated = zcu.clearOutdatedState(anal_unit) or - zcu.intern_pool.setWantTypeLayout(zcu.comp.io, ty.toIntern()); + zcu.intern_pool.setWantTypeLayout(comp.io, ty.toIntern()); if (was_outdated) { // `was_outdated` is true in the initial update, so this isn't a `dev.check`. @@ -1038,7 +1039,7 @@ pub fn ensureTypeLayoutUpToDate( return; } - if (zcu.comp.debugIncremental()) { + if (comp.debugIncremental()) { const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); info.last_update_gen = zcu.generation; info.deps.clearRetainingCapacity(); @@ -1078,15 +1079,17 @@ pub fn ensureTypeLayoutUpToDate( .@"union" => Sema.type_resolution.resolveUnionLayout(&sema, ty), else => unreachable, }; - result catch |err| switch (err) { - error.AnalysisFail => { + const new_success: bool = if (result) s: { + break :s true; + } else |err| switch (err) { + error.AnalysisFail => success: { if (!zcu.failed_analysis.contains(anal_unit)) { // If this unit caused the error, it would have an entry in `failed_analysis`. // Since it does not, this must be a transitive failure. try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); } - return error.AnalysisFail; + break :success false; }, error.OutOfMemory, error.Canceled, @@ -1098,6 +1101,15 @@ pub fn ensureTypeLayoutUpToDate( sema.flushExports() catch |err| switch (err) { error.OutOfMemory => |e| return e, }; + + // We don't need to `markDependeeOutdated`/`markPoDependeeUpToDate` here, because we already + // marked the layout as outdated at the top of this function. However, we do need to tell the + // debug info logic in the backend about this type. + comp.link_prog_node.increaseEstimatedTotalItems(1); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .debug_update_container_type = .{ + .ty = ty.toIntern(), + .success = new_success, + } }); } /// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis @@ -4102,463 +4114,3 @@ fn printVerboseAir( try air.write(w, pt, liveness); try w.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)}); } - -// MLUGG TODO: these functions are all blatant hacks. See if I can remove them! -pub fn resolveTypeForCodegen(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - if (ty.isGenericPoison()) return; - switch (ty.zigTypeTag(zcu)) { - .type, - .void, - .bool, - .noreturn, - .int, - .float, - .error_set, - .@"opaque", - .comptime_float, - .comptime_int, - .undefined, - .null, - .enum_literal, - => {}, - - .frame, .@"anyframe" => @panic("TODO resolveTypeForCodegen async frames"), - - .optional => try pt.resolveTypeForCodegen(ty.childType(zcu)), - .error_union => try pt.resolveTypeForCodegen(ty.errorUnionPayload(zcu)), - .pointer => try pt.resolveTypeForCodegen(ty.childType(zcu)), - .array => try pt.resolveTypeForCodegen(ty.childType(zcu)), - .vector => try pt.resolveTypeForCodegen(ty.childType(zcu)), - - .@"fn" => { - const info = zcu.typeToFunc(ty).?; - for (0..info.param_types.len) |i| { - const param_ty = info.param_types.get(ip)[i]; - try pt.resolveTypeForCodegen(.fromInterned(param_ty)); - } - try pt.resolveTypeForCodegen(.fromInterned(info.return_type)); - }, - - .@"struct" => switch (ip.indexToKey(ty.toIntern())) { - .struct_type => try pt.ensureTypeLayoutUpToDate(ty, null), - .tuple_type => |tuple| for (0..tuple.types.len) |i| { - const field_is_comptime = tuple.values.get(ip)[i] != .none; - if (field_is_comptime) continue; - const field_ty = tuple.types.get(ip)[i]; - try pt.resolveTypeForCodegen(.fromInterned(field_ty)); - }, - else => unreachable, - }, - - .@"union" => try pt.ensureTypeLayoutUpToDate(ty, null), - .@"enum" => try pt.ensureTypeLayoutUpToDate(ty, null), - } -} -pub fn resolveValueTypesForCodegen(pt: Zcu.PerThread, val: Value) Zcu.SemaError!void { - const zcu = pt.zcu; - const ty: Type = switch (val.typeOf(zcu).toIntern()) { - .type_type => if (val.isUndef(zcu)) { - return; - } else val.toType(), - else => |ty| .fromInterned(ty), - }; - return pt.resolveTypeForCodegen(ty); -} -pub fn resolveAirTypesForCodegen(pt: Zcu.PerThread, air: *const Air) Zcu.SemaError!void { - return pt.resolveBodyTypesForCodegen(air, air.getMainBody()); -} -fn resolveBodyTypesForCodegen(pt: Zcu.PerThread, air: *const Air, body: []const Air.Inst.Index) Zcu.SemaError!void { - const zcu = pt.zcu; - const tags = air.instructions.items(.tag); - const datas = air.instructions.items(.data); - for (body) |inst| { - const data = datas[@intFromEnum(inst)]; - switch (tags[@intFromEnum(inst)]) { - .inferred_alloc, .inferred_alloc_comptime => unreachable, - - .arg => try pt.resolveTypeForCodegen(data.arg.ty.toType()), - - .add, - .add_safe, - .add_optimized, - .add_wrap, - .add_sat, - .sub, - .sub_safe, - .sub_optimized, - .sub_wrap, - .sub_sat, - .mul, - .mul_safe, - .mul_optimized, - .mul_wrap, - .mul_sat, - .div_float, - .div_float_optimized, - .div_trunc, - .div_trunc_optimized, - .div_floor, - .div_floor_optimized, - .div_exact, - .div_exact_optimized, - .rem, - .rem_optimized, - .mod, - .mod_optimized, - .max, - .min, - .bit_and, - .bit_or, - .shr, - .shr_exact, - .shl, - .shl_exact, - .shl_sat, - .xor, - .cmp_lt, - .cmp_lt_optimized, - .cmp_lte, - .cmp_lte_optimized, - .cmp_eq, - .cmp_eq_optimized, - .cmp_gte, - .cmp_gte_optimized, - .cmp_gt, - .cmp_gt_optimized, - .cmp_neq, - .cmp_neq_optimized, - .bool_and, - .bool_or, - .store, - .store_safe, - .set_union_tag, - .array_elem_val, - .slice_elem_val, - .ptr_elem_val, - .memset, - .memset_safe, - .memcpy, - .memmove, - .atomic_store_unordered, - .atomic_store_monotonic, - .atomic_store_release, - .atomic_store_seq_cst, - .legalize_vec_elem_val, - => { - try pt.resolveRefTypesForCodegen(data.bin_op.lhs); - try pt.resolveRefTypesForCodegen(data.bin_op.rhs); - }, - - .not, - .bitcast, - .clz, - .ctz, - .popcount, - .byte_swap, - .bit_reverse, - .abs, - .load, - .fptrunc, - .fpext, - .intcast, - .intcast_safe, - .trunc, - .optional_payload, - .optional_payload_ptr, - .optional_payload_ptr_set, - .wrap_optional, - .unwrap_errunion_payload, - .unwrap_errunion_err, - .unwrap_errunion_payload_ptr, - .unwrap_errunion_err_ptr, - .errunion_payload_ptr_set, - .wrap_errunion_payload, - .wrap_errunion_err, - .struct_field_ptr_index_0, - .struct_field_ptr_index_1, - .struct_field_ptr_index_2, - .struct_field_ptr_index_3, - .get_union_tag, - .slice_len, - .slice_ptr, - .ptr_slice_len_ptr, - .ptr_slice_ptr_ptr, - .array_to_slice, - .int_from_float, - .int_from_float_optimized, - .int_from_float_safe, - .int_from_float_optimized_safe, - .float_from_int, - .splat, - .error_set_has_value, - .addrspace_cast, - .c_va_arg, - .c_va_copy, - => { - try pt.resolveTypeForCodegen(data.ty_op.ty.toType()); - try pt.resolveRefTypesForCodegen(data.ty_op.operand); - }, - - .alloc, - .ret_ptr, - .c_va_start, - => try pt.resolveTypeForCodegen(data.ty), - - .ptr_add, - .ptr_sub, - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - .slice, - .slice_elem_ptr, - .ptr_elem_ptr, - => { - const bin = air.extraData(Air.Bin, data.ty_pl.payload).data; - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - try pt.resolveRefTypesForCodegen(bin.lhs); - try pt.resolveRefTypesForCodegen(bin.rhs); - }, - - .block, - .loop, - => { - const block = air.unwrapBlock(inst); - try pt.resolveTypeForCodegen(block.ty); - try pt.resolveBodyTypesForCodegen(air, block.body); - }, - - .dbg_inline_block => { - const block = air.unwrapDbgBlock(inst); - try pt.resolveTypeForCodegen(block.ty); - try pt.resolveBodyTypesForCodegen(air, block.body); - }, - - .sqrt, - .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .floor, - .ceil, - .round, - .trunc_float, - .neg, - .neg_optimized, - .is_null, - .is_non_null, - .is_null_ptr, - .is_non_null_ptr, - .is_err, - .is_non_err, - .is_err_ptr, - .is_non_err_ptr, - .ret, - .ret_safe, - .ret_load, - .is_named_enum_value, - .tag_name, - .error_name, - .cmp_lt_errors_len, - .c_va_end, - .set_err_return_trace, - => try pt.resolveRefTypesForCodegen(data.un_op), - - .br, .switch_dispatch => try pt.resolveRefTypesForCodegen(data.br.operand), - - .cmp_vector, - .cmp_vector_optimized, - => { - const extra = air.extraData(Air.VectorCmp, data.ty_pl.payload).data; - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - try pt.resolveRefTypesForCodegen(extra.lhs); - try pt.resolveRefTypesForCodegen(extra.rhs); - }, - - .reduce, - .reduce_optimized, - => try pt.resolveRefTypesForCodegen(data.reduce.operand), - - .struct_field_ptr, - .struct_field_val, - => { - const extra = air.extraData(Air.StructField, data.ty_pl.payload).data; - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - try pt.resolveRefTypesForCodegen(extra.struct_operand); - }, - - .shuffle_one => { - const unwrapped = air.unwrapShuffleOne(zcu, inst); - try pt.resolveTypeForCodegen(unwrapped.result_ty); - try pt.resolveRefTypesForCodegen(unwrapped.operand); - for (unwrapped.mask) |m| switch (m.unwrap()) { - .elem => {}, - .value => |val| try pt.resolveValueTypesForCodegen(.fromInterned(val)), - }; - }, - - .shuffle_two => { - const unwrapped = air.unwrapShuffleTwo(zcu, inst); - try pt.resolveTypeForCodegen(unwrapped.result_ty); - try pt.resolveRefTypesForCodegen(unwrapped.operand_a); - try pt.resolveRefTypesForCodegen(unwrapped.operand_b); - // No values to check because there are no comptime-known values other than undef - }, - - .cmpxchg_weak, - .cmpxchg_strong, - => { - const extra = air.extraData(Air.Cmpxchg, data.ty_pl.payload).data; - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - try pt.resolveRefTypesForCodegen(extra.ptr); - try pt.resolveRefTypesForCodegen(extra.expected_value); - try pt.resolveRefTypesForCodegen(extra.new_value); - }, - - .aggregate_init => { - const ty = data.ty_pl.ty.toType(); - const elems_len: usize = @intCast(ty.arrayLen(zcu)); - const elems: []const Air.Inst.Ref = @ptrCast(air.extra.items[data.ty_pl.payload..][0..elems_len]); - try pt.resolveTypeForCodegen(ty); - if (ty.zigTypeTag(zcu) == .@"struct") { - for (elems, 0..) |elem, elem_idx| { - if (ty.structFieldIsComptime(elem_idx, zcu)) continue; - try pt.resolveRefTypesForCodegen(elem); - } - } else { - for (elems) |elem| { - try pt.resolveRefTypesForCodegen(elem); - } - } - }, - - .union_init => { - const extra = air.extraData(Air.UnionInit, data.ty_pl.payload).data; - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - try pt.resolveRefTypesForCodegen(extra.init); - }, - - .field_parent_ptr => { - const extra = air.extraData(Air.FieldParentPtr, data.ty_pl.payload).data; - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - try pt.resolveRefTypesForCodegen(extra.field_ptr); - }, - - .atomic_load => try pt.resolveRefTypesForCodegen(data.atomic_load.ptr), - - .prefetch => try pt.resolveRefTypesForCodegen(data.prefetch.ptr), - - .runtime_nav_ptr => try pt.resolveTypeForCodegen(.fromInterned(data.ty_nav.ty)), - - .select, - .mul_add, - .legalize_vec_store_elem, - => { - const bin = air.extraData(Air.Bin, data.pl_op.payload).data; - try pt.resolveRefTypesForCodegen(data.pl_op.operand); - try pt.resolveRefTypesForCodegen(bin.lhs); - try pt.resolveRefTypesForCodegen(bin.rhs); - }, - - .atomic_rmw => { - const extra = air.extraData(Air.AtomicRmw, data.pl_op.payload).data; - try pt.resolveRefTypesForCodegen(data.pl_op.operand); - try pt.resolveRefTypesForCodegen(extra.operand); - }, - - .call, - .call_always_tail, - .call_never_tail, - .call_never_inline, - => { - const call = air.unwrapCall(inst); - try pt.resolveRefTypesForCodegen(call.callee); - for (call.args) |arg| try pt.resolveRefTypesForCodegen(arg); - }, - - .dbg_var_ptr, - .dbg_var_val, - .dbg_arg_inline, - => try pt.resolveRefTypesForCodegen(data.pl_op.operand), - - .@"try", .try_cold => { - const @"try" = air.unwrapTry(inst); - try pt.resolveRefTypesForCodegen(@"try".error_union); - try pt.resolveBodyTypesForCodegen(air, @"try".else_body); - }, - - .try_ptr, .try_ptr_cold => { - const try_ptr = air.unwrapTryPtr(inst); - try pt.resolveTypeForCodegen(try_ptr.error_union_payload_ptr_ty.toType()); - try pt.resolveRefTypesForCodegen(try_ptr.error_union_ptr); - try pt.resolveBodyTypesForCodegen(air, try_ptr.else_body); - }, - - .cond_br => { - const cond_br = air.unwrapCondBr(inst); - try pt.resolveRefTypesForCodegen(cond_br.condition); - try pt.resolveBodyTypesForCodegen(air, cond_br.then_body); - try pt.resolveBodyTypesForCodegen(air, cond_br.else_body); - }, - - .switch_br, .loop_switch_br => { - const switch_br = air.unwrapSwitch(inst); - try pt.resolveRefTypesForCodegen(switch_br.operand); - var it = switch_br.iterateCases(); - while (it.next()) |case| { - for (case.items) |item| { - try pt.resolveRefTypesForCodegen(item); - } - for (case.ranges) |range| { - try pt.resolveRefTypesForCodegen(range[0]); - try pt.resolveRefTypesForCodegen(range[1]); - } - try pt.resolveBodyTypesForCodegen(air, case.body); - } - try pt.resolveBodyTypesForCodegen(air, it.elseBody()); - }, - - .assembly => { - const @"asm" = air.unwrapAsm(inst); - try pt.resolveTypeForCodegen(data.ty_pl.ty.toType()); - for (@"asm".outputs) |output| if (output != .none) try pt.resolveRefTypesForCodegen(output); - for (@"asm".inputs) |input| if (input != .none) try pt.resolveRefTypesForCodegen(input); - }, - - .legalize_compiler_rt_call => { - const compiler_rt_call = air.unwrapCompilerRtCall(inst); - for (compiler_rt_call.args) |arg| try pt.resolveRefTypesForCodegen(arg); - }, - - .trap, - .breakpoint, - .ret_addr, - .frame_addr, - .unreach, - .wasm_memory_size, - .wasm_memory_grow, - .work_item_id, - .work_group_size, - .work_group_id, - .dbg_stmt, - .dbg_empty_stmt, - .err_return_trace, - .save_err_return_trace_index, - .repeat, - => {}, - } - } -} -fn resolveRefTypesForCodegen(pt: Zcu.PerThread, ref: Air.Inst.Ref) Zcu.SemaError!void { - const ip_index = ref.toInterned() orelse { - // `ref` refers to a prior instruction, which we already did the resolution for. - return; - }; - return pt.resolveValueTypesForCodegen(.fromInterned(ip_index)); -} diff --git a/src/codegen.zig b/src/codegen.zig index 45b70dee2a..9608095dc6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -700,7 +700,14 @@ fn lowerPtr( }; return lowerPtr(bin_file, pt, src_loc, field.base, w, reloc_parent, offset + field_off); }, - .arr_elem, .comptime_field, .comptime_alloc => unreachable, + .arr_elem => |arr_elem| { + const base_ptr_ty = Value.fromInterned(arr_elem.base).typeOf(zcu); + assert(base_ptr_ty.ptrSize(zcu) == .many); + const elem_size = base_ptr_ty.childType(zcu).abiSize(zcu); + return lowerPtr(bin_file, pt, src_loc, arr_elem.base, w, reloc_parent, offset + elem_size * arr_elem.index); + }, + .comptime_alloc => unreachable, + .comptime_field => unreachable, }; } @@ -781,9 +788,8 @@ fn lowerNavRef( const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); const is_obj = lf.comp.config.output_mode == .Obj; const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip)); - const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; - if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) { + if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) and ip.getNav(nav_index).getExtern(ip) == null) { try w.splatByteAll(0xaa, ptr_width_bytes); return; } @@ -795,7 +801,7 @@ fn lowerNavRef( dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; assert(reloc_parent == .none); - if (is_fn_body) { + if (nav_ty.zigTypeTag(zcu) == .@"fn") { const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); if (!gop.found_existing) gop.value_ptr.* = {}; if (is_obj) { @@ -1025,23 +1031,26 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo .pointer => switch (ty.ptrSize(zcu)) { .slice => {}, .one, .many, .c => { - const elem_ty = ty.childType(zcu); const ptr = ip.indexToKey(val.toIntern()).ptr; if (ptr.base_addr == .int) return .{ .immediate = ptr.byte_offset }; if (ptr.byte_offset == 0) switch (ptr.base_addr) { .int => unreachable, // handled above - .nav => |nav| if (elem_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { - return .{ .lea_nav = nav }; - } else { - // Create the 0xaa bit pattern... - const undef_ptr_bits: u64 = @intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() + 1)) / 3); - // ...but align the pointer - const alignment = zcu.navAlignment(nav); - return .{ .immediate = alignment.forward(undef_ptr_bits) }; + .nav => |nav_index| { + const nav = ip.getNav(nav_index); + const nav_ty: Type = .fromInterned(nav.typeOf(ip)); + if (nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) or nav.getExtern(ip) != null) { + return .{ .lea_nav = nav_index }; + } else { + // Create the 0xaa bit pattern... + const undef_ptr_bits: u64 = @intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() + 1)) / 3); + // ...but align the pointer + const alignment = zcu.navAlignment(nav_index); + return .{ .immediate = alignment.forward(undef_ptr_bits) }; + } }, - .uav => |uav| if (elem_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { + .uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).isRuntimeFnOrHasRuntimeBits(zcu)) { return .{ .lea_uav = uav }; } else { // Create the 0xaa bit pattern... diff --git a/src/link.zig b/src/link.zig index 4999c8a31a..c8ae7a6bb4 100644 --- a/src/link.zig +++ b/src/link.zig @@ -798,14 +798,27 @@ pub const File = struct { }; /// Never called when LLVM is codegenning the ZCU. - fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { + fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) UpdateContainerTypeError!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)).updateContainerType(pt, ty); + 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) UpdateContainerTypeError!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); }, } } @@ -1375,8 +1388,14 @@ pub const ZcuTask = union(enum) { link_nav: InternPool.Nav.Index, /// Write the machine code for a function to the output file. link_func: Zcu.CodegenTaskPool.Index, - link_type: InternPool.Index, - update_line_number: InternPool.TrackedInst.Index, + /// This struct/union/enum type has finished type resolution (successfully or otherwise), so the + /// linker can now lower debug information for this type (and any structural types which depend + /// on it, such as `?T`, `struct { T }`, `[2]T`, etc). + debug_update_container_type: struct { + ty: InternPool.Index, + success: bool, + }, + debug_update_line_number: InternPool.TrackedInst.Index, }; pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { @@ -1563,21 +1582,24 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void } break :nav ip.indexToKey(func).func.owner_nav; }, - .link_type => |ty| nav: { - const name = Type.fromInterned(ty).containerTypeName(ip).toSlice(ip); - const nav_prog_node = comp.link_prog_node.start(name, 0); - defer nav_prog_node.end(); - if (zcu.llvm_object == null) { + .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); + defer ty_prog_node.end(); + if (zcu.llvm_object) |llvm_object| { + _ = llvm_object; + @compileError("MLUGG TODO"); + } else { if (comp.bin_file) |lf| { - lf.updateContainerType(pt, ty) catch |err| switch (err) { + lf.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), - error.TypeFailureReported => assert(zcu.failed_types.contains(ty)), + error.TypeFailureReported => assert(zcu.failed_types.contains(container_update.ty)), }; } } break :nav null; }, - .update_line_number => |ti| nav: { + .debug_update_line_number => |ti| 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) { diff --git a/src/link/DebugConstPool.zig b/src/link/DebugConstPool.zig new file mode 100644 index 0000000000..ce7f32551b --- /dev/null +++ b/src/link/DebugConstPool.zig @@ -0,0 +1,287 @@ +/// Helper type for debug information implementations (such as `link.Dwarf`) to help them emit +/// information about comptime-known values (constants), including types. +/// +/// Every constant with associated debug information is assigned an `Index` by calling `get`. The +/// pool will track which container types do and do not have a resolved layout, as well as which +/// constants in the pool depend on which types, and call into the implementation to emit debug +/// information for a constant only when all information is available. +/// +/// Indices into the pool are dense, and constants are never removed from the pool, so the debug +/// info implementation can store information for each one with a simple `ArrayList`. +/// +/// To use `DebugConstPool`, the debug info implementation is required to: +/// * forward `updateContainerType` calls to its `DebugConstPool` +/// * expose some callback functions---see functions in `DebugInfo` +/// * ensure that any `get` call is eventually followed by a `flushPending` call +const DebugConstPool = @This(); + +values: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), +pending: std.ArrayList(Index), +complete_containers: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), +container_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, ContainerDepEntry.Index), +container_dep_entries: std.ArrayList(ContainerDepEntry), + +pub const empty: DebugConstPool = .{ + .values = .empty, + .pending = .empty, + .complete_containers = .empty, + .container_deps = .empty, + .container_dep_entries = .empty, +}; + +pub fn deinit(pool: *DebugConstPool, gpa: Allocator) void { + pool.values.deinit(gpa); + pool.pending.deinit(gpa); + pool.complete_containers.deinit(gpa); + pool.container_deps.deinit(gpa); + pool.container_dep_entries.deinit(gpa); +} + +pub const Index = enum(u32) { + _, + pub fn val(i: Index, pool: *const DebugConstPool) InternPool.Index { + return pool.values.keys()[@intFromEnum(i)]; + } +}; + +pub const DebugInfo = union(enum) { + dwarf: *@import("Dwarf.zig"), + llvm: @import("../codegen/llvm.zig").Object.Ptr, + + /// 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` + /// following the `addConst` call, to actually populate the constant's debug info. + fn addConst( + di: DebugInfo, + pt: Zcu.PerThread, + index: Index, + val: InternPool.Index, + ) !void { + switch (di) { + inline else => |impl| return impl.addConst(pt, index, val), + } + } + + /// Tell the debug info implementation to emit information for the constant `val`, which is in + /// the pool at the given index. `val` is "complete", which means: + /// * If it is a type, its layout is known. + /// * Otherwise, the layout of its type is known. + fn updateConst( + di: DebugInfo, + pt: Zcu.PerThread, + index: Index, + val: InternPool.Index, + ) !void { + switch (di) { + inline else => |impl| return impl.updateConst(pt, index, val), + } + } + + /// Tell the debug info implementation to emit information for the constant `val`, which is in + /// the pool at the given index. `val` is "incomplete", meaning the implementation cannot emit + /// full information for it (for instance, perhaps it is a struct type which was never actually + /// initialized so never had its layout resolved). Instead, the implementation must emit some + /// form of placeholder entry representing an incomplete/unknown constant. + fn updateConstIncomplete( + di: DebugInfo, + pt: Zcu.PerThread, + index: Index, + val: InternPool.Index, + ) !void { + switch (di) { + inline else => |impl| return impl.updateConstIncomplete(pt, index, val), + } + } +}; + +const ContainerDepEntry = extern struct { + next: ContainerDepEntry.Index.Optional, + depender: DebugConstPool.Index, + const Index = enum(u32) { + _, + const Optional = enum(u32) { + none = std.math.maxInt(u32), + _, + fn unwrap(o: Optional) ?ContainerDepEntry.Index { + return switch (o) { + .none => null, + else => @enumFromInt(@intFromEnum(o)), + }; + } + }; + fn toOptional(i: ContainerDepEntry.Index) Optional { + return @enumFromInt(@intFromEnum(i)); + } + fn ptr(i: ContainerDepEntry.Index, pool: *DebugConstPool) *ContainerDepEntry { + return &pool.container_dep_entries.items[@intFromEnum(i)]; + } + }; +}; + +/// Calls to `link.File.updateContainerType` must be forwarded to this function so that the debug +/// constant pool has up-to-date information about the resolution status of types. +pub fn updateContainerType( + pool: *DebugConstPool, + pt: Zcu.PerThread, + di: DebugInfo, + container_ty: InternPool.Index, + success: bool, +) !void { + if (success) { + const gpa = pt.zcu.comp.gpa; + try pool.complete_containers.put(gpa, container_ty, {}); + } else { + _ = pool.complete_containers.fetchSwapRemove(container_ty); + } + var opt_dep = pool.container_deps.get(container_ty); + while (opt_dep) |dep| : (opt_dep = dep.ptr(pool).next.unwrap()) { + try pool.update(pt, di, dep.ptr(pool).depender); + } +} + +/// 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: *DebugConstPool, pt: Zcu.PerThread, di: DebugInfo, val: InternPool.Index) !DebugConstPool.Index { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.comp.gpa; + const gop = try pool.values.getOrPut(gpa, val); + const index: DebugConstPool.Index = @enumFromInt(gop.index); + if (!gop.found_existing) { + const ty: Type = switch (ip.typeOf(val)) { + .type_type => if (ip.isUndef(val)) .type else .fromInterned(val), + else => |ty| .fromInterned(ty), + }; + try pool.registerTypeDeps(index, ty, zcu); + try pool.pending.append(gpa, index); + try di.addConst(pt, index, val); + } + return index; +} +pub fn flushPending(pool: *DebugConstPool, pt: Zcu.PerThread, di: DebugInfo) !void { + while (pool.pending.pop()) |pending_ty| { + try pool.update(pt, di, pending_ty); + } +} + +fn update(pool: *DebugConstPool, pt: Zcu.PerThread, di: DebugInfo, index: DebugConstPool.Index) !void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const val = index.val(pool); + const ty: Type = switch (ip.typeOf(val)) { + .type_type => if (ip.isUndef(val)) .type else .fromInterned(val), + else => |ty| .fromInterned(ty), + }; + if (pool.checkType(ty, zcu)) { + try di.updateConst(pt, index, val); + } else { + try di.updateConstIncomplete(pt, index, val); + } +} +fn checkType(pool: *const DebugConstPool, ty: Type, zcu: *const Zcu) bool { + if (ty.isGenericPoison()) return true; + return switch (ty.zigTypeTag(zcu)) { + .type, + .void, + .bool, + .noreturn, + .int, + .float, + .pointer, + .comptime_float, + .comptime_int, + .undefined, + .null, + .error_set, + .@"opaque", + .frame, + .@"anyframe", + .enum_literal, + => true, + + .array, .vector => pool.checkType(ty.childType(zcu), zcu), + .optional => pool.checkType(ty.optionalChild(zcu), zcu), + .error_union => pool.checkType(ty.errorUnionPayload(zcu), zcu), + .@"fn" => { + const ip = &zcu.intern_pool; + const func = ip.indexToKey(ty.toIntern()).func_type; + for (func.param_types.get(ip)) |param_ty_ip| { + if (!pool.checkType(.fromInterned(param_ty_ip), zcu)) return false; + } + return pool.checkType(.fromInterned(func.return_type), zcu); + }, + .@"struct" => if (ty.isTuple(zcu)) { + for (0..ty.structFieldCount(zcu)) |field_index| { + if (!pool.checkType(ty.fieldType(field_index, zcu), zcu)) return false; + } + return true; + } else { + return pool.complete_containers.contains(ty.toIntern()); + }, + .@"union", .@"enum" => { + return pool.complete_containers.contains(ty.toIntern()); + }, + }; +} +fn registerTypeDeps(pool: *DebugConstPool, root: Index, ty: Type, zcu: *const Zcu) Allocator.Error!void { + if (ty.isGenericPoison()) return; + switch (ty.zigTypeTag(zcu)) { + .type, + .void, + .bool, + .noreturn, + .int, + .float, + .pointer, + .comptime_float, + .comptime_int, + .undefined, + .null, + .error_set, + .@"opaque", + .frame, + .@"anyframe", + .enum_literal, + => {}, + + .array, .vector => try pool.registerTypeDeps(root, ty.childType(zcu), zcu), + .optional => try pool.registerTypeDeps(root, ty.optionalChild(zcu), zcu), + .error_union => try pool.registerTypeDeps(root, ty.errorUnionPayload(zcu), zcu), + .@"fn" => { + const ip = &zcu.intern_pool; + const func = ip.indexToKey(ty.toIntern()).func_type; + for (func.param_types.get(ip)) |param_ty_ip| { + try pool.registerTypeDeps(root, .fromInterned(param_ty_ip), zcu); + } + try pool.registerTypeDeps(root, .fromInterned(func.return_type), zcu); + }, + .@"struct", .@"union", .@"enum" => if (ty.isTuple(zcu)) { + for (0..ty.structFieldCount(zcu)) |field_index| { + try pool.registerTypeDeps(root, ty.fieldType(field_index, zcu), zcu); + } + } else { + // `ty` is a container; register the dependency. + + const gpa = zcu.comp.gpa; + try pool.container_deps.ensureUnusedCapacity(gpa, 1); + try pool.container_dep_entries.ensureUnusedCapacity(gpa, 1); + errdefer comptime unreachable; + + const gop = pool.container_deps.getOrPutAssumeCapacity(ty.toIntern()); + const entry: ContainerDepEntry.Index = @enumFromInt(pool.container_dep_entries.items.len); + pool.container_dep_entries.appendAssumeCapacity(.{ + .next = if (gop.found_existing) gop.value_ptr.toOptional() else .none, + .depender = root, + }); + gop.value_ptr.* = entry; + }, + } +} + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const InternPool = @import("../InternPool.zig"); +const Type = @import("../Type.zig"); +const Zcu = @import("../Zcu.zig"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 1d87a42601..3094e8671e 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -18,6 +18,7 @@ const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); const link = @import("../link.zig"); const target_info = @import("../target.zig"); +const DebugConstPool = @import("DebugConstPool.zig"); gpa: Allocator, bin_file: *link.File, @@ -25,9 +26,11 @@ format: DW.Format, endian: std.builtin.Endian, address_size: AddressSize, +const_pool: DebugConstPool, + mods: std.AutoArrayHashMapUnmanaged(*Module, ModInfo), -types: std.AutoArrayHashMapUnmanaged(InternPool.Index, Entry.Index), -values: std.AutoArrayHashMapUnmanaged(InternPool.Index, Entry.Index), +/// Indices are `DebugConstPool.Index`. +values: std.ArrayList(struct { Unit.Index, Entry.Index }), navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Entry.Index), decls: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, Entry.Index), @@ -1034,15 +1037,14 @@ const Entry = struct { }); const zcu = dwarf.bin_file.comp.zcu.?; const ip = &zcu.intern_pool; - for (dwarf.types.keys(), dwarf.types.values()) |ty, other_entry| { - const ty_unit: Unit.Index = if (Type.fromInterned(ty).typeDeclInst(zcu)) |inst_index| - dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?) catch unreachable - else - .main; - if (sec.getUnit(ty_unit) == unit and unit.getEntry(other_entry) == entry) - log.err("missing Type({f}({d}))", .{ - Type.fromInterned(ty).fmt(.{ .tid = .main, .zcu = zcu }), - @intFromEnum(ty), + for (0.., dwarf.values.items) |raw_index, unit_and_entry| { + const index: DebugConstPool.Index = @enumFromInt(raw_index); + const val = index.val(&dwarf.const_pool); + const val_unit, const val_entry = unit_and_entry; + if (sec.getUnit(val_unit) == unit and unit.getEntry(val_entry) == entry) + log.err("missing Value({f}({d}))", .{ + Value.fromInterned(val).fmtValue(.{ .tid = .main, .zcu = zcu }), + @intFromEnum(val), }); } for (dwarf.navs.keys(), dwarf.navs.values()) |nav, other_entry| { @@ -1520,7 +1522,6 @@ pub const WipNav = struct { debug_info: Writer.Allocating, debug_line: Writer.Allocating, debug_loclists: Writer.Allocating, - pending_lazy: PendingLazy, pub fn deinit(wip_nav: *WipNav) void { const gpa = wip_nav.dwarf.gpa; @@ -1529,8 +1530,6 @@ pub const WipNav = struct { wip_nav.debug_info.deinit(); wip_nav.debug_line.deinit(); wip_nav.debug_loclists.deinit(); - wip_nav.pending_lazy.types.deinit(gpa); - wip_nav.pending_lazy.values.deinit(gpa); } pub fn genDebugFrame(wip_nav: *WipNav, loc: u32, cfa: Cfa) UpdateError!void { @@ -1945,6 +1944,12 @@ pub const WipNav = struct { try wip_nav.infoSectionOffset(.debug_str, StringSection.unit, try wip_nav.dwarf.debug_str.addString(wip_nav.dwarf, str), 0); } + fn strpFmt(wip_nav: *WipNav, comptime fmt: []const u8, args: anytype) (UpdateError || Writer.Error)!void { + const str = try std.fmt.allocPrint(wip_nav.dwarf.gpa, fmt, args); + defer wip_nav.dwarf.gpa.free(str); + return wip_nav.strp(str); + } + const ExprLocCounter = struct { dw: Writer.Discarding, section_offset_bytes: u32, @@ -2054,74 +2059,16 @@ pub const WipNav = struct { try dfw.splatByteAll(0, @intFromEnum(wip_nav.dwarf.address_size)); } - fn getNavEntry( - wip_nav: *WipNav, - nav_index: InternPool.Nav.Index, - ) UpdateError!struct { Unit.Index, Entry.Index } { - const zcu = wip_nav.pt.zcu; - const ip = &zcu.intern_pool; - const nav = ip.getNav(nav_index); - const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?); - const gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index); - if (gop.found_existing) return .{ unit, gop.value_ptr.* }; - const entry = try wip_nav.dwarf.addCommonEntry(unit); - gop.value_ptr.* = entry; - return .{ unit, entry }; - } - fn refNav( wip_nav: *WipNav, nav_index: InternPool.Nav.Index, ) (UpdateError || Writer.Error)!void { - const unit, const entry = try wip_nav.getNavEntry(nav_index); + const unit, const entry = try wip_nav.dwarf.getNavEntry(nav_index); try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } - fn getTypeEntry(wip_nav: *WipNav, ty: Type) UpdateError!struct { Unit.Index, Entry.Index } { - const zcu = wip_nav.pt.zcu; - const ip = &zcu.intern_pool; - const maybe_inst_index = ty.typeDeclInst(zcu); - const unit = if (maybe_inst_index) |inst_index| switch (switch (ip.indexToKey(ty.toIntern())) { - else => unreachable, - .struct_type => ip.loadStructType(ty.toIntern()).name_nav, - .union_type => ip.loadUnionType(ty.toIntern()).name_nav, - .enum_type => ip.loadEnumType(ty.toIntern()).name_nav, - .opaque_type => ip.loadOpaqueType(ty.toIntern()).name_nav, - }) { - .none => try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?), - else => |name_nav| return wip_nav.getNavEntry(name_nav.unwrap().?), - } else .main; - const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern()); - if (gop.found_existing) return .{ unit, gop.value_ptr.* }; - const entry = try wip_nav.dwarf.addCommonEntry(unit); - gop.value_ptr.* = entry; - if (maybe_inst_index == null) try wip_nav.pending_lazy.types.append(wip_nav.dwarf.gpa, ty.toIntern()); - return .{ unit, entry }; - } - fn refType(wip_nav: *WipNav, ty: Type) (UpdateError || Writer.Error)!void { - const unit, const entry = try wip_nav.getTypeEntry(ty); - try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); - } - - fn getValueEntry(wip_nav: *WipNav, value: Value) UpdateError!struct { Unit.Index, Entry.Index } { - const zcu = wip_nav.pt.zcu; - const ip = &zcu.intern_pool; - const ty = value.typeOf(zcu); - if (std.debug.runtime_safety) assert(ty.comptimeOnly(zcu)); - if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType()); - if (ip.isFunctionType(ty.toIntern()) and !value.isUndef(zcu)) return wip_nav.getNavEntry(switch (ip.indexToKey(value.toIntern())) { - else => unreachable, - .func => |func| func.owner_nav, - .@"extern" => |@"extern"| @"extern".owner_nav, - }); - const gop = try wip_nav.dwarf.values.getOrPut(wip_nav.dwarf.gpa, value.toIntern()); - const unit: Unit.Index = .main; - if (gop.found_existing) return .{ unit, gop.value_ptr.* }; - const entry = try wip_nav.dwarf.addCommonEntry(unit); - gop.value_ptr.* = entry; - try wip_nav.pending_lazy.values.append(wip_nav.dwarf.gpa, value.toIntern()); - return .{ unit, entry }; + return wip_nav.refValue(ty.toValue()); } fn refValue(wip_nav: *WipNav, value: Value) (UpdateError || Writer.Error)!void { @@ -2129,6 +2076,15 @@ pub const WipNav = struct { try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } + fn getValueEntry(wip_nav: *WipNav, value: Value) UpdateError!struct { Unit.Index, Entry.Index } { + if (value.typeOf(wip_nav.pt.zcu).toIntern() != .type_type) { + 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()); + return dwarf.values.items[@intFromEnum(index)]; + } + fn refForward(wip_nav: *WipNav) (Allocator.Error || Writer.Error)!u32 { const dwarf = wip_nav.dwarf; const diw = &wip_nav.debug_info.writer; @@ -2156,7 +2112,7 @@ pub const WipNav = struct { ) (UpdateError || Writer.Error)!void { const ty = val.typeOf(wip_nav.pt.zcu); const diw = &wip_nav.debug_info.writer; - const size = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0; + 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; @@ -2331,22 +2287,6 @@ pub const WipNav = struct { try wip_nav.refType(parent_type.?); try wip_nav.infoSectionOffset(.debug_info, wip_nav.unit, generic_decl_entry, 0); } - - const PendingLazy = struct { - types: std.ArrayList(InternPool.Index), - values: std.ArrayList(InternPool.Index), - - const empty: PendingLazy = .{ .types = .empty, .values = .empty }; - }; - - fn updateLazy(wip_nav: *WipNav, src_loc: Zcu.LazySrcLoc) (UpdateError || Writer.Error)!void { - while (true) if (wip_nav.pending_lazy.types.pop()) |pending_ty| - try wip_nav.dwarf.updateLazyType(wip_nav.pt, src_loc, pending_ty, &wip_nav.pending_lazy) - else if (wip_nav.pending_lazy.values.pop()) |pending_val| - try wip_nav.dwarf.updateLazyValue(wip_nav.pt, src_loc, pending_val, &wip_nav.pending_lazy) - else - break; - } }; /// When allocating, the ideal_capacity is calculated by @@ -2372,8 +2312,9 @@ pub fn init(lf: *link.File, format: DW.Format) Dwarf { }, .endian = target.cpu.arch.endian(), + .const_pool = .empty, + .mods = .empty, - .types = .empty, .values = .empty, .navs = .empty, .decls = .empty, @@ -2544,9 +2485,9 @@ pub fn initMetadata(dwarf: *Dwarf) UpdateError!void { pub fn deinit(dwarf: *Dwarf) void { const gpa = dwarf.gpa; + dwarf.const_pool.deinit(gpa); for (dwarf.mods.values()) |*mod_info| mod_info.deinit(gpa); dwarf.mods.deinit(gpa); - dwarf.types.deinit(gpa); dwarf.values.deinit(gpa); dwarf.navs.deinit(gpa); dwarf.decls.deinit(gpa); @@ -2562,6 +2503,21 @@ pub fn deinit(dwarf: *Dwarf) void { dwarf.* = undefined; } +fn getNavEntry( + dwarf: *Dwarf, + nav_index: InternPool.Nav.Index, +) UpdateError!struct { Unit.Index, Entry.Index } { + const zcu = dwarf.bin_file.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); + const unit = try dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?); + const gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); + if (gop.found_existing) return .{ unit, gop.value_ptr.* }; + const entry = try dwarf.addCommonEntry(unit); + gop.value_ptr.* = entry; + return .{ unit, entry }; +} + fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index { const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); const unit: Unit.Index = @enumFromInt(mod_gop.index); @@ -2622,6 +2578,10 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo { return &dwarf.mods.values()[@intFromEnum(unit)]; } +fn getUnitModule(dwarf: *Dwarf, unit: Unit.Index) *Module { + return dwarf.mods.keys()[@intFromEnum(unit)]; +} + pub fn initWipNav( dwarf: *Dwarf, pt: Zcu.PerThread, @@ -2683,7 +2643,6 @@ fn initWipNavInner( .debug_info = .init(dwarf.gpa), .debug_line = .init(dwarf.gpa), .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, }; errdefer wip_nav.deinit(); @@ -3050,7 +3009,7 @@ fn finishWipNavWriterError( } try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written()); - try wip_nav.updateLazy(zcu.navSrcLoc(nav_index)); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, CodegenFail }!void { @@ -3087,34 +3046,12 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo return; } - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = try dwarf.getUnit(file.mod.?), - .entry = undefined, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, - }; - defer wip_nav.deinit(); - - const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); - errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop(); - const tag: union(enum) { - done, - decl_alias, - decl_var, - decl_const, - decl_func_alias: InternPool.Nav.Index, + alias, + @"var", + @"const", + func: Type, + func_alias: InternPool.Nav.Index, } = switch (ip.indexToKey(nav_val.toIntern())) { .int_type, .ptr_type, @@ -3128,242 +3065,49 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .func_type, .error_set_type, .inferred_error_set_type, - => .decl_alias, + => .alias, + .struct_type => tag: { const loaded_struct = ip.loadStructType(nav_val.toIntern()); - if (loaded_struct.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; + 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 }); + return; } - wip_nav.entry = nav_gop.value_ptr.*; - - const diw = &wip_nav.debug_info.writer; - - switch (loaded_struct.layout) { - .auto, .@"extern" => { - try wip_nav.declCommon(if (loaded_struct.field_types.len == 0) .{ - .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_namespace_struct, - } else .{ - .decl = .decl_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_struct, - }, &nav, inst_info.file, &decl); - if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else { - try diw.writeUleb128(nav_val.toType().abiSize(zcu)); - try diw.writeUleb128(nav_val.toType().abiAlignment(zcu).toByteUnits().?); - for (0..loaded_struct.field_types.len) |field_index| { - const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index); - const field_init = loaded_struct.field_defaults.getOrNone(ip, field_index); - assert(!(is_comptime and field_init == .none)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const has_runtime_bits, const has_comptime_state = switch (field_init) { - .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu), - }, - }; - try wip_nav.abbrevCode(if (is_comptime) - if (has_comptime_state) - .struct_field_comptime_comptime_state - else if (has_runtime_bits) - .struct_field_comptime_runtime_bits - else - .struct_field_comptime - else if (field_init != .none) - if (has_comptime_state) - .struct_field_default_comptime_state - else if (has_runtime_bits) - .struct_field_default_runtime_bits - else - .struct_field - else - .struct_field); - try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); - try wip_nav.refType(field_type); - if (!is_comptime) { - try diw.writeUleb128(loaded_struct.field_offsets.get(ip)[field_index]); - try diw.writeUleb128(loaded_struct.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - field_type.abiAlignment(zcu).toByteUnits().?); - } - if (has_comptime_state) - try wip_nav.refValue(.fromInterned(field_init)) - else if (has_runtime_bits) - try wip_nav.blockValue(nav_src_loc, .fromInterned(field_init)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - }, - .@"packed" => { - try wip_nav.declCommon(.{ - .decl = .decl_packed_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_packed_struct, - }, &nav, inst_info.file, &decl); - try wip_nav.refType(.fromInterned(loaded_struct.packed_backing_int_type)); - var field_bit_offset: u16 = 0; - for (0..loaded_struct.field_types.len) |field_index| { - try wip_nav.abbrevCode(.packed_struct_field); - try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(field_bit_offset); - field_bit_offset += @intCast(field_type.bitSize(zcu)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - } - break :tag .done; + break :tag .alias; }, .enum_type => tag: { const loaded_enum = ip.loadEnumType(nav_val.toIntern()); - const type_zir_index = loaded_enum.zir_index.unwrap() orelse break :tag .decl_alias; - if (type_zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; + 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 }); + return; } - wip_nav.entry = nav_gop.value_ptr.*; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(if (loaded_enum.field_names.len > 0) .{ - .decl = .decl_enum, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_enum, - } else .{ - .decl = .decl_empty_enum, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_empty_enum, - }, &nav, inst_info.file, &decl); - try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); - for (0..loaded_enum.field_names.len) |field_index| { - try wip_nav.enumConstValue(loaded_enum, .{ - .sdata = .signed_enum_field, - .udata = .unsigned_enum_field, - .block = .big_enum_field, - }, field_index); - try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); - } - if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - break :tag .done; + break :tag .alias; }, .union_type => tag: { const loaded_union = ip.loadUnionType(nav_val.toIntern()); - if (loaded_union.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; + 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 }); + return; } - wip_nav.entry = nav_gop.value_ptr.*; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(.{ - .decl = .decl_union, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_union, - }, &nav, inst_info.file, &decl); - const union_layout = Type.getUnionLayout(loaded_union, zcu); - try diw.writeUleb128(union_layout.abi_size); - try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); - const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); - if (loaded_union.has_runtime_tag) { - try wip_nav.abbrevCode(.tagged_union); - try wip_nav.infoSectionOffset( - .debug_info, - wip_nav.unit, - wip_nav.entry, - @intCast(diw.end + dwarf.sectionOffsetBytes()), - ); - { - try wip_nav.abbrevCode(.generated_field); - try wip_nav.strp("tag"); - try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type)); - try diw.writeUleb128(union_layout.tagOffset()); - - for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.enumConstValue(loaded_tag, .{ - .sdata = .signed_tagged_union_field, - .udata = .unsigned_tagged_union_field, - .block = .big_tagged_union_field, - }, field_index); - { - try wip_nav.abbrevCode(.struct_field); - try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(union_layout.payloadOffset()); - try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } else for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.abbrevCode(.untagged_union_field); - try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - break :tag .done; + break :tag .alias; }, .opaque_type => tag: { const loaded_opaque = ip.loadOpaqueType(nav_val.toIntern()); - if (loaded_opaque.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; + 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 }); + return; } - wip_nav.entry = nav_gop.value_ptr.*; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(.{ - .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_namespace_struct, - }, &nav, inst_info.file, &decl); - try diw.writeByte(@intFromBool(true)); - break :tag .done; + break :tag .alias; }, + .undef, .simple_value, .int, @@ -3378,63 +3122,69 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .aggregate, .un, .bitpack, - => .decl_const, - .variable => .decl_var, - .@"extern" => unreachable, - .func => |func| tag: { - if (func.owner_nav != nav_index) break :tag .{ .decl_func_alias = func.owner_nav }; - if (nav_gop.found_existing) switch (try dwarf.debug_info.declAbbrevCode(wip_nav.unit, nav_gop.value_ptr.*)) { - .null => {}, - else => unreachable, - .decl_nullary_func, .decl_func, .decl_instance_nullary_func, .decl_instance_func => return, - .decl_nullary_func_generic, - .decl_func_generic, - .decl_instance_nullary_func_generic, - .decl_instance_func_generic, - => dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear(), - } else nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - wip_nav.entry = nav_gop.value_ptr.*; + => .@"const", - const func_type = ip.indexToKey(func.ty).func_type; - const is_nullary = !func_type.is_var_args and for (0..func_type.param_types.len) |param_index| { - if (!func_type.paramIsComptime(std.math.cast(u5, param_index) orelse break false)) break false; - } else true; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(if (is_nullary) .{ - .decl = .decl_nullary_func_generic, - .generic_decl = .generic_decl_func, - .decl_instance = .decl_instance_nullary_func_generic, - } else .{ - .decl = .decl_func_generic, - .generic_decl = .generic_decl_func, - .decl_instance = .decl_instance_func_generic, - }, &nav, inst_info.file, &decl); - try wip_nav.refType(.fromInterned(func_type.return_type)); - if (!is_nullary) { - for (0..func_type.param_types.len) |param_index| { - if (std.math.cast(u5, param_index)) |small_param_index| - if (func_type.paramIsComptime(small_param_index)) continue; - try wip_nav.abbrevCode(.func_type_param); - try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); - } - if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - break :tag .done; + .variable => .@"var", + + .@"extern" => unreachable, + + .func => |func| tag: { + if (func.owner_nav != nav_index) break :tag .{ .func_alias = func.owner_nav }; + break :tag .{ .func = .fromInterned(func.ty) }; }, + // memoization, not types .memoized_call => unreachable, }; - if (tag != .done) { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - wip_nav.entry = nav_gop.value_ptr.*; + + const unit = try dwarf.getUnit(file.mod.?); + + const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); + errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop(); + + if (nav_gop.found_existing) { + if (tag == .func) switch (try dwarf.debug_info.declAbbrevCode(unit, nav_gop.value_ptr.*)) { + else => unreachable, + + .decl_nullary_func, + .decl_func, + .decl_instance_nullary_func, + .decl_instance_func, + => return, + + .null, + .decl_nullary_func_generic, + .decl_func_generic, + .decl_instance_nullary_func_generic, + .decl_instance_func_generic, + => {}, + }; + dwarf.debug_info.section.getUnit(unit).getEntry(nav_gop.value_ptr.*).clear(); + } else { + nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); } + + var wip_nav: WipNav = .{ + .dwarf = dwarf, + .pt = pt, + .unit = unit, + .entry = nav_gop.value_ptr.*, + .any_children = false, + .func = .none, + .func_sym_index = undefined, + .func_high_pc = undefined, + .blocks = undefined, + .cfi = undefined, + .debug_frame = .init(dwarf.gpa), + .debug_info = .init(dwarf.gpa), + .debug_line = .init(dwarf.gpa), + .debug_loclists = .init(dwarf.gpa), + }; + defer wip_nav.deinit(); + const diw = &wip_nav.debug_info.writer; + switch (tag) { - .done => {}, - .decl_alias => { + .alias => { try wip_nav.declCommon(.{ .decl = .decl_alias, .generic_decl = .generic_decl_const, @@ -3442,8 +3192,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo }, &nav, inst_info.file, &decl); try wip_nav.refType(nav_val.toType()); }, - .decl_var => { - const diw = &wip_nav.debug_info.writer; + .@"var" => { try wip_nav.declCommon(.{ .decl = .decl_var, .generic_decl = .generic_decl_var, @@ -3460,8 +3209,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo nav_ty.abiAlignment(zcu).toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); }, - .decl_const => { - const diw = &wip_nav.debug_info.writer; + .@"const" => { const nav_ty = nav_val.typeOf(zcu); const has_runtime_bits = nav_ty.hasRuntimeBits(zcu); const has_comptime_state = nav_ty.comptimeOnly(zcu); @@ -3496,7 +3244,33 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo try wip_nav.abbrevCode(.is_const); try wip_nav.refType(nav_ty); }, - .decl_func_alias => |owner_nav| { + .func => |func_ty| { + const func_type = ip.indexToKey(func_ty.toIntern()).func_type; + const is_nullary = !func_type.is_var_args and for (0..func_type.param_types.len) |param_index| { + if (!func_type.paramIsComptime(std.math.cast(u5, param_index) orelse break false)) break false; + } else true; + try wip_nav.declCommon(if (is_nullary) .{ + .decl = .decl_nullary_func_generic, + .generic_decl = .generic_decl_func, + .decl_instance = .decl_instance_nullary_func_generic, + } else .{ + .decl = .decl_func_generic, + .generic_decl = .generic_decl_func, + .decl_instance = .decl_instance_func_generic, + }, &nav, inst_info.file, &decl); + try wip_nav.refType(.fromInterned(func_type.return_type)); + if (!is_nullary) { + for (0..func_type.param_types.len) |param_index| { + if (std.math.cast(u5, param_index)) |small_param_index| + if (func_type.paramIsComptime(small_param_index)) continue; + try wip_nav.abbrevCode(.func_type_param); + try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); + } + if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + }, + .func_alias => |owner_nav| { try wip_nav.declCommon(.{ .decl = .decl_alias, .generic_decl = .generic_decl_const, @@ -3505,31 +3279,81 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo try wip_nav.refNav(owner_nav); }, } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try wip_nav.updateLazy(nav_src_loc); + try dwarf.debug_info.section.replaceEntry(unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } -fn updateLazyType( +pub fn updateContainerType( dwarf: *Dwarf, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - type_index: InternPool.Index, - pending_lazy: *WipNav.PendingLazy, -) (UpdateError || Writer.Error)!void { + ty: InternPool.Index, + success: bool, +) !void { + try dwarf.const_pool.updateContainerType(pt, .{ .dwarf = dwarf }, ty, success); +} +/// Should only be called by the `DebugConstPool` implementation. +pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: DebugConstPool.Index, val: InternPool.Index) !void { const zcu = pt.zcu; const ip = &zcu.intern_pool; - assert(ip.typeOf(type_index) == .type_type); - const ty: Type = .fromInterned(type_index); - switch (type_index) { - .generic_poison_type => log.debug("updateLazyType({s})", .{"anytype"}), - else => log.debug("updateLazyType({f})", .{ty.fmt(pt)}), + + const unit: Unit.Index, const entry: Entry.Index = switch (ip.indexToKey(val)) { + else => .{ .main, try dwarf.addCommonEntry(.main) }, + .func => |func| try dwarf.getNavEntry(func.owner_nav), + .@"extern" => |@"extern"| try dwarf.getNavEntry(@"extern".owner_nav), + .struct_type, .union_type, .enum_type, .opaque_type => |_, tag| entry: { + const name_nav = switch (tag) { + .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, + else => unreachable, + }; + if (name_nav.unwrap()) |nav| { + break :entry try dwarf.getNavEntry(nav); + } else { + const zir_index = Type.fromInterned(val).typeDeclInstAllowGeneratedTag(zcu).?; + const unit = try dwarf.getUnit(zcu.fileByIndex(zir_index.resolveFile(ip)).mod.?); + break :entry .{ unit, try dwarf.addCommonEntry(unit) }; + } + }, + }; + + assert(@intFromEnum(index) == dwarf.values.items.len); + try dwarf.values.append(dwarf.gpa, .{ unit, entry }); +} +/// Should only be called by the `DebugConstPool` implementation. +/// +/// Emits a "dummy" DIE for the given comptime-only value (which may be a type). For types, this is +/// an opaque type. Otherwise, it is an undefined value of the value's type. +pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: DebugConstPool.Index, value_index: InternPool.Index) !void { + const zcu = pt.zcu; + + const val: Value = .fromInterned(value_index); + + switch (value_index) { + .generic_poison_type => log.debug("updateValueIncomplete(anytype)", .{}), + else => log.debug("updateValueIncomplete(@as({f}, {f}))", .{ + val.typeOf(zcu).fmt(pt), + val.fmtValue(pt), + }), } + const unit, const entry = dwarf.values.items[@intFromEnum(debug_const_index)]; + + for ([_]*Section{ + &dwarf.debug_aranges.section, + &dwarf.debug_aranges.section, + &dwarf.debug_info.section, + &dwarf.debug_line.section, + &dwarf.debug_loclists.section, + &dwarf.debug_rnglists.section, + }) |sec| sec.getUnit(unit).getEntry(entry).clear(); + var wip_nav: WipNav = .{ .dwarf = dwarf, .pt = pt, - .unit = .main, - .entry = dwarf.types.get(type_index).?, + .unit = unit, + .entry = entry, .any_children = false, .func = .none, .func_sym_index = undefined, @@ -3540,43 +3364,119 @@ fn updateLazyType( .debug_info = .init(dwarf.gpa), .debug_line = .init(dwarf.gpa), .debug_loclists = .init(dwarf.gpa), - .pending_lazy = pending_lazy.*, }; - defer { - pending_lazy.* = wip_nav.pending_lazy; - wip_nav.pending_lazy = .empty; - wip_nav.deinit(); - } - const diw = &wip_nav.debug_info.writer; - const name = switch (type_index) { - .generic_poison_type => "", - else => try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)}), - }; - defer dwarf.gpa.free(name); - - switch (ip.indexToKey(type_index)) { - .undef => { - try wip_nav.abbrevCode(.undefined_comptime_value); - try wip_nav.refType(.type); + defer wip_nav.deinit(); + switch (val.typeOf(zcu).toIntern()) { + .type_type => { + try wip_nav.abbrevCode(.generated_empty_struct_type); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); }, + else => |ty| { + try wip_nav.abbrevCode(.undefined_comptime_value); + try wip_nav.refType(.fromInterned(ty)); + }, + } + try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written()); + try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written()); +} +/// Should only be called by the `DebugConstPool` implementation. +/// +/// Emits a DIE for the given comptime-only value (which may be a type). +pub fn updateConst(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: DebugConstPool.Index, value_index: InternPool.Index) !void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + const val: Value = .fromInterned(value_index); + + if (val.typeOf(zcu).toIntern() == .type_type and !val.isUndef(zcu)) { + val.toType().assertHasLayout(zcu); + } else { + val.typeOf(zcu).assertHasLayout(zcu); + } + + const value_ip_key = ip.indexToKey(value_index); + switch (value_ip_key) { + .func => return, // populated by the Nav instead (`updateComptimeNav` or `initWipNav`) + .@"extern" => return, // populated by the Nav instead (`initWipNav`) + else => {}, + } + + switch (value_index) { + .generic_poison_type => log.debug("updateValue(anytype)", .{}), + else => log.debug("updateValue(@as({f}, {f}))", .{ + val.typeOf(zcu).fmt(pt), + val.fmtValue(pt), + }), + } + + const unit, const entry = dwarf.values.items[@intFromEnum(debug_const_index)]; + + for ([_]*Section{ + &dwarf.debug_aranges.section, + &dwarf.debug_info.section, + &dwarf.debug_line.section, + &dwarf.debug_loclists.section, + &dwarf.debug_rnglists.section, + }) |sec| sec.getUnit(unit).getEntry(entry).clear(); + + var wip_nav: WipNav = .{ + .dwarf = dwarf, + .pt = pt, + .unit = unit, + .entry = entry, + .any_children = false, + .func = .none, + .func_sym_index = undefined, + .func_high_pc = undefined, + .blocks = undefined, + .cfi = undefined, + .debug_frame = .init(dwarf.gpa), + .debug_info = .init(dwarf.gpa), + .debug_line = .init(dwarf.gpa), + .debug_loclists = .init(dwarf.gpa), + }; + defer wip_nav.deinit(); + + // TODO: we really shouldn't need source locations at this point in the pipeline: we've lost + // that information by now. If the linker fundamentally cannot lower certain values, that needs + // to be caught in the frontend; if it can only hit transient failures, they should be reported + // without trying to tie them to a bogus source location. + const src_loc: Zcu.LazySrcLoc = .{ + .base_node_inst = inst: { + const mod_root_file_index = zcu.module_roots.get(dwarf.getUnitModule(unit)).?.unwrap().?; + const mod_root_type_index = zcu.fileRootType(mod_root_file_index); + break :inst ip.loadStructType(mod_root_type_index).zir_index; + }, + .offset = .{ .byte_abs = 0 }, + }; + + const diw = &wip_nav.debug_info.writer; + var big_int_space: Value.BigIntSpace = undefined; + switch (value_ip_key) { + .func => unreachable, // handled above + .@"extern" => unreachable, // handled above + .int_type => |int_type| { try wip_nav.abbrevCode(.numeric_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try diw.writeByte(switch (int_type.signedness) { inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)), }); try diw.writeUleb128(int_type.bits); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); }, .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .one, .many, .c => { const ptr_child_type: Type = .fromInterned(ptr_type.child); - try wip_nav.abbrevCode(if (ptr_type.sentinel == .none) .ptr_type else .ptr_sentinel_type); - try wip_nav.strp(name); + try wip_nav.abbrevCode(switch (ptr_type.flags.alignment) { + .none => if (ptr_type.sentinel == .none) .ptr_type else .ptr_sentinel_type, + else => if (ptr_type.sentinel == .none) .ptr_aligned_type else .ptr_aligned_sentinel_type, + }); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); if (ptr_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(ptr_type.sentinel)); - try diw.writeUleb128(ptr_type.flags.alignment.toByteUnits() orelse - ptr_child_type.abiAlignment(zcu).toByteUnits().?); + if (ptr_type.flags.alignment.toByteUnits()) |a| try diw.writeUleb128(a); try diw.writeByte(@intFromEnum(ptr_type.flags.address_space)); if (ptr_type.flags.is_const or ptr_type.flags.is_volatile) try wip_nav.infoSectionOffset( .debug_info, @@ -3600,12 +3500,12 @@ fn updateLazyType( }, .slice => { try wip_nav.abbrevCode(.generated_struct_type); - try wip_nav.strp(name); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("ptr"); - const ptr_field_type = ty.slicePtrFieldType(zcu); + const ptr_field_type = val.toType().slicePtrFieldType(zcu); try wip_nav.refType(ptr_field_type); try diw.writeUleb128(0); try wip_nav.abbrevCode(.generated_field); @@ -3619,7 +3519,7 @@ fn updateLazyType( .array_type => |array_type| { const array_child_type: Type = .fromInterned(array_type.child); try wip_nav.abbrevCode(if (array_type.sentinel == .none) .array_type else .array_sentinel_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); if (array_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(array_type.sentinel)); try wip_nav.refType(array_child_type); try wip_nav.abbrevCode(.array_len); @@ -3629,7 +3529,7 @@ fn updateLazyType( }, .vector_type => |vector_type| { try wip_nav.abbrevCode(.vector_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try wip_nav.refType(.fromInterned(vector_type.child)); try wip_nav.abbrevCode(.array_len); try wip_nav.refType(.usize); @@ -3640,9 +3540,9 @@ fn updateLazyType( const opt_child_type: Type = .fromInterned(opt_child_type_index); const opt_repr = optRepr(opt_child_type, zcu); try wip_nav.abbrevCode(.generated_union_type); - try wip_nav.strp(name); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); switch (opt_repr) { .opv_null => { try wip_nav.abbrevCode(.generated_field); @@ -3720,12 +3620,12 @@ fn updateLazyType( }; try wip_nav.abbrevCode(.generated_union_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); if (error_union_type.error_set_type != .generic_poison_type and error_union_type.payload_type != .generic_poison_type) { - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); } else { try diw.writeUleb128(0); try diw.writeUleb128(1); @@ -3791,20 +3691,24 @@ fn updateLazyType( .bool, => { try wip_nav.abbrevCode(.numeric_type); - try wip_nav.strp(name); - try diw.writeByte(if (type_index == .bool_type) + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeByte(if (value_index == .bool_type) DW.ATE.boolean - else if (ty.isRuntimeFloat()) + else if (val.toType().isRuntimeFloat()) DW.ATE.float - else if (ty.isSignedInt(zcu)) + else if (val.toType().isSignedInt(zcu)) DW.ATE.signed - else if (ty.isUnsignedInt(zcu)) + else if (val.toType().isUnsignedInt(zcu)) DW.ATE.unsigned else unreachable); - try diw.writeUleb128(ty.bitSize(zcu)); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try diw.writeUleb128(val.toType().bitSize(zcu)); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); + }, + .generic_poison => { + try wip_nav.abbrevCode(.void_type); + try wip_nav.strp("anytype"); }, .anyopaque, .void, @@ -3815,37 +3719,29 @@ fn updateLazyType( .null, .undefined, .enum_literal, - .generic_poison, => { try wip_nav.abbrevCode(.void_type); - try wip_nav.strp(if (type_index == .generic_poison_type) "anytype" else name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); }, .anyerror => return, // delay until flush .adhoc_inferred_error_set => unreachable, }, - .struct_type, - .union_type, - .opaque_type, - => unreachable, .tuple_type => |tuple_type| if (tuple_type.types.len == 0) { try wip_nav.abbrevCode(.generated_empty_struct_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try diw.writeByte(@intFromBool(false)); } else { try wip_nav.abbrevCode(.generated_struct_type); - try wip_nav.strp(name); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().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_type: Type = .fromInterned(tuple_type.types.get(ip)[field_index]); const has_runtime_bits, const has_comptime_state = switch (comptime_value) { .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu), - }, + else => .{ field_type.hasRuntimeBits(zcu), field_type.comptimeOnly(zcu) }, }; try wip_nav.abbrevCode(if (has_comptime_state) .struct_field_comptime_comptime_state @@ -3875,25 +3771,259 @@ fn updateLazyType( } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, - .enum_type => { - const loaded_enum = ip.loadEnumType(type_index); - try wip_nav.abbrevCode(if (loaded_enum.field_names.len == 0) .generated_empty_enum_type else .generated_enum_type); - try wip_nav.strp(name); - try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); - for (0..loaded_enum.field_names.len) |field_index| { - try wip_nav.enumConstValue(loaded_enum, .{ - .sdata = .signed_enum_field, - .udata = .unsigned_enum_field, - .block = .big_enum_field, - }, field_index); - try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); + .struct_type => { + const loaded_struct = ip.loadStructType(value_index); + const ty = val.toType(); + const file = loaded_struct.zir_index.resolveFile(ip); + switch (loaded_struct.layout) { + .auto, .@"extern" => { + const struct_is_file: bool = if (loaded_struct.zir_index.resolve(ip)) |inst| f: { + break :f inst == .main_struct_inst; + } else false; + if (loaded_struct.name_nav.unwrap()) |nav_index| { + assert(!struct_is_file); + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + 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_instance = .decl_instance_namespace_struct, + } else .{ + .decl = .decl_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_struct, + }, &nav, file, &decl); + } else { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(switch (loaded_struct.field_types.len) { + 0 => if (struct_is_file) .empty_file else .empty_struct_type, + else => if (struct_is_file) .file else .struct_type, + }); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_struct.name.toSlice(ip)); + } + if (loaded_struct.field_types.len == 0) { + if (!struct_is_file) try diw.writeByte(@intFromBool(false)); + } else { + try diw.writeUleb128(ty.abiSize(zcu)); + try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + for (0..loaded_struct.field_types.len) |field_index| { + const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index); + const field_init = loaded_struct.field_defaults.getOrNone(ip, field_index); + assert(!(is_comptime and field_init == .none)); + const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + const has_runtime_bits, const has_comptime_state = switch (field_init) { + .none => .{ false, false }, + else => .{ + field_type.hasRuntimeBits(zcu), + field_type.comptimeOnly(zcu), + }, + }; + try wip_nav.abbrevCode(if (is_comptime) + if (has_comptime_state) + .struct_field_comptime_comptime_state + else if (has_runtime_bits) + .struct_field_comptime_runtime_bits + else + .struct_field_comptime + else if (field_init != .none) + if (has_comptime_state) + .struct_field_default_comptime_state + else if (has_runtime_bits) + .struct_field_default_runtime_bits + else + .struct_field + else + .struct_field); + try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); + try wip_nav.refType(field_type); + if (!is_comptime) { + try diw.writeUleb128(loaded_struct.field_offsets.get(ip)[field_index]); + try diw.writeUleb128(loaded_struct.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + field_type.abiAlignment(zcu).toByteUnits().?); + } + if (has_comptime_state) + try wip_nav.refValue(.fromInterned(field_init)) + else if (has_runtime_bits) + try wip_nav.blockValue(ty.srcLoc(zcu), .fromInterned(field_init)); + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + }, + .@"packed" => { + const need_terminator: bool = if (loaded_struct.name_nav.unwrap()) |nav_index| t: { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_packed_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_packed_struct, + }, &nav, file, &decl); + break :t true; + } else t: { + 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)); + break :t loaded_struct.field_types.len > 0; + }; + try wip_nav.refType(.fromInterned(loaded_struct.packed_backing_int_type)); + var field_bit_offset: u16 = 0; + for (0..loaded_struct.field_types.len) |field_index| { + try wip_nav.abbrevCode(.packed_struct_field); + try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(field_bit_offset); + field_bit_offset += @intCast(field_type.bitSize(zcu)); + } + if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + }, } - if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + }, + .union_type => { + const loaded_union = ip.loadUnionType(value_index); + const file = loaded_union.zir_index.resolveFile(ip); + const need_terminator: bool = if (loaded_union.name_nav.unwrap()) |nav_index| t: { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_union, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_union, + }, &nav, file, &decl); + break :t true; + } else t: { + 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)); + break :t loaded_union.field_types.len > 0; + }; + const union_layout = Type.getUnionLayout(loaded_union, zcu); + try diw.writeUleb128(union_layout.abi_size); + try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); + const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); + if (loaded_union.has_runtime_tag) { + try wip_nav.abbrevCode(.tagged_union); + try wip_nav.infoSectionOffset( + .debug_info, + wip_nav.unit, + wip_nav.entry, + @intCast(diw.end + dwarf.sectionOffsetBytes()), + ); + { + try wip_nav.abbrevCode(.generated_field); + try wip_nav.strp("tag"); + try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type)); + try diw.writeUleb128(union_layout.tagOffset()); + + for (0..loaded_union.field_types.len) |field_index| { + try wip_nav.enumConstValue(loaded_tag, .{ + .sdata = .signed_tagged_union_field, + .udata = .unsigned_tagged_union_field, + .block = .big_tagged_union_field, + }, field_index); + { + try wip_nav.abbrevCode(.struct_field); + try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(union_layout.payloadOffset()); + try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } else for (0..loaded_union.field_types.len) |field_index| { + try wip_nav.abbrevCode(.untagged_union_field); + try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + } + if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(value_index); + if (loaded_enum.zir_index.unwrap()) |zir_index| { + assert(loaded_enum.owner_union == .none); + const file = zir_index.resolveFile(ip); + if (loaded_enum.name_nav.unwrap()) |nav_index| { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + 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_instance = .decl_instance_enum, + } else .{ + .decl = .decl_empty_enum, + .generic_decl = .generic_decl_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.refType(.fromInterned(loaded_enum.int_tag_type)); + for (0..loaded_enum.field_names.len) |field_index| { + try wip_nav.enumConstValue(loaded_enum, .{ + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, + }, field_index); + try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); + } + if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } 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.refType(.fromInterned(loaded_enum.int_tag_type)); + for (0..loaded_enum.field_names.len) |field_index| { + try wip_nav.enumConstValue(loaded_enum, .{ + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, + }, field_index); + try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); + } + if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(value_index); + const file = loaded_opaque.zir_index.resolveFile(ip); + if (loaded_opaque.name_nav.unwrap()) |nav_index| { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_namespace_struct, + .generic_decl = .generic_decl_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 diw.writeByte(@intFromBool(true)); }, .func_type => |func_type| { const is_nullary = func_type.param_types.len == 0 and !func_type.is_var_args; try wip_nav.abbrevCode(if (is_nullary) .nullary_func_type else .func_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); const cc: DW.CC = cc: { if (zcu.getTarget().cCallingConvention()) |cc| { if (@as(std.builtin.CallingConvention.Tag, cc) == func_type.cc) { @@ -3975,7 +4105,7 @@ fn updateLazyType( }, .error_set_type => |error_set_type| { try wip_nav.abbrevCode(if (error_set_type.names.len == 0) .generated_empty_enum_type else .generated_enum_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{ .signedness = .unsigned, .bits = zcu.errorSetBits(), @@ -3990,100 +4120,28 @@ fn updateLazyType( }, .inferred_error_set_type => |func| { try wip_nav.abbrevCode(.inferred_error_set_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try wip_nav.refType(.fromInterned(switch (ip.funcIesResolvedUnordered(func)) { .none => .anyerror_type, else => |ies| ies, })); }, - // values, not types - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - .bitpack, - // memoization, not types - .memoized_call, - => unreachable, - } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); -} - -fn updateLazyValue( - dwarf: *Dwarf, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - value_index: InternPool.Index, - pending_lazy: *WipNav.PendingLazy, -) (UpdateError || Writer.Error)!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - assert(ip.typeOf(value_index) != .type_type); - log.debug("updateLazyValue(@as({f}, {f}))", .{ - Value.fromInterned(value_index).typeOf(zcu).fmt(pt), - Value.fromInterned(value_index).fmtValue(pt), - }); - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = .main, - .entry = dwarf.values.get(value_index).?, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = pending_lazy.*, - }; - defer { - pending_lazy.* = wip_nav.pending_lazy; - wip_nav.pending_lazy = .empty; - wip_nav.deinit(); - } - const diw = &wip_nav.debug_info.writer; - var big_int_space: Value.BigIntSpace = undefined; - switch (ip.indexToKey(value_index)) { - .int_type, - .ptr_type, - .array_type, - .vector_type, - .opt_type, - .anyframe_type, - .error_union_type, - .simple_type, - .struct_type, - .tuple_type, - .union_type, - .opaque_type, - .enum_type, - .func_type, - .error_set_type, - .inferred_error_set_type, - => unreachable, // already handled .undef => |ty| { try wip_nav.abbrevCode(.undefined_comptime_value); try wip_nav.refType(.fromInterned(ty)); }, - .simple_value => unreachable, // opv state - .variable, .@"extern" => unreachable, // not a value - .func => unreachable, // already handled + .simple_value => |simple_value| switch (simple_value) { + .void => unreachable, // opv state + .true, .false => unreachable, // runtime bits + .@"unreachable" => unreachable, // not a value + .null => { + // TODO: proper representation for this + try wip_nav.abbrevCode(.undefined_comptime_value); + try wip_nav.refType(.null); + }, + }, + .variable => unreachable, // not a value .int => |int| { try wip_nav.bigIntConstValue(.{ .sdata = .sdata_comptime_value, @@ -4202,7 +4260,7 @@ fn updateLazyValue( var byte_offset = ptr.byte_offset; const base_unit, const base_entry = while (true) { const base_ptr, const access: Access = base_ptr_access: switch (base_addr) { - .nav => |nav_index| break try wip_nav.getNavEntry(nav_index), + .nav => |nav_index| break try dwarf.getNavEntry(nav_index), .comptime_alloc, .comptime_field => unreachable, .uav => |uav| { const uav_ty: Type = .fromInterned(ip.typeOf(uav.val)); @@ -4319,17 +4377,7 @@ fn updateLazyValue( switch (optRepr(opt_child_type, zcu)) { .opv_null => try diw.writeUleb128(0), .unpacked => try wip_nav.blockValue(src_loc, .makeBool(opt.val != .none)), - .error_set => try wip_nav.blockValue(src_loc, .fromInterned(value_index)), - .pointer => if (opt_child_type.comptimeOnly(zcu)) { - var buf: [8]u8 = undefined; - const bytes = buf[0..@divExact(zcu.getTarget().ptrBitWidth(), 8)]; - dwarf.writeInt(bytes, switch (opt.val) { - .none => 0, - else => opt_child_type.ptrAlignment(zcu).toByteUnits().?, - }); - try diw.writeUleb128(bytes.len); - try diw.writeAll(bytes); - } else try wip_nav.blockValue(src_loc, .fromInterned(value_index)), + .error_set, .pointer => try wip_nav.blockValue(src_loc, .fromInterned(value_index)), } } if (opt.val != .none) child_field: { @@ -4457,7 +4505,8 @@ fn updateLazyValue( }, .memoized_call => unreachable, // not a value } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); + try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written()); + try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written()); } fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum { unpacked, opv_null, error_set, pointer } { @@ -4472,312 +4521,6 @@ fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum { unpacked, opv_null, err }; } -pub fn updateContainerType( - dwarf: *Dwarf, - pt: Zcu.PerThread, - type_index: InternPool.Index, -) UpdateError!void { - return dwarf.updateContainerTypeWriterError(pt, type_index) catch |err| switch (err) { - error.WriteFailed => error.OutOfMemory, - else => |e| e, - }; -} -fn updateContainerTypeWriterError( - dwarf: *Dwarf, - pt: Zcu.PerThread, - type_index: InternPool.Index, -) (UpdateError || Writer.Error)!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const ty: Type = .fromInterned(type_index); - const ty_src_loc = ty.srcLoc(zcu); - log.debug("updateContainerType({f})", .{ty.fmt(pt)}); - - const inst_info = ty.typeDeclInst(zcu).?.resolveFull(ip).?; - const file = zcu.fileByIndex(inst_info.file); - const unit = try dwarf.getUnit(file.mod.?); - const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, inst_info.file); - if (inst_info.inst == .main_struct_inst) { - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, type_index); - if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(unit); - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = unit, - .entry = type_gop.value_ptr.*, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, - }; - defer wip_nav.deinit(); - - const loaded_struct = ip.loadStructType(type_index); - - const diw = &wip_nav.debug_info.writer; - try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_file else .file); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_struct.name.toSlice(ip)); - if (loaded_struct.field_types.len > 0) { - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); - for (0..loaded_struct.field_types.len) |field_index| { - const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index); - const field_init = loaded_struct.field_defaults.getOrNone(ip, field_index); - assert(!(is_comptime and field_init == .none)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const has_runtime_bits, const has_comptime_state = switch (field_init) { - .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu), - }, - }; - try wip_nav.abbrevCode(if (is_comptime) - if (has_comptime_state) - .struct_field_comptime_comptime_state - else if (has_runtime_bits) - .struct_field_comptime_runtime_bits - else - .struct_field_comptime - else if (field_init != .none) - if (has_comptime_state) - .struct_field_default_comptime_state - else if (has_runtime_bits) - .struct_field_default_runtime_bits - else - .struct_field - else - .struct_field); - try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); - try wip_nav.refType(field_type); - if (!is_comptime) { - try diw.writeUleb128(loaded_struct.field_offsets.get(ip)[field_index]); - try diw.writeUleb128(loaded_struct.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - field_type.abiAlignment(zcu).toByteUnits().?); - } - if (has_comptime_state) - try wip_nav.refValue(.fromInterned(field_init)) - else if (has_runtime_bits) - try wip_nav.blockValue(ty_src_loc, .fromInterned(field_init)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try wip_nav.updateLazy(ty_src_loc); - } else { - { - // Note that changes to ZIR instruction tracking only need to update this code - // if a newly-tracked instruction can be a type's owner `zir_index`. - comptime assert(Zir.inst_tracking_version == 0); - - const decl_inst = file.zir.?.instructions.get(@intFromEnum(inst_info.inst)); - const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) { - .struct_init, .struct_init_ref, .struct_init_anon => .anon, - .extended => switch (decl_inst.data.extended.opcode) { - .struct_decl => file.zir.?.getStructDecl(inst_info.inst).name_strategy, - .union_decl => file.zir.?.getUnionDecl(inst_info.inst).name_strategy, - .enum_decl => file.zir.?.getEnumDecl(inst_info.inst).name_strategy, - .opaque_decl => file.zir.?.getOpaqueDecl(inst_info.inst).name_strategy, - - .reify_enum, - .reify_struct, - .reify_union, - => @enumFromInt(decl_inst.data.extended.small), - - else => unreachable, - }, - else => unreachable, - }; - if (name_strat == .parent) return; - } - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, type_index); - if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(unit); - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = unit, - .entry = type_gop.value_ptr.*, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, - }; - defer wip_nav.deinit(); - const diw = &wip_nav.debug_info.writer; - const name = try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)}); - defer dwarf.gpa.free(name); - - switch (ip.indexToKey(type_index)) { - .struct_type => { - const loaded_struct = ip.loadStructType(type_index); - switch (loaded_struct.layout) { - .auto, .@"extern" => { - try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_struct_type else .struct_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else { - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); - for (0..loaded_struct.field_types.len) |field_index| { - const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index); - const field_init = loaded_struct.field_defaults.getOrNone(ip, field_index); - assert(!(is_comptime and field_init == .none)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const has_runtime_bits, const has_comptime_state = switch (field_init) { - .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu), - }, - }; - try wip_nav.abbrevCode(if (is_comptime) - if (has_comptime_state) - .struct_field_comptime_comptime_state - else if (has_runtime_bits) - .struct_field_comptime_runtime_bits - else - .struct_field_comptime - else if (field_init != .none) - if (has_comptime_state) - .struct_field_default_comptime_state - else if (has_runtime_bits) - .struct_field_default_runtime_bits - else - .struct_field - else - .struct_field); - try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); - try wip_nav.refType(field_type); - if (!is_comptime) { - try diw.writeUleb128(loaded_struct.field_offsets.get(ip)[field_index]); - try diw.writeUleb128(loaded_struct.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - field_type.abiAlignment(zcu).toByteUnits().?); - } - if (has_comptime_state) - try wip_nav.refValue(.fromInterned(field_init)) - else if (has_runtime_bits) - try wip_nav.blockValue(ty_src_loc, .fromInterned(field_init)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - }, - .@"packed" => { - 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(name); - try wip_nav.refType(.fromInterned(loaded_struct.packed_backing_int_type)); - var field_bit_offset: u16 = 0; - for (0..loaded_struct.field_types.len) |field_index| { - try wip_nav.abbrevCode(.packed_struct_field); - try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(field_bit_offset); - field_bit_offset += @intCast(field_type.bitSize(zcu)); - } - if (loaded_struct.field_types.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - } - }, - .enum_type => { - const loaded_enum = ip.loadEnumType(type_index); - 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(name); - try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); - for (0..loaded_enum.field_names.len) |field_index| { - try wip_nav.enumConstValue(loaded_enum, .{ - .sdata = .signed_enum_field, - .udata = .unsigned_enum_field, - .block = .big_enum_field, - }, field_index); - try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); - } - if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - .union_type => { - const loaded_union = ip.loadUnionType(type_index); - 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(name); - const union_layout = Type.getUnionLayout(loaded_union, zcu); - try diw.writeUleb128(union_layout.abi_size); - try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); - const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); - if (loaded_union.has_runtime_tag) { - try wip_nav.abbrevCode(.tagged_union); - try wip_nav.infoSectionOffset( - .debug_info, - wip_nav.unit, - wip_nav.entry, - @intCast(diw.end + dwarf.sectionOffsetBytes()), - ); - { - try wip_nav.abbrevCode(.generated_field); - try wip_nav.strp("tag"); - try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type)); - try diw.writeUleb128(union_layout.tagOffset()); - - for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.enumConstValue(loaded_tag, .{ - .sdata = .signed_tagged_union_field, - .udata = .unsigned_tagged_union_field, - .block = .big_tagged_union_field, - }, field_index); - { - try wip_nav.abbrevCode(.struct_field); - try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(union_layout.payloadOffset()); - try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } else for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.abbrevCode(.untagged_union_field); - try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - if (loaded_union.field_types.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - .opaque_type => { - try wip_nav.abbrevCode(.empty_struct_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - try diw.writeByte(@intFromBool(true)); - }, - else => unreachable, - } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written()); - try wip_nav.updateLazy(ty_src_loc); - } -} - pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedInst.Index) UpdateError!void { const comp = dwarf.bin_file.comp; const io = comp.io; @@ -4840,14 +4583,15 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Erro const comp = dwarf.bin_file.comp; const io = comp.io; + // Update `anyerror` based on the finished global error set. { - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, .anyerror_type); - if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(.main); + const index = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, .anyerror_type); + const unit, const entry = dwarf.values.items[@intFromEnum(index)]; var wip_nav: WipNav = .{ .dwarf = dwarf, .pt = pt, - .unit = .main, - .entry = type_gop.value_ptr.*, + .unit = unit, + .entry = entry, .any_children = false, .func = .none, .func_sym_index = undefined, @@ -4858,7 +4602,6 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Erro .debug_info = .init(dwarf.gpa), .debug_line = .init(dwarf.gpa), .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, }; defer wip_nav.deinit(); const diw = &wip_nav.debug_info.writer; @@ -4876,7 +4619,7 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Erro } if (global_error_set_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try wip_nav.updateLazy(.unneeded); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } for (dwarf.mods.keys(), dwarf.mods.values()) |mod, *mod_info| { @@ -5324,6 +5067,8 @@ const AbbrevCode = enum { inferred_error_set_type, ptr_type, ptr_sentinel_type, + ptr_aligned_type, + ptr_aligned_sentinel_type, is_const, is_volatile, array_type, @@ -5960,12 +5705,29 @@ const AbbrevCode = enum { .tag = .pointer_type, .attrs = &.{ .{ .name, .strp }, - .{ .alignment, .udata }, .{ .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 }, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 85f37f88ce..dd4c2abd24 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1711,13 +1711,14 @@ pub fn updateContainerType( self: *Elf, pt: Zcu.PerThread, ty: InternPool.Index, + success: bool, ) link.File.UpdateContainerTypeError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } const zcu = pt.zcu; const gpa = zcu.gpa; - return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) { + return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => |e| { try zcu.failed_types.putNoClobber(gpa, ty, try Zcu.ErrorMsg.create( diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 9bf352c1b1..91133412e8 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1719,11 +1719,12 @@ pub fn updateContainerType( self: *ZigObject, pt: Zcu.PerThread, ty: InternPool.Index, + success: bool, ) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.dwarf) |*dwarf| try dwarf.updateContainerType(pt, ty); + if (self.dwarf) |*dwarf| try dwarf.updateContainerType(pt, ty, success); } fn updateLazySymbol(