Commit 3654ec8770

Andrew Kelley <andrew@ziglang.org>
2022-03-08 06:14:22
LLVM: add debug type lowering for ptr, slice, opaque, optional
also fix issue with memoization and recursiveness.
1 parent 40c0bdd
Changed files (1)
src
codegen
src/codegen/llvm.zig
@@ -1075,15 +1075,13 @@ pub const DeclGen = struct {
             },
             .Optional => {
                 var buf: Type.Payload.ElemType = undefined;
-                const child_type = t.optionalChild(&buf);
-                if (!child_type.hasRuntimeBits()) {
+                const child_ty = t.optionalChild(&buf);
+                if (!child_ty.hasRuntimeBits()) {
                     return dg.context.intType(1);
                 }
-                const payload_llvm_ty = try dg.llvmType(child_type);
+                const payload_llvm_ty = try dg.llvmType(child_ty);
                 if (t.isPtrLikeOptional()) {
                     return payload_llvm_ty;
-                } else if (!child_type.hasRuntimeBits()) {
-                    return dg.context.intType(1);
                 }
 
                 const fields: [2]*const llvm.Type = .{
@@ -1905,20 +1903,19 @@ pub const DeclGen = struct {
     }
 
     fn lowerDebugType(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType {
-        const gop = try dg.object.di_type_map.getOrPut(dg.gpa, ty);
+        const gpa = dg.gpa;
+        // Be careful not to reference this `gop` variable after any recursive calls
+        // to `lowerDebugType`.
+        const gop = try dg.object.di_type_map.getOrPut(gpa, ty);
+        if (gop.found_existing) return gop.value_ptr.*;
         errdefer assert(dg.object.di_type_map.remove(ty));
-        if (!gop.found_existing) {
-            gop.value_ptr.* = try lowerDebugTypeRaw(dg, ty);
-        }
-        return gop.value_ptr.*;
-    }
-
-    fn lowerDebugTypeRaw(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType {
         const target = dg.module.getTarget();
         const dib = dg.object.di_builder.?;
-        const gpa = dg.gpa;
         switch (ty.zigTypeTag()) {
-            .Void, .NoReturn => return dib.createBasicType("void", 0, DW.ATE.signed),
+            .Void, .NoReturn => {
+                gop.value_ptr.* = dib.createBasicType("void", 0, DW.ATE.signed);
+                return gop.value_ptr.*;
+            },
             .Int => {
                 const info = ty.intInfo(target);
                 assert(info.bits != 0);
@@ -1927,13 +1924,18 @@ pub const DeclGen = struct {
                     .signed => DW.ATE.signed,
                     .unsigned => DW.ATE.unsigned,
                 };
-                return dib.createBasicType(name, info.bits, dwarf_encoding);
+                gop.value_ptr.* = dib.createBasicType(name, info.bits, dwarf_encoding);
+                return gop.value_ptr.*;
             },
             .Enum => {
                 const owner_decl = ty.getOwnerDecl();
 
                 if (!ty.hasRuntimeBits()) {
-                    return dg.makeEmptyNamespaceDIType(owner_decl);
+                    const enum_di_ty = try dg.makeEmptyNamespaceDIType(owner_decl);
+                    // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
+                    // means we can't use `gop` anymore.
+                    try dg.object.di_type_map.put(gpa, ty, enum_di_ty);
+                    return enum_di_ty;
                 }
 
                 const field_names = ty.enumFields().keys();
@@ -1966,7 +1968,7 @@ pub const DeclGen = struct {
                 var buffer: Type.Payload.Bits = undefined;
                 const int_ty = ty.intTagType(&buffer);
 
-                return dib.createEnumerationType(
+                const enum_di_ty = dib.createEnumerationType(
                     di_scope,
                     name,
                     di_file,
@@ -1978,79 +1980,264 @@ pub const DeclGen = struct {
                     try lowerDebugType(dg, int_ty),
                     "",
                 );
+                // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, enum_di_ty);
+                return enum_di_ty;
             },
             .Float => {
                 const bits = ty.floatBits(target);
                 const name = try ty.nameAlloc(gpa); // TODO this is a leak
-                return dib.createBasicType(name, bits, DW.ATE.float);
+                gop.value_ptr.* = dib.createBasicType(name, bits, DW.ATE.float);
+                return gop.value_ptr.*;
+            },
+            .Bool => {
+                gop.value_ptr.* = dib.createBasicType("bool", 1, DW.ATE.boolean);
+                return gop.value_ptr.*;
             },
-            .Bool => return dib.createBasicType("bool", 1, DW.ATE.boolean),
             .Pointer => {
+                // Normalize everything that the debug info does not represent.
+                const ptr_info = ty.ptrInfo().data;
+
+                if (ptr_info.sentinel != null or
+                    ptr_info.@"addrspace" != .generic or
+                    ptr_info.bit_offset != 0 or
+                    ptr_info.host_size != 0 or
+                    ptr_info.@"allowzero" or
+                    !ptr_info.mutable or
+                    ptr_info.@"volatile" or
+                    ptr_info.size == .Many or ptr_info.size == .C)
+                {
+                    var payload: Type.Payload.Pointer = .{
+                        .data = .{
+                            .pointee_type = ptr_info.pointee_type,
+                            .sentinel = null,
+                            .@"align" = ptr_info.@"align",
+                            .@"addrspace" = .generic,
+                            .bit_offset = 0,
+                            .host_size = 0,
+                            .@"allowzero" = false,
+                            .mutable = true,
+                            .@"volatile" = false,
+                            .size = switch (ptr_info.size) {
+                                .Many, .C, .One => .One,
+                                .Slice => .Slice,
+                            },
+                        },
+                    };
+                    const bland_ptr_ty = Type.initPayload(&payload.base);
+                    const ptr_di_ty = try dg.lowerDebugType(bland_ptr_ty);
+                    // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                    try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
+                    return ptr_di_ty;
+                }
+
                 if (ty.isSlice()) {
-                    @panic("TODO debug info type for slices");
-                    //var buf: Type.SlicePtrFieldTypeBuffer = undefined;
-                    //const ptr_type = ty.slicePtrFieldType(&buf);
-
-                    //const fields: [2]*const llvm.Type = .{
-                    //    try dg.llvmType(ptr_type),
-                    //    try dg.llvmType(Type.usize),
-                    //};
-                    //return dg.context.structType(&fields, fields.len, .False);
+                    var buf: Type.SlicePtrFieldTypeBuffer = undefined;
+                    const ptr_ty = ty.slicePtrFieldType(&buf);
+                    const len_ty = Type.usize;
+
+                    const name = try ty.nameAlloc(gpa); // TODO this is a leak
+                    const di_file: ?*llvm.DIFile = null;
+                    const line = 0;
+                    const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
+                    const fwd_decl = dib.createReplaceableCompositeType(
+                        DW.TAG.structure_type,
+                        name.ptr,
+                        compile_unit_scope,
+                        di_file,
+                        line,
+                    );
+                    gop.value_ptr.* = fwd_decl;
+
+                    const ptr_size = ptr_ty.abiSize(target);
+                    const ptr_align = ptr_ty.abiAlignment(target);
+                    const len_size = len_ty.abiSize(target);
+                    const len_align = len_ty.abiAlignment(target);
+
+                    var offset: u64 = 0;
+                    offset += ptr_size;
+                    offset = std.mem.alignForwardGeneric(u64, offset, len_align);
+                    const len_offset = offset;
+
+                    const fields: [2]*llvm.DIType = .{
+                        dib.createMemberType(
+                            fwd_decl.toScope(),
+                            "ptr",
+                            di_file,
+                            line,
+                            ptr_size * 8, // size in bits
+                            ptr_align * 8, // align in bits
+                            0, // offset in bits
+                            0, // flags
+                            try dg.lowerDebugType(ptr_ty),
+                        ),
+                        dib.createMemberType(
+                            fwd_decl.toScope(),
+                            "len",
+                            di_file,
+                            line,
+                            len_size * 8, // size in bits
+                            len_align * 8, // align in bits
+                            len_offset * 8, // offset in bits
+                            0, // flags
+                            try dg.lowerDebugType(len_ty),
+                        ),
+                    };
+
+                    const replacement_di_type = dib.createStructType(
+                        compile_unit_scope,
+                        name.ptr,
+                        di_file,
+                        line,
+                        ty.abiSize(target) * 8, // size in bits
+                        ty.abiAlignment(target) * 8, // align in bits
+                        0, // flags
+                        null, // derived from
+                        &fields,
+                        fields.len,
+                        0, // run time lang
+                        null, // vtable holder
+                        "", // unique id
+                    );
+                    dib.replaceTemporary(fwd_decl, replacement_di_type);
+                    // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                    try dg.object.di_type_map.put(gpa, ty, replacement_di_type);
+                    return replacement_di_type;
                 }
 
-                const ptr_info = ty.ptrInfo().data;
                 const elem_di_ty = try lowerDebugType(dg, ptr_info.pointee_type);
                 const name = try ty.nameAlloc(gpa); // TODO this is a leak
-                return dib.createPointerType(
+                const ptr_di_ty = dib.createPointerType(
                     elem_di_ty,
                     target.cpu.arch.ptrBitWidth(),
                     ty.ptrAlignment(target) * 8,
                     name,
                 );
+                // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
+                return ptr_di_ty;
             },
             .Opaque => {
-                @panic("TODO debug info type for opaque");
+                const name = try ty.nameAlloc(gpa); // TODO this is a leak
+                const owner_decl = ty.getOwnerDecl();
+                const opaque_di_ty = dib.createForwardDeclType(
+                    DW.TAG.structure_type,
+                    name,
+                    try dg.namespaceToDebugScope(owner_decl.src_namespace),
+                    try dg.object.getDIFile(gpa, owner_decl.src_namespace.file_scope),
+                    owner_decl.src_node + 1,
+                );
+                // The recursive call to `lowerDebugType` va `namespaceToDebugScope`
+                // means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, opaque_di_ty);
+                return opaque_di_ty;
             },
             .Array => {
-                const elem_di_ty = try lowerDebugType(dg, ty.childType());
-                return dib.createArrayType(
+                const array_di_ty = dib.createArrayType(
                     ty.abiSize(target) * 8,
                     ty.abiAlignment(target) * 8,
-                    elem_di_ty,
+                    try lowerDebugType(dg, ty.childType()),
                     @intCast(c_int, ty.arrayLen()),
                 );
+                // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, array_di_ty);
+                return array_di_ty;
             },
             .Vector => {
                 @panic("TODO debug info type for vector");
             },
             .Optional => {
-                @panic("TODO debug info type for optional");
-                //var buf: Type.Payload.ElemType = undefined;
-                //const child_type = ty.optionalChild(&buf);
-                //if (!child_type.hasRuntimeBits()) {
-                //    return dg.context.intType(1);
-                //}
-                //const payload_llvm_ty = try dg.llvmType(child_type);
-                //if (ty.isPtrLikeOptional()) {
-                //    return payload_llvm_ty;
-                //} else if (!child_type.hasRuntimeBits()) {
-                //    return dg.context.intType(1);
-                //}
+                const name = try ty.nameAlloc(gpa); // TODO this is a leak
+                var buf: Type.Payload.ElemType = undefined;
+                const child_ty = ty.optionalChild(&buf);
+                if (!child_ty.hasRuntimeBits()) {
+                    gop.value_ptr.* = dib.createBasicType(name, 1, DW.ATE.boolean);
+                    return gop.value_ptr.*;
+                }
+                if (ty.isPtrLikeOptional()) {
+                    const ptr_di_ty = try dg.lowerDebugType(child_ty);
+                    // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                    try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
+                    return ptr_di_ty;
+                }
 
-                //const fields: [2]*const llvm.Type = .{
-                //    payload_llvm_ty, dg.context.intType(1),
-                //};
-                //return dg.context.structType(&fields, fields.len, .False);
+                const di_file: ?*llvm.DIFile = null;
+                const line = 0;
+                const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
+                const fwd_decl = dib.createReplaceableCompositeType(
+                    DW.TAG.structure_type,
+                    name.ptr,
+                    compile_unit_scope,
+                    di_file,
+                    line,
+                );
+                gop.value_ptr.* = fwd_decl;
+
+                const non_null_ty = Type.bool;
+                const payload_size = child_ty.abiSize(target);
+                const payload_align = child_ty.abiAlignment(target);
+                const non_null_size = non_null_ty.abiSize(target);
+                const non_null_align = non_null_ty.abiAlignment(target);
+
+                var offset: u64 = 0;
+                offset += payload_size;
+                offset = std.mem.alignForwardGeneric(u64, offset, non_null_align);
+                const non_null_offset = offset;
+
+                const fields: [2]*llvm.DIType = .{
+                    dib.createMemberType(
+                        fwd_decl.toScope(),
+                        "data",
+                        di_file,
+                        line,
+                        payload_size * 8, // size in bits
+                        payload_align * 8, // align in bits
+                        0, // offset in bits
+                        0, // flags
+                        try dg.lowerDebugType(child_ty),
+                    ),
+                    dib.createMemberType(
+                        fwd_decl.toScope(),
+                        "some",
+                        di_file,
+                        line,
+                        non_null_size * 8, // size in bits
+                        non_null_align * 8, // align in bits
+                        non_null_offset * 8, // offset in bits
+                        0, // flags
+                        try dg.lowerDebugType(non_null_ty),
+                    ),
+                };
+
+                const replacement_di_type = dib.createStructType(
+                    compile_unit_scope,
+                    name.ptr,
+                    di_file,
+                    line,
+                    ty.abiSize(target) * 8, // size in bits
+                    ty.abiAlignment(target) * 8, // align in bits
+                    0, // flags
+                    null, // derived from
+                    &fields,
+                    fields.len,
+                    0, // run time lang
+                    null, // vtable holder
+                    "", // unique id
+                );
+                dib.replaceTemporary(fwd_decl, replacement_di_type);
+                // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, replacement_di_type);
+                return replacement_di_type;
             },
             .ErrorUnion => {
                 const err_set_ty = ty.errorUnionSet();
-                const err_set_di_ty = try dg.lowerDebugType(err_set_ty);
                 const payload_ty = ty.errorUnionPayload();
                 if (!payload_ty.hasRuntimeBits()) {
+                    const err_set_di_ty = try dg.lowerDebugType(err_set_ty);
+                    // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                    try dg.object.di_type_map.put(gpa, ty, err_set_di_ty);
                     return err_set_di_ty;
                 }
-                const payload_di_ty = try dg.lowerDebugType(payload_ty);
-
                 const name = try ty.nameAlloc(gpa); // TODO this is a leak
                 const di_file: ?*llvm.DIFile = null;
                 const line = 0;
@@ -2062,6 +2249,7 @@ pub const DeclGen = struct {
                     di_file,
                     line,
                 );
+                gop.value_ptr.* = fwd_decl;
 
                 const err_set_size = err_set_ty.abiSize(target);
                 const err_set_align = err_set_ty.abiAlignment(target);
@@ -2083,7 +2271,7 @@ pub const DeclGen = struct {
                         err_set_align * 8, // align in bits
                         0, // offset in bits
                         0, // flags
-                        err_set_di_ty,
+                        try dg.lowerDebugType(err_set_ty),
                     ),
                     dib.createMemberType(
                         fwd_decl.toScope(),
@@ -2094,7 +2282,7 @@ pub const DeclGen = struct {
                         payload_align * 8, // align in bits
                         payload_offset * 8, // offset in bits
                         0, // flags
-                        payload_di_ty,
+                        try dg.lowerDebugType(payload_ty),
                     ),
                 };
 
@@ -2114,13 +2302,15 @@ pub const DeclGen = struct {
                     "", // unique id
                 );
                 dib.replaceTemporary(fwd_decl, replacement_di_type);
-
+                // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, replacement_di_type);
                 return replacement_di_type;
             },
             .ErrorSet => {
                 // TODO make this a proper enum with all the error codes in it.
                 // will need to consider how to take incremental compilation into account.
-                return dib.createBasicType("anyerror", 16, DW.ATE.unsigned);
+                gop.value_ptr.* = dib.createBasicType("anyerror", 16, DW.ATE.unsigned);
+                return gop.value_ptr.*;
             },
             .Struct => {
                 @panic("TODO debug info type for struct");
@@ -2347,11 +2537,14 @@ pub const DeclGen = struct {
                     }
                 }
 
-                return dib.createSubroutineType(
+                const fn_di_ty = dib.createSubroutineType(
                     param_di_types.items.ptr,
                     @intCast(c_int, param_di_types.items.len),
                     0,
                 );
+                // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                try dg.object.di_type_map.put(gpa, ty, fn_di_ty);
+                return fn_di_ty;
             },
             .ComptimeInt => unreachable,
             .ComptimeFloat => unreachable,