Index: llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.h =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -144,6 +144,13 @@ /// always looked up in the normal TypeIndices map. DenseMap CompleteTypeIndices; + const DISubprogram *CurrentSubprogram = nullptr; + + // The UDTs we have seen while processing types; each entry is a pair of type + // index and type name. + std::vector> LocalUDTs, + GlobalUDTs; + typedef std::map FileToFilepathMapTy; FileToFilepathMapTy FileToFilepathMap; StringRef getFullFilepath(const DIFile *S); @@ -157,6 +164,13 @@ FileIdMap.clear(); FnDebugInfo.clear(); FileToFilepathMap.clear(); + LocalUDTs.clear(); + GlobalUDTs.clear(); + } + + void setCurrentSubprogram(const DISubprogram *SP) { + CurrentSubprogram = SP; + LocalUDTs.clear(); } /// Emit the magic version number at the start of a CodeView type or symbol @@ -171,6 +185,9 @@ void emitDebugInfoForGlobals(); + void emitDebugInfoForUDTs( + ArrayRef> UDTs); + void emitDebugInfoForGlobal(const DIGlobalVariable *DIGV, MCSymbol *GVSym); /// Opens a subsection of the given kind in a .debug$S codeview section. Index: llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -253,12 +253,20 @@ emitDebugInfoForFunction(P.first, P.second); // Emit global variable debug information. + setCurrentSubprogram(nullptr); emitDebugInfoForGlobals(); // Switch back to the generic .debug$S section after potentially processing // comdat symbol sections. switchToDebugSectionForSymbol(nullptr); + // Emit UDT records for any types used by global variables. + if (!GlobalUDTs.empty()) { + MCSymbol *SymbolsEnd = beginCVSubsection(ModuleSubstreamKind::Symbols); + emitDebugInfoForUDTs(GlobalUDTs); + endCVSubsection(SymbolsEnd); + } + // This subsection holds a file index to offset in string table table. OS.AddComment("File index to string table offset subsection"); OS.EmitCVFileChecksumsDirective(); @@ -457,7 +465,9 @@ switchToDebugSectionForSymbol(Fn); StringRef FuncName; - if (auto *SP = GV->getSubprogram()) + auto *SP = GV->getSubprogram(); + setCurrentSubprogram(SP); + if (SP != nullptr) FuncName = SP->getDisplayName(); // If our DISubprogram name is empty, use the mangled name. @@ -519,6 +529,9 @@ emitInlinedCallSite(FI, InlinedAt, I->second); } + if (SP != nullptr) + emitDebugInfoForUDTs(LocalUDTs); + // We're done with this function. OS.AddComment("Record length"); OS.EmitIntValue(0x0002, 2); @@ -755,15 +768,61 @@ } } +static const DISubprogram *getQualifiedNameComponents( + const DIScope *Scope, SmallVectorImpl &QualifiedNameComponents) { + const DISubprogram *ClosestSubprogram = nullptr; + while (Scope != nullptr) { + if (ClosestSubprogram == nullptr) + ClosestSubprogram = dyn_cast(Scope); + StringRef ScopeName = Scope->getName(); + if (!ScopeName.empty()) + QualifiedNameComponents.push_back(ScopeName); + Scope = Scope->getScope().resolve(); + } + return ClosestSubprogram; +} + +static std::string getQualifiedName(ArrayRef QualifiedNameComponents, + StringRef TypeName) { + std::string FullyQualifiedName; + for (StringRef QualifiedNameComponent : reverse(QualifiedNameComponents)) { + FullyQualifiedName.append(QualifiedNameComponent); + FullyQualifiedName.append("::"); + } + FullyQualifiedName.append(TypeName); + return FullyQualifiedName; +} + TypeIndex CodeViewDebug::lowerTypeAlias(const DIDerivedType *Ty) { - // TODO: MSVC emits a S_UDT record. DITypeRef UnderlyingTypeRef = Ty->getBaseType(); TypeIndex UnderlyingTypeIndex = getTypeIndex(UnderlyingTypeRef); + StringRef TypeName = Ty->getName(); + + SmallVector QualifiedNameComponents; + const DISubprogram *ClosestSubprogram = getQualifiedNameComponents( + Ty->getScope().resolve(), QualifiedNameComponents); + + if (ClosestSubprogram == nullptr) { + std::string FullyQualifiedName = + getQualifiedName(QualifiedNameComponents, TypeName); + GlobalUDTs.emplace_back(std::move(FullyQualifiedName), UnderlyingTypeIndex); + } else if (ClosestSubprogram == CurrentSubprogram) { + std::string FullyQualifiedName = + getQualifiedName(QualifiedNameComponents, TypeName); + LocalUDTs.emplace_back(std::move(FullyQualifiedName), UnderlyingTypeIndex); + } + // TODO: What if the ClosestSubprogram is neither null or the current + // subprogram? Currently, the UDT just gets dropped on the floor. + // + // The current behavior is not desirable. To get maximal fidelity, we would + // need to perform all type translation before beginning emission of .debug$S + // and then make LocalUDTs a member of FunctionInfo + if (UnderlyingTypeIndex == TypeIndex(SimpleTypeKind::Int32Long) && - Ty->getName() == "HRESULT") + TypeName == "HRESULT") return TypeIndex(SimpleTypeKind::HResult); if (UnderlyingTypeIndex == TypeIndex(SimpleTypeKind::UInt16Short) && - Ty->getName() == "wchar_t") + TypeName == "wchar_t") return TypeIndex(SimpleTypeKind::WideCharacter); return UnderlyingTypeIndex; } @@ -1307,6 +1366,26 @@ 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); + + OS.AddComment("Record kind: S_UDT"); + OS.EmitIntValue(unsigned(SymbolKind::S_UDT), 2); + + OS.AddComment("Type"); + OS.EmitIntValue(UDT.second.getIndex(), 4); + + emitNullTerminatedSymbolName(OS, UDT.first); + OS.EmitLabel(UDTRecordEnd); + } +} + void CodeViewDebug::emitDebugInfoForGlobals() { NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu"); for (const MDNode *Node : CUs->operands()) { Index: llvm/trunk/test/DebugInfo/COFF/typedef.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/typedef.ll +++ llvm/trunk/test/DebugInfo/COFF/typedef.ll @@ -8,6 +8,14 @@ ; CHECK: ] ; CHECK: VarName: foo ; CHECK: } +; CHECK: Subsection [ +; CHECK: SubSectionType: Symbols (0xF1) +; CHECK: SubSectionSize: 0xC +; CHECK: UDT { +; CHECK: Type: wchar_t (0x71) +; CHECK: UDTName: XYZ +; CHECK: } +; CHECK: ] target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" target triple = "i686-pc-windows-msvc18.0.0" Index: llvm/trunk/test/DebugInfo/COFF/udts.ll =================================================================== --- llvm/trunk/test/DebugInfo/COFF/udts.ll +++ llvm/trunk/test/DebugInfo/COFF/udts.ll @@ -0,0 +1,57 @@ +; RUN: llc < %s -filetype=obj | llvm-readobj - -codeview | FileCheck %s +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i686-pc-windows-msvc18.0.0" + +; C++ source to regenerate: +; $ cat t.cpp +; void f() { +; typedef int FOO; +; FOO f; +; } + +; CHECK: ProcStart { +; CHECK: DisplayName: f +; CHECK: LinkageName: ?f@@YAXXZ +; CHECK: } +; CHECK: UDT { +; CHECK-NEXT: Type: int (0x74) +; CHECK-NEXT: UDTName: f::FOO +; CHECK-NEXT: } +; CHECK-NEXT: ProcEnd { +; CHECK-NEXT: } + + +; Function Attrs: nounwind +define void @"\01?f@@YAXXZ"() #0 !dbg !6 { +entry: + %f = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %f, metadata !10, metadata !13), !dbg !14 + ret void, !dbg !15 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind "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" "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 } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.9.0 (trunk 272079) (llvm/trunk 271895)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "-", directory: "/usr/local/src") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{!"clang version 3.9.0 (trunk 272079) (llvm/trunk 271895)"} +!6 = distinct !DISubprogram(name: "f", linkageName: "\01?f@@YAXXZ", scope: !7, file: !7, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!7 = !DIFile(filename: "", directory: "/usr/local/src") +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !DILocalVariable(name: "f", scope: !6, file: !7, line: 4, type: !11) +!11 = !DIDerivedType(tag: DW_TAG_typedef, name: "FOO", scope: !6, file: !7, line: 3, baseType: !12) +!12 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!13 = !DIExpression() +!14 = !DILocation(line: 4, column: 5, scope: !6) +!15 = !DILocation(line: 5, column: 1, scope: !6)