Index: llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -416,6 +416,10 @@ return (Options & ClassOptions::Nested) != ClassOptions::None; } + bool isForwardRef() const { + return (Options & ClassOptions::ForwardReference) != ClassOptions::None; + } + uint16_t getMemberCount() const { return MemberCount; } ClassOptions getOptions() const { return Options; } TypeIndex getFieldList() const { return FieldList; } Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -187,8 +187,8 @@ // The UDTs we have seen while processing types; each entry is a pair of type // index and type name. - std::vector> LocalUDTs, - GlobalUDTs; + std::vector> LocalUDTs; + std::vector> GlobalUDTs; using FileToFilepathMapTy = std::map; FileToFilepathMapTy FileToFilepathMap; @@ -222,8 +222,13 @@ void emitDebugInfoForRetainedTypes(); - void emitDebugInfoForUDTs( - ArrayRef> UDTs); + void + emitDebugInfoForUDTs(ArrayRef> UDTs, + bool CreateSection); + + void emitOneUDT(StringRef Name, codeview::TypeIndex TI); + + codeview::TypeIndex getUDTReferralTI(const DIType *T); void emitDebugInfoForGlobal(const DIGlobalVariable *DIGV, const GlobalVariable *GV, MCSymbol *GVSym); @@ -266,7 +271,7 @@ codeview::TypeIndex getVBPTypeIndex(); - void addToUDTs(const DIType *Ty, codeview::TypeIndex TI); + void addToUDTs(const DIType *Ty); codeview::TypeIndex lowerType(const DIType *Ty, const DIType *ClassTy); codeview::TypeIndex lowerTypeAlias(const DIDerivedType *Ty); Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -463,11 +463,8 @@ switchToDebugSectionForSymbol(nullptr); // Emit UDT records for any types used by global variables. - if (!GlobalUDTs.empty()) { - MCSymbol *SymbolsEnd = beginCVSubsection(DebugSubsectionKind::Symbols); - emitDebugInfoForUDTs(GlobalUDTs); - endCVSubsection(SymbolsEnd); - } + if (!GlobalUDTs.empty()) + emitDebugInfoForUDTs(GlobalUDTs, true); // This subsection holds a file index to offset in string table table. OS.AddComment("File index to string table offset subsection"); @@ -861,7 +858,7 @@ } if (SP != nullptr) - emitDebugInfoForUDTs(LocalUDTs); + emitDebugInfoForUDTs(LocalUDTs, false); // We're done with this function. OS.AddComment("Record length"); @@ -1099,7 +1096,7 @@ } } -void CodeViewDebug::addToUDTs(const DIType *Ty, TypeIndex TI) { +void CodeViewDebug::addToUDTs(const DIType *Ty) { // Don't record empty UDTs. if (Ty->getName().empty()) return; @@ -1111,10 +1108,11 @@ std::string FullyQualifiedName = getQualifiedName(QualifiedNameComponents, getPrettyScopeName(Ty)); - if (ClosestSubprogram == nullptr) - GlobalUDTs.emplace_back(std::move(FullyQualifiedName), TI); - else if (ClosestSubprogram == CurrentSubprogram) - LocalUDTs.emplace_back(std::move(FullyQualifiedName), TI); + if (ClosestSubprogram == nullptr) { + GlobalUDTs.emplace_back(std::move(FullyQualifiedName), Ty); + } else if (ClosestSubprogram == CurrentSubprogram) { + LocalUDTs.emplace_back(std::move(FullyQualifiedName), Ty); + } // TODO: What if the ClosestSubprogram is neither null or the current // subprogram? Currently, the UDT just gets dropped on the floor. @@ -1172,7 +1170,7 @@ TypeIndex UnderlyingTypeIndex = getTypeIndex(UnderlyingTypeRef); StringRef TypeName = Ty->getName(); - addToUDTs(Ty, UnderlyingTypeIndex); + addToUDTs(Ty); if (UnderlyingTypeIndex == TypeIndex(SimpleTypeKind::Int32Long) && TypeName == "HRESULT") @@ -1764,7 +1762,7 @@ TypeTable.writeKnownType(USLR); } - addToUDTs(Ty, ClassTI); + addToUDTs(Ty); return ClassTI; } @@ -1803,7 +1801,7 @@ UdtSourceLineRecord USLR(UnionTI, SIRI, Ty->getLine()); TypeTable.writeKnownType(USLR); - addToUDTs(Ty, UnionTI); + addToUDTs(Ty); return UnionTI; } @@ -2202,24 +2200,95 @@ OS.EmitValueToAlignment(4); } -void CodeViewDebug::emitDebugInfoForUDTs( - ArrayRef> UDTs) { - for (const std::pair &UDT : UDTs) { - MCSymbol *UDTRecordBegin = MMI->getContext().createTempSymbol(), - *UDTRecordEnd = MMI->getContext().createTempSymbol(); - OS.AddComment("Record length"); - OS.emitAbsoluteSymbolDiff(UDTRecordEnd, UDTRecordBegin, 2); - OS.EmitLabel(UDTRecordBegin); +/// UDTs require special handling. While lowering types, we may end up +/// encountering a situation where we want to emit an S_UDT, but all we have +/// seen so far is a forward decl of the type that the S_UDT refers to. For +/// example, consider this code: +/// struct Foo; +/// typedef Foo Bar; +/// int f(Bar *B) { return (int)B; } +/// Here we need to emit an S_UDT for Bar which points to Foo, but we only +/// have enough information to make the S_UDT point to the forward decl for +/// Foo. After all types have been lowered for a module, if we *still* have +/// never seen a complete decl for the type referred to, we can forego emitting +/// an S_UDT record entirely. +/// We can be even more aggressive when an S_UDT refers to another derived type +/// (such as a pointer, etc) by dropping S_UDTs in situations where the type the +/// pointer refers to (etc) does not have a complete decl. +TypeIndex CodeViewDebug::getUDTReferralTI(const DIType *T) { + if (!T) + return TypeIndex::None(); + + if (const DIDerivedType *Ty = dyn_cast(T)) { + const DIType *BaseTy = cast(T)->getBaseType().resolve(); + TypeIndex BaseTI = getUDTReferralTI(BaseTy); + if (BaseTI == TypeIndex::None()) + return BaseTI; + + // If this S_UDT points to a typedef, the UDT should refer to the complete + // type index of the base type. + if (Ty->getTag() == dwarf::DW_TAG_typedef) + return BaseTI; + + // Otherwise (e.g. this S_UDT refers to an LF_POINTER, LF_MODIFIER, ETC) the + // UDT should refer to the complete type index of this type. + return getCompleteTypeIndex(T); + } + + // Always emit S_UDTs that refer to basic types as their type information is + // complete by definition + if (isa(T)) + return getTypeIndex(T); + + TypeIndex TI = getTypeIndex(T); + TypeIndex CTI = getCompleteTypeIndex(T); + // If the complete type index is different from the type index, then we have a + // forward decl and a complete decl, so emit it. + if (TI != CTI) + return CTI; + + // The type we have is a forward decl and we don't have a corresponding complete + // decl. Bail out and don't emit this S_UDT. + if (T->isForwardDecl()) + return TypeIndex::None(); + + return TI; +} - OS.AddComment("Record kind: S_UDT"); - OS.EmitIntValue(unsigned(SymbolKind::S_UDT), 2); +void CodeViewDebug::emitOneUDT(StringRef Name, TypeIndex TI) { + MCSymbol *UDTRecordBegin = MMI->getContext().createTempSymbol(), + *UDTRecordEnd = MMI->getContext().createTempSymbol(); + OS.AddComment("Record length"); + OS.emitAbsoluteSymbolDiff(UDTRecordEnd, UDTRecordBegin, 2); + OS.EmitLabel(UDTRecordBegin); + + OS.AddComment("Record kind: S_UDT"); + OS.EmitIntValue(unsigned(SymbolKind::S_UDT), 2); + + OS.AddComment("Type"); + OS.EmitIntValue(TI.getIndex(), 4); + + emitNullTerminatedSymbolName(OS, Name); + OS.EmitLabel(UDTRecordEnd); +} + +void CodeViewDebug::emitDebugInfoForUDTs( + ArrayRef> UDTs, bool CreateSection) { + MCSymbol *SymbolsEnd = nullptr; - OS.AddComment("Type"); - OS.EmitIntValue(UDT.second.getIndex(), 4); + for (const std::pair &UDT : UDTs) { + const DIType *T = UDT.second; + TypeIndex TI = getUDTReferralTI(T); + if (TI.isNoneType()) + continue; - emitNullTerminatedSymbolName(OS, UDT.first); - OS.EmitLabel(UDTRecordEnd); + if (!SymbolsEnd && CreateSection) + SymbolsEnd = beginCVSubsection(DebugSubsectionKind::Symbols); + emitOneUDT(UDT.first, TI); } + + if (SymbolsEnd) + endCVSubsection(SymbolsEnd); } void CodeViewDebug::emitDebugInfoForGlobals() { Index: llvm/test/DebugInfo/COFF/purge-typedef-udts.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/COFF/purge-typedef-udts.ll @@ -0,0 +1,120 @@ +; RUN: llc < %s -filetype=obj | llvm-readobj - -codeview | FileCheck %s +source_filename = "test/DebugInfo/COFF/purge-typedef-udts.ll" +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i686-pc-windows-msvc19.11.25506" + +; C++ source to regenerate: +; $ cat t.cpp +; struct Foo; +; struct Bar { +; Bar() {} +; int X; +; }; +; +; typedef Foo FooTypedef; +; typedef Bar BarTypedef; +; +; int func(void *F) { return 7; } +; int func(const FooTypedef *F) { return func((void*)F); } +; int func(const BarTypedef *B) { return func((void*)B->X); } + +; CHECK-NOT: UDTName: FooTypedef +; CHECK: UDTName: BarTypedef + +%struct.Foo = type opaque +%struct.Bar = type { i32 } + +; Function Attrs: noinline nounwind optnone +define i32 @"\01?func@@YAHPAX@Z"(i8* %F) #0 !dbg !10 { +entry: + %F.addr = alloca i8*, align 4 + store i8* %F, i8** %F.addr, align 4 + call void @llvm.dbg.declare(metadata i8** %F.addr, metadata !14, metadata !DIExpression()), !dbg !15 + ret i32 7, !dbg !16 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: noinline nounwind optnone +define i32 @"\01?func@@YAHPBUFoo@@@Z"(%struct.Foo* %F) #0 !dbg !17 { +entry: + %F.addr = alloca %struct.Foo*, align 4 + store %struct.Foo* %F, %struct.Foo** %F.addr, align 4 + call void @llvm.dbg.declare(metadata %struct.Foo** %F.addr, metadata !24, metadata !DIExpression()), !dbg !25 + %0 = load %struct.Foo*, %struct.Foo** %F.addr, align 4, !dbg !26 + %1 = bitcast %struct.Foo* %0 to i8*, !dbg !26 + %call = call i32 @"\01?func@@YAHPAX@Z"(i8* %1), !dbg !27 + ret i32 %call, !dbg !28 +} + +; Function Attrs: noinline nounwind optnone +define i32 @"\01?func@@YAHPBUBar@@@Z"(%struct.Bar* %B) #0 !dbg !29 { +entry: + %B.addr = alloca %struct.Bar*, align 4 + store %struct.Bar* %B, %struct.Bar** %B.addr, align 4 + call void @llvm.dbg.declare(metadata %struct.Bar** %B.addr, metadata !42, metadata !DIExpression()), !dbg !43 + %0 = load %struct.Bar*, %struct.Bar** %B.addr, align 4, !dbg !44 + %X = getelementptr inbounds %struct.Bar, %struct.Bar* %0, i32 0, i32 0, !dbg !45 + %1 = load i32, i32* %X, align 4, !dbg !45 + %2 = inttoptr i32 %1 to i8*, !dbg !46 + %call = call i32 @"\01?func@@YAHPAX@Z"(i8* %2), !dbg !47 + ret i32 %call, !dbg !48 +} + +attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 6.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3) +!1 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvmbuild\5Cninja", checksumkind: CSK_MD5, checksum: "27c44c8a5531845f61f582a24ef5c151") +!2 = !{} +!3 = !{!4} +!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 32) +!5 = !{i32 1, !"NumRegisterParameters", i32 0} +!6 = !{i32 2, !"CodeView", i32 1} +!7 = !{i32 2, !"Debug Info Version", i32 3} +!8 = !{i32 1, !"wchar_size", i32 2} +!9 = !{!"clang version 6.0.0 "} +!10 = distinct !DISubprogram(name: "func", linkageName: "\01?func@@YAHPAX@Z", scope: !1, file: !1, line: 10, type: !11, isLocal: false, isDefinition: true, scopeLine: 10, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !4} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DILocalVariable(name: "F", arg: 1, scope: !10, file: !1, line: 10, type: !4) +!15 = !DILocation(line: 10, column: 16, scope: !10) +!16 = !DILocation(line: 10, column: 21, scope: !10) +!17 = distinct !DISubprogram(name: "func", linkageName: "\01?func@@YAHPBUFoo@@@Z", scope: !1, file: !1, line: 11, type: !18, isLocal: false, isDefinition: true, scopeLine: 11, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!18 = !DISubroutineType(types: !19) +!19 = !{!13, !20} +!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 32) +!21 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !22) +!22 = !DIDerivedType(tag: DW_TAG_typedef, name: "FooTypedef", file: !1, line: 7, baseType: !23) +!23 = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !1, line: 1, flags: DIFlagFwdDecl, identifier: ".?AUFoo@@") +!24 = !DILocalVariable(name: "F", arg: 1, scope: !17, file: !1, line: 11, type: !20) +!25 = !DILocation(line: 11, column: 28, scope: !17) +!26 = !DILocation(line: 11, column: 52, scope: !17) +!27 = !DILocation(line: 11, column: 40, scope: !17) +!28 = !DILocation(line: 11, column: 33, scope: !17) +!29 = distinct !DISubprogram(name: "func", linkageName: "\01?func@@YAHPBUBar@@@Z", scope: !1, file: !1, line: 12, type: !30, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!30 = !DISubroutineType(types: !31) +!31 = !{!13, !32} +!32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 32) +!33 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !34) +!34 = !DIDerivedType(tag: DW_TAG_typedef, name: "BarTypedef", file: !1, line: 8, baseType: !35) +!35 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Bar", file: !1, line: 2, size: 32, elements: !36, identifier: ".?AUBar@@") +!36 = !{!37, !38} +!37 = !DIDerivedType(tag: DW_TAG_member, name: "X", scope: !35, file: !1, line: 4, baseType: !13, size: 32) +!38 = !DISubprogram(name: "Bar", scope: !35, file: !1, line: 3, type: !39, isLocal: false, isDefinition: false, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false) +!39 = !DISubroutineType(cc: DW_CC_BORLAND_thiscall, types: !40) +!40 = !{null, !41} +!41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 32, flags: DIFlagArtificial | DIFlagObjectPointer) +!42 = !DILocalVariable(name: "B", arg: 1, scope: !29, file: !1, line: 12, type: !32) +!43 = !DILocation(line: 12, column: 28, scope: !29) +!44 = !DILocation(line: 12, column: 52, scope: !29) +!45 = !DILocation(line: 12, column: 55, scope: !29) +!46 = !DILocation(line: 12, column: 45, scope: !29) +!47 = !DILocation(line: 12, column: 40, scope: !29) +!48 = !DILocation(line: 12, column: 33, scope: !29)