llvm: fix lowering of recursive debug info

This change allows recursing over types that are currently being
resolved fully with a second pass of forward resolution.

Closes #16414
This commit is contained in:
Jacob Young 2024-02-15 02:26:32 +01:00 committed by Andrew Kelley
parent c280811d1d
commit 6fe90a913a
2 changed files with 61 additions and 34 deletions

View File

@ -1928,17 +1928,26 @@ pub const Object = struct {
const gop = try o.di_type_map.getOrPut(gpa, ty.toIntern());
if (gop.found_existing) {
const annotated = gop.value_ptr.*;
const di_type = annotated.toDIType();
if (!annotated.isFwdOnly() or resolve == .fwd) {
return di_type;
switch (annotated) {
// This type is currently attempting to be resolved fully, so make
// sure a second recursion through the types uses forward resolution.
.null => assert(resolve == .fwd),
// This type already has at least forward resolution, only resolve
// fully during full resolution.
_ => {
const di_type = annotated.toDIType();
if (!annotated.isFwdOnly() or resolve == .fwd) {
return di_type;
}
const entry: Object.DITypeMap.Entry = .{
.key_ptr = gop.key_ptr,
.value_ptr = gop.value_ptr,
};
return o.lowerDebugTypeImpl(entry, resolve, di_type);
},
}
const entry: Object.DITypeMap.Entry = .{
.key_ptr = gop.key_ptr,
.value_ptr = gop.value_ptr,
};
return o.lowerDebugTypeImpl(entry, resolve, di_type);
}
errdefer assert(o.di_type_map.orderedRemove(ty.toIntern()));
} else gop.value_ptr.* = .null;
errdefer if (!gop.found_existing) assert(o.di_type_map.orderedRemove(ty.toIntern()));
const entry: Object.DITypeMap.Entry = .{
.key_ptr = gop.key_ptr,
.value_ptr = gop.value_ptr,
@ -2041,7 +2050,7 @@ pub const Object = struct {
ty.abiAlignment(mod).toByteUnits(0) * 8,
enumerators.ptr,
@intCast(enumerators.len),
try o.lowerDebugType(int_ty, .full),
try o.lowerDebugType(int_ty, resolve),
"",
);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
@ -2139,7 +2148,7 @@ pub const Object = struct {
ptr_align.toByteUnits(0) * 8, // align in bits
0, // offset in bits
0, // flags
try o.lowerDebugType(ptr_ty, .full),
try o.lowerDebugType(ptr_ty, resolve),
),
dib.createMemberType(
fwd_decl.toScope(),
@ -2150,7 +2159,7 @@ pub const Object = struct {
len_align.toByteUnits(0) * 8, // align in bits
len_offset * 8, // offset in bits
0, // flags
try o.lowerDebugType(len_ty, .full),
try o.lowerDebugType(len_ty, resolve),
),
};
@ -2214,7 +2223,7 @@ pub const Object = struct {
const array_di_ty = dib.createArrayType(
ty.abiSize(mod) * 8,
ty.abiAlignment(mod).toByteUnits(0) * 8,
try o.lowerDebugType(ty.childType(mod), .full),
try o.lowerDebugType(ty.childType(mod), resolve),
@intCast(ty.arrayLen(mod)),
);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
@ -2240,7 +2249,7 @@ pub const Object = struct {
break :blk dib.createBasicType(name, info.bits, dwarf_encoding);
},
.Bool => dib.createBasicType("bool", 1, DW.ATE.boolean),
else => try o.lowerDebugType(ty.childType(mod), .full),
else => try o.lowerDebugType(ty.childType(mod), resolve),
};
const vector_di_ty = dib.createVectorType(
@ -2307,7 +2316,7 @@ pub const Object = struct {
payload_align.toByteUnits(0) * 8, // align in bits
0, // offset in bits
0, // flags
try o.lowerDebugType(child_ty, .full),
try o.lowerDebugType(child_ty, resolve),
),
dib.createMemberType(
fwd_decl.toScope(),
@ -2318,7 +2327,7 @@ pub const Object = struct {
non_null_align.toByteUnits(0) * 8, // align in bits
non_null_offset * 8, // offset in bits
0, // flags
try o.lowerDebugType(non_null_ty, .full),
try o.lowerDebugType(non_null_ty, resolve),
),
};
@ -2345,7 +2354,7 @@ pub const Object = struct {
.ErrorUnion => {
const payload_ty = ty.errorUnionPayload(mod);
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full);
const err_set_di_ty = try o.lowerDebugType(Type.anyerror, resolve);
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(err_set_di_ty));
return err_set_di_ty;
@ -2399,7 +2408,7 @@ pub const Object = struct {
error_align.toByteUnits(0) * 8, // align in bits
error_offset * 8, // offset in bits
0, // flags
try o.lowerDebugType(Type.anyerror, .full),
try o.lowerDebugType(Type.anyerror, resolve),
);
fields[payload_index] = dib.createMemberType(
fwd_decl.toScope(),
@ -2410,7 +2419,7 @@ pub const Object = struct {
payload_align.toByteUnits(0) * 8, // align in bits
payload_offset * 8, // offset in bits
0, // flags
try o.lowerDebugType(payload_ty, .full),
try o.lowerDebugType(payload_ty, resolve),
);
const full_di_ty = dib.createStructType(
@ -2506,7 +2515,7 @@ pub const Object = struct {
field_align.toByteUnits(0) * 8, // align in bits
field_offset * 8, // offset in bits
0, // flags
try o.lowerDebugType(Type.fromInterned(field_ty), .full),
try o.lowerDebugType(Type.fromInterned(field_ty), resolve),
));
}
@ -2584,7 +2593,7 @@ pub const Object = struct {
const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
try ip.getOrPutStringFmt(gpa, "{d}", .{field_index});
const field_di_ty = try o.lowerDebugType(field_ty, .full);
const field_di_ty = try o.lowerDebugType(field_ty, resolve);
try di_fields.append(gpa, dib.createMemberType(
fwd_decl.toScope(),
@ -2653,7 +2662,7 @@ pub const Object = struct {
const layout = mod.getUnionLayout(union_obj);
if (layout.payload_size == 0) {
const tag_di_ty = try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty), .full);
const tag_di_ty = try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty), resolve);
const di_fields = [_]*llvm.DIType{tag_di_ty};
const full_di_ty = dib.createStructType(
compile_unit_scope,
@ -2689,7 +2698,7 @@ pub const Object = struct {
const field_size = Type.fromInterned(field_ty).abiSize(mod);
const field_align = mod.unionFieldNormalAlignment(union_obj, @intCast(field_index));
const field_di_ty = try o.lowerDebugType(Type.fromInterned(field_ty), .full);
const field_di_ty = try o.lowerDebugType(Type.fromInterned(field_ty), resolve);
const field_name = union_obj.field_names.get(ip)[field_index];
di_fields.appendAssumeCapacity(dib.createMemberType(
fwd_decl.toScope(),
@ -2751,7 +2760,7 @@ pub const Object = struct {
layout.tag_align.toByteUnits(0) * 8,
tag_offset * 8, // offset in bits
0, // flags
try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty), .full),
try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty), resolve),
);
const payload_di = dib.createMemberType(
@ -2802,21 +2811,21 @@ pub const Object = struct {
if (Type.fromInterned(fn_info.return_type).hasRuntimeBitsIgnoreComptime(mod)) {
const sret = firstParamSRet(fn_info, mod);
const di_ret_ty = if (sret) Type.void else Type.fromInterned(fn_info.return_type);
try param_di_types.append(try o.lowerDebugType(di_ret_ty, .full));
try param_di_types.append(try o.lowerDebugType(di_ret_ty, resolve));
if (sret) {
const ptr_ty = try mod.singleMutPtrType(Type.fromInterned(fn_info.return_type));
try param_di_types.append(try o.lowerDebugType(ptr_ty, .full));
try param_di_types.append(try o.lowerDebugType(ptr_ty, resolve));
}
} else {
try param_di_types.append(try o.lowerDebugType(Type.void, .full));
try param_di_types.append(try o.lowerDebugType(Type.void, resolve));
}
if (Type.fromInterned(fn_info.return_type).isError(mod) and
o.module.comp.config.any_error_tracing)
{
const ptr_ty = try mod.singleMutPtrType(try o.getStackTraceType());
try param_di_types.append(try o.lowerDebugType(ptr_ty, .full));
try param_di_types.append(try o.lowerDebugType(ptr_ty, resolve));
}
for (0..fn_info.param_types.len) |i| {
@ -2825,9 +2834,9 @@ pub const Object = struct {
if (isByRef(param_ty, mod)) {
const ptr_ty = try mod.singleMutPtrType(param_ty);
try param_di_types.append(try o.lowerDebugType(ptr_ty, .full));
try param_di_types.append(try o.lowerDebugType(ptr_ty, resolve));
} else {
try param_di_types.append(try o.lowerDebugType(param_ty, .full));
try param_di_types.append(try o.lowerDebugType(param_ty, resolve));
}
}
@ -11682,6 +11691,7 @@ const optional_layout_version = 3;
/// whether the type is fully resolved. Types that are only fwd declared
/// have the LSB flipped to a 1.
const AnnotatedDITypePtr = enum(usize) {
null,
_,
fn initFwd(di_type: *llvm.DIType) AnnotatedDITypePtr {
@ -11702,12 +11712,17 @@ const AnnotatedDITypePtr = enum(usize) {
}
fn toDIType(self: AnnotatedDITypePtr) *llvm.DIType {
const fixed_addr = @intFromEnum(self) & ~@as(usize, 1);
return @ptrFromInt(fixed_addr);
switch (self) {
.null => unreachable,
_ => return @ptrFromInt(@intFromEnum(self) & ~@as(usize, 1)),
}
}
fn isFwdOnly(self: AnnotatedDITypePtr) bool {
return @as(u1, @truncate(@intFromEnum(self))) != 0;
switch (self) {
.null => unreachable,
_ => return @as(u1, @truncate(@intFromEnum(self))) != 0,
}
}
};

View File

@ -2114,3 +2114,15 @@ test "initiate global variable with runtime value" {
};
try expect(S.some_struct.field == 1);
}
test "struct containing optional pointer to array of @This()" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const S = struct {
x: ?*const [1]@This(),
};
var s: S = .{ .x = &.{.{ .x = null }} };
_ = &s;
try expect(s.x.?[0].x == null);
}