Modify emitTypeInformation to use MemoryTypeTableBuilder, take 2

This effectively revers commit r270389 and re-lands r270106, but it's
almost a rewrite.

The behavior change in r270106 was that we could no longer assume that
each LF_FUNC_ID record got its own type index. This patch adds a map
from DINode* to TypeIndex, so we can stop making that assumption.

This change also emits padding bytes between type records similar to the
way MSVC does. The size of the type record includes the padding bytes.

llvm-svn: 270485
This commit is contained in:
Reid Kleckner 2016-05-23 20:23:46 +00:00
parent 91f6f07bb8
commit 2280f9325e
5 changed files with 223 additions and 86 deletions

View File

@ -46,6 +46,8 @@ private:
public:
MemoryTypeTableBuilder() {}
bool empty() const { return Records.empty(); }
template <typename TFunc> void ForEachRecord(TFunc Func) {
uint32_t Index = TypeIndex::FirstNonSimpleIndex;

View File

@ -113,14 +113,34 @@ CodeViewDebug::getInlineSite(const DILocation *InlinedAt,
if (SiteInsertion.second) {
Site->SiteFuncId = NextFuncId++;
Site->Inlinee = Inlinee;
auto InlineeInsertion =
SubprogramIndices.insert({Inlinee, InlinedSubprograms.size()});
if (InlineeInsertion.second)
InlinedSubprograms.push_back(Inlinee);
InlinedSubprograms.insert(Inlinee);
recordFuncIdForSubprogram(Inlinee);
}
return *Site;
}
TypeIndex CodeViewDebug::getGenericFunctionTypeIndex() {
if (VoidFnTyIdx.getIndex() != 0)
return VoidFnTyIdx;
ArrayRef<TypeIndex> NoArgs;
ArgListRecord ArgListRec(TypeRecordKind::ArgList, NoArgs);
TypeIndex ArgListIndex = TypeTable.writeArgList(ArgListRec);
ProcedureRecord Procedure(TypeIndex::Void(), CallingConvention::NearC,
FunctionOptions::None, 0, ArgListIndex);
VoidFnTyIdx = TypeTable.writeProcedure(Procedure);
return VoidFnTyIdx;
}
void CodeViewDebug::recordFuncIdForSubprogram(const DISubprogram *SP) {
TypeIndex ParentScope = TypeIndex(0);
StringRef DisplayName = SP->getDisplayName();
FuncIdRecord FuncId(ParentScope, getGenericFunctionTypeIndex(), DisplayName);
TypeIndex TI = TypeTable.writeFuncId(FuncId);
TypeIndices[SP] = TI;
}
void CodeViewDebug::recordLocalVariable(LocalVariable &&Var,
const DILocation *InlinedAt) {
if (InlinedAt) {
@ -244,72 +264,38 @@ static void emitNullTerminatedSymbolName(MCStreamer &OS, StringRef S) {
}
void CodeViewDebug::emitTypeInformation() {
// Do nothing if we have no debug info or no inlined subprograms. The types
// we currently emit exist only to support inlined call site info.
// Do nothing if we have no debug info or if no non-trivial types were emitted
// to TypeTable during codegen.
NamedMDNode *CU_Nodes =
MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
if (!CU_Nodes)
return;
if (InlinedSubprograms.empty())
if (TypeTable.empty())
return;
// Start the .debug$T section with 0x4.
OS.SwitchSection(Asm->getObjFileLowering().getCOFFDebugTypesSection());
OS.AddComment("Debug section magic");
OS.EmitValueToAlignment(4);
OS.EmitIntValue(COFF::DEBUG_SECTION_MAGIC, 4);
// This type info currently only holds function ids for use with inline call
// frame info. All functions are assigned a simple 'void ()' type. Emit that
// type here.
unsigned ArgListIndex = getNextTypeIndex();
OS.AddComment("Type record length");
OS.EmitIntValue(ArgListRecord::getLayoutSize(), 2);
OS.AddComment("Leaf type: LF_ARGLIST");
OS.EmitIntValue(LF_ARGLIST, 2);
OS.AddComment("Number of arguments");
OS.EmitIntValue(0, 4);
unsigned VoidFnTyIdx = getNextTypeIndex();
OS.AddComment("Type record length");
OS.EmitIntValue(ProcedureRecord::getLayoutSize(), 2);
OS.AddComment("Leaf type: LF_PROCEDURE");
OS.EmitIntValue(LF_PROCEDURE, 2);
OS.AddComment("Return type index");
OS.EmitIntValue(TypeIndex::Void().getIndex(), 4);
OS.AddComment("Calling convention");
OS.EmitIntValue(char(CallingConvention::NearC), 1);
OS.AddComment("Function options");
OS.EmitIntValue(char(FunctionOptions::None), 1);
OS.AddComment("# of parameters");
OS.EmitIntValue(0, 2);
OS.AddComment("Argument list type index");
OS.EmitIntValue(ArgListIndex, 4);
// Emit LF_FUNC_ID records for all inlined subprograms to the type stream.
// Allocate one type index for each func id.
unsigned NextIdx = getNextTypeIndex(InlinedSubprograms.size());
(void)NextIdx;
assert(NextIdx == FuncIdTypeIndexStart && "func id type indices broken");
for (auto *SP : InlinedSubprograms) {
StringRef DisplayName = SP->getDisplayName();
OS.AddComment("Type record length");
MCSymbol *FuncBegin = MMI->getContext().createTempSymbol(),
*FuncEnd = MMI->getContext().createTempSymbol();
OS.emitAbsoluteSymbolDiff(FuncEnd, FuncBegin, 2);
OS.EmitLabel(FuncBegin);
OS.AddComment("Leaf type: LF_FUNC_ID");
OS.EmitIntValue(LF_FUNC_ID, 2);
OS.AddComment("Scope type index");
OS.EmitIntValue(0, 4);
OS.AddComment("Function type");
OS.EmitIntValue(VoidFnTyIdx, 4);
{
OS.AddComment("Function name");
emitNullTerminatedSymbolName(OS, DisplayName);
}
OS.EmitLabel(FuncEnd);
}
TypeTable.ForEachRecord(
[&](TypeIndex Index, const MemoryTypeTableBuilder::Record *R) {
// Each record should be 4 byte aligned. We achieve that by emitting
// LF_PAD padding bytes. The on-disk record size includes the padding
// bytes so that consumers don't have to skip past them.
uint64_t RecordSize = R->size() + 2;
uint64_t AlignedSize = alignTo(RecordSize, 4);
uint64_t AlignedRecordSize = AlignedSize - 2;
assert(AlignedRecordSize < (1 << 16) && "type record size overflow");
OS.AddComment("Type record length");
OS.EmitIntValue(AlignedRecordSize, 2);
OS.AddComment("Type record data");
OS.EmitBytes(StringRef(R->data(), R->size()));
// Pad the record with LF_PAD bytes.
for (unsigned I = AlignedSize - RecordSize; I > 0; --I)
OS.EmitIntValue(LF_PAD0 + I, 1);
});
}
void CodeViewDebug::emitInlineeFuncIdsAndLines() {
@ -330,8 +316,10 @@ void CodeViewDebug::emitInlineeFuncIdsAndLines() {
OS.AddComment("Inlinee lines signature");
OS.EmitIntValue(unsigned(InlineeLinesSignature::Normal), 4);
unsigned InlineeIndex = FuncIdTypeIndexStart;
for (const DISubprogram *SP : InlinedSubprograms) {
assert(TypeIndices.count(SP));
TypeIndex InlineeIdx = TypeIndices[SP];
OS.AddBlankLine();
unsigned FileId = maybeRecordFile(SP->getFile());
OS.AddComment("Inlined function " + SP->getDisplayName() + " starts at " +
@ -341,14 +329,11 @@ void CodeViewDebug::emitInlineeFuncIdsAndLines() {
// 1.
unsigned FileOffset = (FileId - 1) * 8;
OS.AddComment("Type index of inlined function");
OS.EmitIntValue(InlineeIndex, 4);
OS.EmitIntValue(InlineeIdx.getIndex(), 4);
OS.AddComment("Offset into filechecksum table");
OS.EmitIntValue(FileOffset, 4);
OS.AddComment("Starting line number");
OS.EmitIntValue(SP->getLine(), 4);
// The next inlined subprogram has the next function id.
InlineeIndex++;
}
OS.EmitLabel(InlineEnd);
@ -371,8 +356,8 @@ void CodeViewDebug::emitInlinedCallSite(const FunctionInfo &FI,
MCSymbol *InlineBegin = MMI->getContext().createTempSymbol(),
*InlineEnd = MMI->getContext().createTempSymbol();
assert(SubprogramIndices.count(Site.Inlinee));
unsigned InlineeIdx = FuncIdTypeIndexStart + SubprogramIndices[Site.Inlinee];
assert(TypeIndices.count(Site.Inlinee));
TypeIndex InlineeIdx = TypeIndices[Site.Inlinee];
// SymbolRecord
OS.AddComment("Record length");
@ -386,7 +371,7 @@ void CodeViewDebug::emitInlinedCallSite(const FunctionInfo &FI,
OS.AddComment("PtrEnd");
OS.EmitIntValue(0, 4);
OS.AddComment("Inlinee type index");
OS.EmitIntValue(InlineeIdx, 4);
OS.EmitIntValue(InlineeIdx.getIndex(), 4);
unsigned FileId = maybeRecordFile(Site.Inlinee->getFile());
unsigned StartLineNum = Site.Inlinee->getLine();

View File

@ -20,6 +20,7 @@
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DebugLoc.h"
@ -34,6 +35,7 @@ class LexicalScope;
/// \brief Collects and handles line tables information in a CodeView format.
class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
MCStreamer &OS;
codeview::MemoryTypeTableBuilder TypeTable;
/// Represents the most general definition range.
struct LocalVarDefRange {
@ -103,20 +105,16 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
/// to be confused with type indices for LF_FUNC_ID records.
unsigned NextFuncId = 0;
/// The next available type index.
unsigned NextTypeIndex = llvm::codeview::TypeIndex::FirstNonSimpleIndex;
codeview::TypeIndex VoidFnTyIdx;
/// Get the next type index and reserve it. Can be used to reserve more than
/// one type index.
unsigned getNextTypeIndex(unsigned NumRecords = 1) {
unsigned Result = NextTypeIndex;
NextTypeIndex += NumRecords;
return Result;
}
/// Get a type index for a generic void function type.
codeview::TypeIndex getGenericFunctionTypeIndex();
InlineSite &getInlineSite(const DILocation *InlinedAt,
const DISubprogram *Inlinee);
void recordFuncIdForSubprogram(const DISubprogram *SP);
static void collectInlineSiteChildren(SmallVectorImpl<unsigned> &Children,
const FunctionInfo &FI,
const InlineSite &Site);
@ -128,18 +126,12 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
/// Map from DIFile to .cv_file id.
DenseMap<const DIFile *, unsigned> FileIdMap;
/// Map from subprogram to index in InlinedSubprograms.
DenseMap<const DISubprogram *, size_t> SubprogramIndices;
/// All inlined subprograms in the order they should be emitted.
SmallVector<const DISubprogram *, 4> InlinedSubprograms;
SmallSetVector<const DISubprogram *, 4> InlinedSubprograms;
/// The first type index that refers to an LF_FUNC_ID record. We have one
/// record per inlined subprogram.
/// FIXME: Keep in sync with emitTypeInformation until we buffer type records
/// on the side as we go. Once we buffer type records, we can allocate type
/// indices on demand without interleaving our assembly output.
unsigned FuncIdTypeIndexStart = NextTypeIndex + 2;
/// Map from DI metadata nodes to CodeView type indices. Primarily indexed by
/// DIType* and DISubprogram*.
DenseMap<const DINode *, codeview::TypeIndex> TypeIndices;
typedef std::map<const DIFile *, std::string> FileToFilepathMapTy;
FileToFilepathMapTy FileToFilepathMap;

View File

@ -0,0 +1,101 @@
; RUN: llc < %s -filetype=obj -o - | llvm-readobj -codeview -codeview-subsection-bytes | FileCheck %s
; Check how we pad out the LF_FUNC_ID records. The 00F3F2F1 bytes in LeafData are
; what's interesting here.
; CHECK: FuncId (0x1002) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: Name: a
; CHECK: LeafData (
; CHECK: 0000: {{.*}} 6100F2F1 |........a...|
; CHECK: )
; CHECK: }
; CHECK: FuncId (0x1003) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: Name: ab
; CHECK: LeafData (
; CHECK: 0000: {{.*}} 616200F1 |........ab..|
; CHECK: )
; CHECK: }
; CHECK: FuncId (0x1004) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: Name: abc
; CHECK: LeafData (
; CHECK: 0000: {{.*}} 61626300 |........abc.|
; CHECK: )
; CHECK: }
; CHECK: FuncId (0x1005) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: Name: abcd
; CHECK: LeafData (
; CHECK: 0000: {{.*}} 61626364 00F3F2F1 |........abcd....|
; CHECK: )
; CHECK: }
; C++ source used to generate the IR:
;
; extern volatile int x;
; static void a() { x++; }
; static void ab() { x++; }
; static void abc() { x++; }
; static void abcd() { x++; }
; int main() {
; a();
; ab();
; abc();
; abcd();
; }
; ModuleID = 't.cpp'
source_filename = "t.cpp"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"
@x = external global i32, align 4
; Function Attrs: norecurse nounwind
define i32 @main() #0 !dbg !6 {
entry:
store volatile i32 0, i32* @x, align 4, !dbg !11, !tbaa !16
store volatile i32 0, i32* @x, align 4, !dbg !20, !tbaa !16
store volatile i32 0, i32* @x, align 4, !dbg !23, !tbaa !16
store volatile i32 0, i32* @x, align 4, !dbg !26, !tbaa !16
ret i32 0, !dbg !29
}
attributes #0 = { norecurse nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4}
!llvm.ident = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 (trunk 270461) (llvm/trunk 270469)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "<stdin>", directory: "D:\5Csrc\5Cllvm\5Cbuild")
!2 = !{}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{!"clang version 3.9.0 (trunk 270461) (llvm/trunk 270469)"}
!6 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 6, type: !8, isLocal: false, isDefinition: true, scopeLine: 6, isOptimized: true, unit: !0, variables: !2)
!7 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvm\5Cbuild")
!8 = !DISubroutineType(types: !9)
!9 = !{!10}
!10 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
!11 = !DILocation(line: 2, scope: !12, inlinedAt: !15)
!12 = distinct !DISubprogram(name: "a", scope: !7, file: !7, line: 2, type: !13, isLocal: true, isDefinition: true, scopeLine: 2, isOptimized: true, unit: !0, variables: !2)
!13 = !DISubroutineType(types: !14)
!14 = !{null}
!15 = distinct !DILocation(line: 7, scope: !6)
!16 = !{!17, !17, i64 0}
!17 = !{!"int", !18, i64 0}
!18 = !{!"omnipotent char", !19, i64 0}
!19 = !{!"Simple C/C++ TBAA"}
!20 = !DILocation(line: 3, scope: !21, inlinedAt: !22)
!21 = distinct !DISubprogram(name: "ab", scope: !7, file: !7, line: 3, type: !13, isLocal: true, isDefinition: true, scopeLine: 3, isOptimized: true, unit: !0, variables: !2)
!22 = distinct !DILocation(line: 8, scope: !6)
!23 = !DILocation(line: 4, scope: !24, inlinedAt: !25)
!24 = distinct !DISubprogram(name: "abc", scope: !7, file: !7, line: 4, type: !13, isLocal: true, isDefinition: true, scopeLine: 4, isOptimized: true, unit: !0, variables: !2)
!25 = distinct !DILocation(line: 9, scope: !6)
!26 = !DILocation(line: 5, scope: !27, inlinedAt: !28)
!27 = distinct !DISubprogram(name: "abcd", scope: !7, file: !7, line: 5, type: !13, isLocal: true, isDefinition: true, scopeLine: 5, isOptimized: true, unit: !0, variables: !2)
!28 = distinct !DILocation(line: 10, scope: !6)
!29 = !DILocation(line: 11, scope: !6)

View File

@ -0,0 +1,57 @@
; RUN: llc -mtriple=x86_64-windows-msvc < %s -filetype=obj -o - | llvm-readobj - -codeview | FileCheck %s
; We should only get one func id record, and both inlinees should point to it,
; even though there are two DISubprograms.
; CHECK: FuncId (0x1002) {
; CHECK-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK-NEXT: ParentScope: 0x0
; CHECK-NEXT: FunctionType: void () (0x1001)
; CHECK-NEXT: Name: same_name
; CHECK-NEXT: }
; CHECK-NOT: Name: same_name
; CHECK: CodeViewDebugInfo [
; CHECK: Section: .debug$S
; CHECK: Subsection [
; CHECK: ProcStart {
; CHECK: DisplayName: main
; CHECK: }
; CHECK: InlineSite {
; CHECK: Inlinee: same_name (0x1002)
; CHECK: }
; CHECK: InlineSiteEnd {
; CHECK: }
; CHECK: InlineSite {
; CHECK: Inlinee: same_name (0x1002)
; CHECK: }
; CHECK: InlineSiteEnd {
; CHECK: }
; CHECK: ProcEnd
; CHECK: ]
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"
define void @main(i32* %i.i) {
store volatile i32 3, i32* %i.i, !dbg !6
store volatile i32 3, i32* %i.i, !dbg !19
ret void
}
!llvm.module.flags = !{!0, !1, !2}
!llvm.dbg.cu = !{!4}
!0 = !{i32 2, !"CodeView", i32 1}
!1 = !{i32 2, !"Debug Info Version", i32 3}
!2 = !{i32 6, !"Linker Options", !{}}
!4 = distinct !DICompileUnit(language: DW_LANG_D, file: !5, producer: "LDC (http://wiki.dlang.org/LDC)", isOptimized: false, runtimeVersion: 1, emissionKind: FullDebug)
!5 = !DIFile(filename: "opover2.d", directory: "C:\5CLDC\5Cninja-ldc\5C..\5Cldc\5Ctests\5Cd2\5Cdmd-testsuite\5Crunnable")
!6 = !DILocation(line: 302, column: 9, scope: !7, inlinedAt: !15)
!7 = distinct !DISubprogram(name: "same_name", linkageName: "same_name", scope: null, file: !5, line: 302, type: !8, isLocal: false, isDefinition: true, scopeLine: 302, flags: DIFlagPrototyped, isOptimized: false, unit: !4, variables: !{})
!8 = !DISubroutineType(types: !{})
!15 = distinct !DILocation(line: 333, column: 5, scope: !16)
!16 = distinct !DISubprogram(name: "main", linkageName: "main", scope: null, file: !5, line: 328, type: !8, isLocal: false, isDefinition: true, scopeLine: 328, flags: DIFlagPrototyped, isOptimized: false, unit: !4, variables: !{})
!19 = !DILocation(line: 308, column: 9, scope: !20, inlinedAt: !25)
!20 = distinct !DISubprogram(name: "same_name", linkageName: "same_name", scope: null, file: !5, line: 308, type: !8, isLocal: false, isDefinition: true, scopeLine: 308, flags: DIFlagPrototyped, isOptimized: false, unit: !4, variables: !{})
!25 = distinct !DILocation(line: 334, column: 5, scope: !16)