Commit 6fe90a913a

Jacob Young <jacobly0@users.noreply.github.com>
2024-02-15 02:26:32
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
1 parent c280811
Changed files (2)
src
codegen
test
behavior
src/codegen/llvm.zig
@@ -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,
+        }
     }
 };
 
test/behavior/struct.zig
@@ -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);
+}