Commit f4a357d720

Andrew Kelley <andrew@ziglang.org>
2022-03-29 01:20:38
stage2: finish debug info for unions in the LLVM backend
Sema: * queue full resolution of std.builtin.Type.Error when doing `@typeInfo` for error sets. LLVM backend: * change a TODO comment to a proper explanation of why debug info for structs is left as a fwd decl sometimes. * remove handling of packed unions which does not match the type information or constant generation code. * remove copy+pasted code * fix union debug info not matching the memory layout * remove unnecessary error checks and type casting
1 parent c546608
Changed files (2)
src
src/codegen/llvm.zig
@@ -1388,8 +1388,13 @@ pub const Object = struct {
                 if (ty.castTag(.@"struct")) |payload| {
                     const struct_obj = payload.data;
                     if (!struct_obj.haveFieldTypes()) {
-                        // TODO: improve the frontend to populate this struct.
-                        // For now we treat it as a zero bit type.
+                        // This can happen if a struct type makes it all the way to
+                        // flush() without ever being instantiated or referenced (even
+                        // via pointer). The only reason we are hearing about it now is
+                        // that it is being used as a namespace to put other debug types
+                        // into. Therefore we can satisfy this by making an empty namespace,
+                        // rather than changing the frontend to unnecessarily resolve the
+                        // struct field types.
                         const owner_decl = ty.getOwnerDecl();
                         const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
                         dib.replaceTemporary(fwd_decl, struct_di_ty);
@@ -1471,16 +1476,6 @@ pub const Object = struct {
                 const name = try ty.nameAlloc(gpa, target);
                 defer gpa.free(name);
 
-                if (ty.cast(Type.Payload.Union)) |payload| {
-                    const union_obj = payload.data;
-                    if (union_obj.layout == .Packed) {
-                        const bit_size = ty.bitSize(target);
-                        const di_ty = dib.createBasicType(name, bit_size, DW.ATE.unsigned);
-                        gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty);
-                        return di_ty;
-                    }
-                }
-
                 const fwd_decl = opt_fwd_decl orelse blk: {
                     const fwd_decl = dib.createReplaceableCompositeType(
                         DW.TAG.structure_type,
@@ -1494,12 +1489,7 @@ pub const Object = struct {
                     break :blk fwd_decl;
                 };
 
-                const union_obj = ty.cast(Type.Payload.Union).?.data;
-
-                // TODO COPYPASTE >>>
-                if (!union_obj.haveFieldTypes()) {
-                    // TODO: improve the frontend to populate this union.
-                    // For now we treat it as a zero bit type.
+                if (!ty.hasRuntimeBitsIgnoreComptime()) {
                     const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
                     dib.replaceTemporary(fwd_decl, union_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
@@ -1507,15 +1497,33 @@ pub const Object = struct {
                     try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target });
                     return union_di_ty;
                 }
-                // TODO <<<
 
-                if (!ty.hasRuntimeBitsIgnoreComptime()) {
-                    const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
-                    dib.replaceTemporary(fwd_decl, union_di_ty);
+                const layout = ty.unionGetLayout(target);
+                const union_obj = ty.cast(Type.Payload.Union).?.data;
+
+                if (layout.payload_size == 0) {
+                    const tag_di_ty = try o.lowerDebugType(union_obj.tag_ty, .full);
+                    const di_fields = [_]*llvm.DIType{tag_di_ty};
+                    const full_di_ty = dib.createStructType(
+                        compile_unit_scope,
+                        name.ptr,
+                        null, // file
+                        0, // line
+                        ty.abiSize(target) * 8, // size in bits
+                        ty.abiAlignment(target) * 8, // align in bits
+                        0, // flags
+                        null, // derived from
+                        &di_fields,
+                        di_fields.len,
+                        0, // run time lang
+                        null, // vtable holder
+                        "", // unique id
+                    );
+                    dib.replaceTemporary(fwd_decl, full_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target });
-                    return union_di_ty;
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                    return full_di_ty;
                 }
 
                 var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};
@@ -1523,8 +1531,8 @@ pub const Object = struct {
 
                 try di_fields.ensureUnusedCapacity(gpa, union_obj.fields.count());
 
-                var field_iterator = union_obj.fields.iterator();
-                while (field_iterator.next()) |kv| {
+                var it = union_obj.fields.iterator();
+                while (it.next()) |kv| {
                     const field_name = kv.key_ptr.*;
                     const field = kv.value_ptr.*;
 
@@ -1536,7 +1544,7 @@ pub const Object = struct {
                     const field_name_copy = try gpa.dupeZ(u8, field_name);
                     defer gpa.free(field_name_copy);
 
-                    try di_fields.append(gpa, dib.createMemberType(
+                    di_fields.appendAssumeCapacity(dib.createMemberType(
                         fwd_decl.toScope(),
                         field_name_copy,
                         null, // file
@@ -1549,31 +1557,11 @@ pub const Object = struct {
                     ));
                 }
 
-                const tag_ty = union_obj.tag_ty;
-                if (!tag_ty.hasRuntimeBitsIgnoreComptime()) {
-                    const union_di_ty = dib.createUnionType(
-                        compile_unit_scope,
-                        name.ptr,
-                        null, // file
-                        0, // line
-                        ty.abiSize(target) * 8, // size in bits
-                        ty.abiAlignment(target) * 8, // align in bits
-                        0, // flags
-                        di_fields.items.ptr,
-                        @intCast(c_int, di_fields.items.len),
-                        0, // run time lang
-                        "", // unique id
-                    );
-
-                    dib.replaceTemporary(fwd_decl, union_di_ty);
-                    // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target });
-                    return union_di_ty;
-                }
+                const union_name = if (layout.tag_size == 0) "AnonUnion" else name.ptr;
 
                 const union_di_ty = dib.createUnionType(
-                    fwd_decl.toScope(),
-                    "AnonUnion",
+                    compile_unit_scope,
+                    union_name,
                     null, // file
                     0, // line
                     ty.abiSize(target) * 8, // size in bits
@@ -1585,47 +1573,50 @@ pub const Object = struct {
                     "", // unique id
                 );
 
-                const payload_size = ty.abiSize(target);
-                const payload_align = ty.abiAlignment(target);
-                const tag_size = tag_ty.abiSize(target);
-                const tag_align = tag_ty.abiAlignment(target);
-
-                assert(tag_size > 0);
-                assert(tag_align > 0);
+                if (layout.tag_size == 0) {
+                    dib.replaceTemporary(fwd_decl, union_di_ty);
+                    // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target });
+                    return union_di_ty;
+                }
 
-                var offset: u64 = 0;
-                offset += payload_size;
-                offset = std.mem.alignForwardGeneric(u64, offset, tag_align);
-                const tag_offset = offset;
+                var tag_offset: u64 = undefined;
+                var payload_offset: u64 = undefined;
+                if (layout.tag_align >= layout.payload_align) {
+                    tag_offset = 0;
+                    payload_offset = std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align);
+                } else {
+                    payload_offset = 0;
+                    tag_offset = std.mem.alignForwardGeneric(u64, layout.payload_size, layout.tag_align);
+                }
 
-                const payload_di = dib.createMemberType(
+                const tag_di = dib.createMemberType(
                     fwd_decl.toScope(),
-                    "payload",
+                    "tag",
                     null, // file
                     0, // line
-                    payload_size * 8, // size in bits
-                    payload_align * 8, // align in bits
-                    0, // field_offset * 8, // offset in bits
+                    layout.tag_size * 8,
+                    layout.tag_align * 8, // align in bits
+                    tag_offset * 8, // offset in bits
                     0, // flags
-                    union_di_ty,
+                    try o.lowerDebugType(union_obj.tag_ty, .full),
                 );
 
-                const tag_di = dib.createMemberType(
+                const payload_di = dib.createMemberType(
                     fwd_decl.toScope(),
-                    "tag",
+                    "payload",
                     null, // file
                     0, // line
-                    tag_size * 8, // TODO: should this be multiplied by 8??? analyze.cpp:9237
-                    tag_align * 8, // align in bits
-                    tag_offset * 8, // offset in bits
+                    layout.payload_size * 8, // size in bits
+                    layout.payload_align * 8, // align in bits
+                    payload_offset * 8, // offset in bits
                     0, // flags
-                    try o.lowerDebugType(tag_ty, .full),
+                    union_di_ty,
                 );
 
-                const full_di_fields = [_]*llvm.DIType {
-                    payload_di,
-                    tag_di,
-                };
+                const full_di_fields: [2]*llvm.DIType =
+                    if (layout.tag_align >= layout.payload_align)
+                .{ tag_di, payload_di } else .{ payload_di, tag_di };
 
                 const full_di_ty = dib.createStructType(
                     compile_unit_scope,
@@ -1637,7 +1628,7 @@ pub const Object = struct {
                     0, // flags
                     null, // derived from
                     &full_di_fields,
-                    @intCast(c_int, full_di_fields.len),
+                    full_di_fields.len,
                     0, // run time lang
                     null, // vtable holder
                     "", // unique id
@@ -1646,60 +1637,6 @@ pub const Object = struct {
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
                 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
                 return full_di_ty;
-
-                //if (layout.payload_size == 0) {
-                //    const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty);
-                //    gop.value_ptr.* = enum_tag_llvm_ty;
-                //    return enum_tag_llvm_ty;
-                //}
-
-                //const name = try union_obj.getFullyQualifiedName(gpa);
-                //defer gpa.free(name);
-
-                //const llvm_union_ty = dg.context.structCreateNamed(name);
-                //gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls
-
-                //const aligned_field = union_obj.fields.values()[layout.most_aligned_field];
-                //const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty);
-
-                //const llvm_payload_ty = ty: {
-                //    if (layout.most_aligned_field_size == layout.payload_size) {
-                //        break :ty llvm_aligned_field_ty;
-                //    }
-                //    const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size);
-                //    const fields: [2]*const llvm.Type = .{
-                //        llvm_aligned_field_ty,
-                //        dg.context.intType(8).arrayType(padding_len),
-                //    };
-                //    break :ty dg.context.structType(&fields, fields.len, .True);
-                //};
-
-                //if (layout.tag_size == 0) {
-                //    var llvm_fields: [1]*const llvm.Type = .{llvm_payload_ty};
-                //    llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
-                //    return llvm_union_ty;
-                //}
-                //const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty);
-
-                //// Put the tag before or after the payload depending on which one's
-                //// alignment is greater.
-                //var llvm_fields: [3]*const llvm.Type = undefined;
-                //var llvm_fields_len: c_uint = 2;
-
-                //if (layout.tag_align >= layout.payload_align) {
-                //    llvm_fields = .{ enum_tag_llvm_ty, llvm_payload_ty, undefined };
-                //} else {
-                //    llvm_fields = .{ llvm_payload_ty, enum_tag_llvm_ty, undefined };
-                //}
-
-                //// Insert padding to make the LLVM struct ABI size match the Zig union ABI size.
-                //if (layout.padding != 0) {
-                //    llvm_fields[2] = dg.context.intType(8).arrayType(layout.padding);
-                //    llvm_fields_len = 3;
-                //}
-
-                //llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
-                //return llvm_union_ty;
             },
             .Fn => {
                 const fn_info = ty.fnInfo();
src/Sema.zig
@@ -11091,6 +11091,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 break :t try set_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
             };
 
+            try sema.queueFullTypeResolution(try error_field_ty.copy(sema.arena));
+
             // If the error set is inferred it has to be resolved at this point
             try sema.resolveInferredErrorSetTy(block, src, ty);