From c546608fcae4e36a593c4ff6c566b864d379e741 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 27 Mar 2022 14:37:13 +0200 Subject: [PATCH 1/2] stage2: LLVM: (WIP) add union fields debug info --- src/Module.zig | 16 +--- src/codegen/llvm.zig | 163 +++++++++++++++++++++++++++++++--- src/codegen/llvm/bindings.zig | 2 +- 3 files changed, 156 insertions(+), 25 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index e74d5f1dd8..ce93e091fd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1231,13 +1231,7 @@ pub const Union = struct { for (u.fields.values()) |field, i| { if (!field.ty.hasRuntimeBits()) continue; - const field_align = a: { - if (field.abi_align == 0) { - break :a field.ty.abiAlignment(target); - } else { - break :a field.abi_align; - } - }; + const field_align = field.normalAlignment(target); if (field_align > most_alignment) { most_alignment = field_align; most_index = i; @@ -1253,13 +1247,7 @@ pub const Union = struct { for (u.fields.values()) |field| { if (!field.ty.hasRuntimeBits()) continue; - const field_align = a: { - if (field.abi_align == 0) { - break :a field.ty.abiAlignment(target); - } else { - break :a field.abi_align; - } - }; + const field_align = field.normalAlignment(target); max_align = @maximum(max_align, field_align); } return max_align; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2e2e4ca819..e0fb3103c2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1465,11 +1465,22 @@ pub const Object = struct { return full_di_ty; }, .Union => { + const compile_unit_scope = o.di_compile_unit.?.toScope(); const owner_decl = ty.getOwnerDecl(); 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, @@ -1483,8 +1494,22 @@ pub const Object = struct { break :blk fwd_decl; }; - const TODO_implement_this = true; // TODO - if (TODO_implement_this or !ty.hasRuntimeBitsIgnoreComptime()) { + 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. + const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); + dib.replaceTemporary(fwd_decl, union_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; + } + // TODO <<< + + 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` @@ -1493,16 +1518,134 @@ pub const Object = struct { return union_di_ty; } - @panic("TODO debug info type for union"); - //const gop = try o.type_map.getOrPut(gpa, ty); - //if (gop.found_existing) return gop.value_ptr.*; + var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; + defer di_fields.deinit(gpa); - //// The Type memory is ephemeral; since we want to store a longer-lived - //// reference, we need to copy it here. - //gop.key_ptr.* = try ty.copy(o.type_map_arena.allocator()); + try di_fields.ensureUnusedCapacity(gpa, union_obj.fields.count()); - //const layout = ty.unionGetLayout(target); - //const union_obj = ty.cast(Type.Payload.Union).?.data; + var field_iterator = union_obj.fields.iterator(); + while (field_iterator.next()) |kv| { + const field_name = kv.key_ptr.*; + const field = kv.value_ptr.*; + + if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue; + + const field_size = field.ty.abiSize(target); + const field_align = field.normalAlignment(target); + + const field_name_copy = try gpa.dupeZ(u8, field_name); + defer gpa.free(field_name_copy); + + try di_fields.append(gpa, dib.createMemberType( + fwd_decl.toScope(), + field_name_copy, + null, // file + 0, // line + field_size * 8, // size in bits + field_align * 8, // align in bits + 0, // offset in bits + 0, // flags + try o.lowerDebugType(field.ty, .full), + )); + } + + 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_di_ty = dib.createUnionType( + fwd_decl.toScope(), + "AnonUnion", + 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 + ); + + 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); + + var offset: u64 = 0; + offset += payload_size; + offset = std.mem.alignForwardGeneric(u64, offset, tag_align); + const tag_offset = offset; + + const payload_di = dib.createMemberType( + fwd_decl.toScope(), + "payload", + null, // file + 0, // line + payload_size * 8, // size in bits + payload_align * 8, // align in bits + 0, // field_offset * 8, // offset in bits + 0, // flags + union_di_ty, + ); + + const tag_di = dib.createMemberType( + fwd_decl.toScope(), + "tag", + 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 + 0, // flags + try o.lowerDebugType(tag_ty, .full), + ); + + const full_di_fields = [_]*llvm.DIType { + payload_di, + tag_di, + }; + + 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 + &full_di_fields, + @intCast(c_int, full_di_fields.len), + 0, // run time lang + null, // vtable holder + "", // unique id + ); + dib.replaceTemporary(fwd_decl, full_di_ty); + // 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); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 33b862499c..b799d649b0 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -1601,7 +1601,7 @@ pub const DIBuilder = opaque { dib: *DIBuilder, scope: *DIScope, name: [*:0]const u8, - file: *DIFile, + file: ?*DIFile, line_number: c_uint, size_in_bits: u64, align_in_bits: u64, From f4a357d7209db61acdfcb24ecec316da66eb318d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Mar 2022 16:20:38 -0700 Subject: [PATCH 2/2] 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 --- src/Sema.zig | 2 + src/codegen/llvm.zig | 213 +++++++++++++++---------------------------- 2 files changed, 77 insertions(+), 138 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e79e358907..8842c74ca7 100644 --- a/src/Sema.zig +++ b/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); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e0fb3103c2..74de485721 100644 --- a/src/codegen/llvm.zig +++ b/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,21 +1489,6 @@ 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. - const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); - dib.replaceTemporary(fwd_decl, union_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; - } - // TODO <<< - if (!ty.hasRuntimeBitsIgnoreComptime()) { const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); dib.replaceTemporary(fwd_decl, union_di_ty); @@ -1518,13 +1498,41 @@ pub const Object = struct { return 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(full_di_ty), .{ .target = o.target }); + return full_di_ty; + } + var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; defer di_fields.deinit(gpa); 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); + 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; + } - assert(tag_size > 0); - assert(tag_align > 0); - - var offset: u64 = 0; - offset += payload_size; - offset = std.mem.alignForwardGeneric(u64, offset, tag_align); - const tag_offset = offset; - - const payload_di = dib.createMemberType( - fwd_decl.toScope(), - "payload", - null, // file - 0, // line - payload_size * 8, // size in bits - payload_align * 8, // align in bits - 0, // field_offset * 8, // offset in bits - 0, // flags - union_di_ty, - ); + 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 tag_di = dib.createMemberType( fwd_decl.toScope(), "tag", null, // file 0, // line - tag_size * 8, // TODO: should this be multiplied by 8??? analyze.cpp:9237 - tag_align * 8, // align in bits + layout.tag_size * 8, + layout.tag_align * 8, // align in bits tag_offset * 8, // offset in bits 0, // flags - try o.lowerDebugType(tag_ty, .full), + try o.lowerDebugType(union_obj.tag_ty, .full), ); - const full_di_fields = [_]*llvm.DIType { - payload_di, - tag_di, - }; + const payload_di = dib.createMemberType( + fwd_decl.toScope(), + "payload", + null, // file + 0, // line + layout.payload_size * 8, // size in bits + layout.payload_align * 8, // align in bits + payload_offset * 8, // offset in bits + 0, // flags + union_di_ty, + ); + + 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();