Skip to content

Commit 1f8b67b

Browse files
committed
[Analysis] Add DebugInfoCache analysis
Summary: The analysis simply primes and caches DebugInfoFinders for each DICompileUnit in a module. This allows (future) callers like CoroSplitPass to compute global debug info metadata (required for coroutine function cloning) much faster. Specifically, pay the price of DICompileUnit processing only once per compile unit, rather than once per coroutine. Test Plan: Added a smoke test for the new analysis ninja check-llvm-unit check-llvm stack-info: PR: llvm/llvm-project#118629, branch: users/artempyanykh/fast-coro-upstream/10
1 parent 1554948 commit 1f8b67b

File tree

8 files changed

+315
-1
lines changed

8 files changed

+315
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===- llvm/Analysis/DebugInfoCache.h - debug info cache --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains an analysis that builds a cache of debug info for each
10+
// DICompileUnit in a module.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_ANALYSIS_DEBUGINFOCACHE_H
15+
#define LLVM_ANALYSIS_DEBUGINFOCACHE_H
16+
17+
#include "llvm/IR/DebugInfo.h"
18+
#include "llvm/IR/PassManager.h"
19+
20+
namespace llvm {
21+
22+
/// Processes and caches debug info for each DICompileUnit in a module.
23+
///
24+
/// The result of the analysis is a set of DebugInfoFinders primed on their
25+
/// respective DICompileUnit. Such DebugInfoFinders can be used to speed up
26+
/// function cloning which otherwise requires an expensive traversal of
27+
/// DICompileUnit-level debug info. See an example usage in CoroSplit.
28+
class DebugInfoCache {
29+
public:
30+
using DIFinderCache = SmallDenseMap<const DICompileUnit *, DebugInfoFinder>;
31+
DIFinderCache Result;
32+
33+
DebugInfoCache(const Module &M);
34+
35+
bool invalidate(Module &, const PreservedAnalyses &,
36+
ModuleAnalysisManager::Invalidator &);
37+
};
38+
39+
class DebugInfoCacheAnalysis
40+
: public AnalysisInfoMixin<DebugInfoCacheAnalysis> {
41+
friend AnalysisInfoMixin<DebugInfoCacheAnalysis>;
42+
static AnalysisKey Key;
43+
44+
public:
45+
using Result = DebugInfoCache;
46+
Result run(Module &M, ModuleAnalysisManager &);
47+
};
48+
} // namespace llvm
49+
50+
#endif

llvm/include/llvm/IR/DebugInfo.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ class DebugInfoFinder {
120120
/// Process subprogram.
121121
void processSubprogram(DISubprogram *SP);
122122

123+
/// Process a compile unit.
124+
void processCompileUnit(DICompileUnit *CU);
125+
123126
/// Clear all lists.
124127
void reset();
125128

126129
private:
127-
void processCompileUnit(DICompileUnit *CU);
128130
void processScope(DIScope *Scope);
129131
void processType(DIType *DT);
130132
bool addCompileUnit(DICompileUnit *CU);

llvm/lib/Analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ add_llvm_component_library(LLVMAnalysis
5252
DDGPrinter.cpp
5353
ConstraintSystem.cpp
5454
Delinearization.cpp
55+
DebugInfoCache.cpp
5556
DemandedBits.cpp
5657
DependenceAnalysis.cpp
5758
DependenceGraphBuilder.cpp

llvm/lib/Analysis/DebugInfoCache.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===- llvm/Analysis/DebugInfoCache.cpp - debug info cache ----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains an analysis that builds a cache of debug info for each
10+
// DICompileUnit in a module.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "llvm/Analysis/DebugInfoCache.h"
15+
#include "llvm/IR/Module.h"
16+
17+
using namespace llvm;
18+
19+
namespace {
20+
DebugInfoFinder processCompileUnit(DICompileUnit *CU) {
21+
DebugInfoFinder DIFinder;
22+
DIFinder.processCompileUnit(CU);
23+
24+
return DIFinder;
25+
}
26+
} // namespace
27+
28+
DebugInfoCache::DebugInfoCache(const Module &M) {
29+
for (const auto CU : M.debug_compile_units()) {
30+
auto DIFinder = processCompileUnit(CU);
31+
Result[CU] = std::move(DIFinder);
32+
}
33+
}
34+
35+
bool DebugInfoCache::invalidate(Module &M, const PreservedAnalyses &PA,
36+
ModuleAnalysisManager::Invalidator &) {
37+
// Check whether the analysis has been explicitly invalidated. Otherwise, it's
38+
// stateless and remains preserved.
39+
auto PAC = PA.getChecker<DebugInfoCacheAnalysis>();
40+
return !PAC.preservedWhenStateless();
41+
}
42+
43+
AnalysisKey DebugInfoCacheAnalysis::Key;
44+
45+
DebugInfoCache DebugInfoCacheAnalysis::run(Module &M, ModuleAnalysisManager &) {
46+
return DebugInfoCache(M);
47+
}

llvm/lib/Passes/PassBuilder.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "llvm/Analysis/DDGPrinter.h"
3535
#include "llvm/Analysis/DXILMetadataAnalysis.h"
3636
#include "llvm/Analysis/DXILResource.h"
37+
#include "llvm/Analysis/DebugInfoCache.h"
3738
#include "llvm/Analysis/Delinearization.h"
3839
#include "llvm/Analysis/DemandedBits.h"
3940
#include "llvm/Analysis/DependenceAnalysis.h"

llvm/lib/Passes/PassRegistry.def

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
MODULE_ANALYSIS("callgraph", CallGraphAnalysis())
2222
MODULE_ANALYSIS("collector-metadata", CollectorMetadataAnalysis())
2323
MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis())
24+
MODULE_ANALYSIS("debug-info-cache", DebugInfoCacheAnalysis())
2425
MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis())
2526
MODULE_ANALYSIS("dxil-resource", DXILResourceAnalysis())
2627
MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis())

llvm/unittests/Analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ set(ANALYSIS_TEST_SOURCES
2525
ConstraintSystemTest.cpp
2626
CtxProfAnalysisTest.cpp
2727
DDGTest.cpp
28+
DebugInfoCacheTest.cpp
2829
DomTreeUpdaterTest.cpp
2930
DXILResourceTest.cpp
3031
GraphWriterTest.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
//===- DebugInfoCacheTest.cpp - DebugInfoCache unit tests -----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Analysis/DebugInfoCache.h"
10+
#include "llvm/AsmParser/Parser.h"
11+
#include "llvm/IR/Module.h"
12+
#include "llvm/IR/Verifier.h"
13+
#include "llvm/Support/SourceMgr.h"
14+
#include "llvm/Support/raw_ostream.h"
15+
#include "gtest/gtest.h"
16+
17+
namespace llvm {
18+
namespace {
19+
20+
// Forward declare the assembly
21+
extern StringRef MultiCUModule;
22+
23+
const DICompileUnit *findCU(const Module &M, StringRef FileName) {
24+
for (const auto CU : M.debug_compile_units()) {
25+
if (CU->getFilename() == FileName)
26+
return CU;
27+
}
28+
29+
return nullptr;
30+
}
31+
32+
class DebugInfoCacheTest : public testing::Test {
33+
protected:
34+
LLVMContext C;
35+
36+
std::unique_ptr<Module> makeModule(StringRef Assembly) {
37+
SMDiagnostic Err;
38+
auto M = parseAssemblyString(Assembly, Err, C);
39+
if (!M)
40+
Err.print("DebugInfoCacheTest", errs());
41+
42+
verifyModule(*M, &errs());
43+
return M;
44+
}
45+
};
46+
47+
TEST_F(DebugInfoCacheTest, TestEmpty) {
48+
auto M = makeModule("");
49+
DebugInfoCache DIC{*M};
50+
EXPECT_EQ(DIC.Result.size(), 0u);
51+
}
52+
53+
TEST_F(DebugInfoCacheTest, TestMultiCU) {
54+
auto M = makeModule(MultiCUModule);
55+
DebugInfoCache DIC{*M};
56+
EXPECT_EQ(DIC.Result.size(), 2u);
57+
58+
auto *File1CU = findCU(*M, "file1.cpp");
59+
EXPECT_NE(File1CU, nullptr);
60+
61+
auto File1DIFinder = DIC.Result.find(File1CU);
62+
EXPECT_NE(File1DIFinder, DIC.Result.end());
63+
64+
EXPECT_EQ(File1DIFinder->getSecond().compile_unit_count(), 1u);
65+
EXPECT_EQ(File1DIFinder->getSecond().type_count(), 6u);
66+
EXPECT_EQ(File1DIFinder->getSecond().subprogram_count(), 0u);
67+
EXPECT_EQ(File1DIFinder->getSecond().scope_count(), 1u);
68+
69+
auto *File2CU = findCU(*M, "file2.cpp");
70+
EXPECT_NE(File1CU, nullptr);
71+
72+
auto File2DIFinder = DIC.Result.find(File2CU);
73+
EXPECT_NE(File2DIFinder, DIC.Result.end());
74+
75+
EXPECT_EQ(File2DIFinder->getSecond().compile_unit_count(), 1u);
76+
EXPECT_EQ(File2DIFinder->getSecond().type_count(), 2u);
77+
EXPECT_EQ(File2DIFinder->getSecond().subprogram_count(), 0u);
78+
EXPECT_EQ(File2DIFinder->getSecond().scope_count(), 2u);
79+
}
80+
81+
/* Generated roughly by
82+
file1.cpp:
83+
struct file1_extern_type1;
84+
struct file1_extern_type2;
85+
86+
namespace file1 {
87+
typedef struct file1_type1 { int x; float y; } file1_type1;
88+
file1_type1 global{0, 1.};
89+
} // file1
90+
91+
extern struct file1_extern_type1 *file1_extern_func1(struct
92+
file1_extern_type2*);
93+
94+
file1::file1_type1 file1_func1(file1::file1_type1 x) { return x; }
95+
--------
96+
file2.cpp:
97+
struct file2_extern_type1;
98+
struct file2_extern_type2;
99+
100+
namespace file2 {
101+
typedef struct file2_type1 { float x; float y; } file2_type1;
102+
enum class file2_type2 { opt1, opt2 };
103+
104+
namespace inner {
105+
file2_type2 inner_global{file2_type2::opt2};
106+
} // inner
107+
} // file2
108+
109+
extern struct file2_extern_type1 *file2_extern_func1(struct
110+
file2_extern_type2*);
111+
112+
file2::file2_type1 file2_func1(file2::file2_type1 x, file2::file2_type2 y) {
113+
return x; }
114+
--------
115+
$ clang -S -emit-llvm file*.cpp
116+
$ llvm-link -S -o single.ll file*.ll
117+
*/
118+
StringRef MultiCUModule = R"""(
119+
%"struct.file1::file1_type1" = type { i32, float }
120+
%"struct.file2::file2_type1" = type { float, float }
121+
122+
@_ZN5file16globalE = dso_local global %"struct.file1::file1_type1" { i32 0, float 1.000000e+00 }, align 4, !dbg !0
123+
@_ZN5file25inner12inner_globalE = dso_local global i32 1, align 4, !dbg !11
124+
125+
define dso_local i64 @_Z11file1_func1N5file111file1_type1E(i64 %0) !dbg !33 {
126+
%2 = alloca %"struct.file1::file1_type1", align 4
127+
%3 = alloca %"struct.file1::file1_type1", align 4
128+
store i64 %0, ptr %3, align 4
129+
#dbg_declare(ptr %3, !37, !DIExpression(), !38)
130+
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %2, ptr align 4 %3, i64 8, i1 false), !dbg !39
131+
%4 = load i64, ptr %2, align 4, !dbg !40
132+
ret i64 %4, !dbg !40
133+
}
134+
135+
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
136+
137+
define dso_local <2 x float> @_Z11file2_func1N5file211file2_type1ENS_11file2_type2E(<2 x float> %0, i32 noundef %1) !dbg !41 {
138+
%3 = alloca %"struct.file2::file2_type1", align 4
139+
%4 = alloca %"struct.file2::file2_type1", align 4
140+
%5 = alloca i32, align 4
141+
store <2 x float> %0, ptr %4, align 4
142+
#dbg_declare(ptr %4, !49, !DIExpression(), !50)
143+
store i32 %1, ptr %5, align 4
144+
#dbg_declare(ptr %5, !51, !DIExpression(), !52)
145+
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %3, ptr align 4 %4, i64 8, i1 false), !dbg !53
146+
%6 = load <2 x float>, ptr %3, align 4, !dbg !54
147+
ret <2 x float> %6, !dbg !54
148+
}
149+
150+
!llvm.dbg.cu = !{!20, !22}
151+
!llvm.ident = !{!25, !25}
152+
!llvm.module.flags = !{!26, !27, !28, !29, !30, !31, !32}
153+
154+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
155+
!1 = distinct !DIGlobalVariable(name: "global", linkageName: "_ZN5file16globalE", scope: !2, file: !3, line: 6, type: !4, isLocal: false, isDefinition: true)
156+
!2 = !DINamespace(name: "file1", scope: null)
157+
!3 = !DIFile(filename: "file1.cpp", directory: "")
158+
!4 = !DIDerivedType(tag: DW_TAG_typedef, name: "file1_type1", scope: !2, file: !3, line: 5, baseType: !5)
159+
!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "file1_type1", scope: !2, file: !3, line: 5, size: 64, flags: DIFlagTypePassByValue, elements: !6, identifier: "_ZTSN5file111file1_type1E")
160+
!6 = !{!7, !9}
161+
!7 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !5, file: !3, line: 5, baseType: !8, size: 32)
162+
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
163+
!9 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !5, file: !3, line: 5, baseType: !10, size: 32, offset: 32)
164+
!10 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
165+
!11 = !DIGlobalVariableExpression(var: !12, expr: !DIExpression())
166+
!12 = distinct !DIGlobalVariable(name: "inner_global", linkageName: "_ZN5file25inner12inner_globalE", scope: !13, file: !15, line: 9, type: !16, isLocal: false, isDefinition: true)
167+
!13 = !DINamespace(name: "inner", scope: !14)
168+
!14 = !DINamespace(name: "file2", scope: null)
169+
!15 = !DIFile(filename: "file2.cpp", directory: "")
170+
!16 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, name: "file2_type2", scope: !14, file: !15, line: 6, baseType: !8, size: 32, flags: DIFlagEnumClass, elements: !17, identifier: "_ZTSN5file211file2_type2E")
171+
!17 = !{!18, !19}
172+
!18 = !DIEnumerator(name: "opt1", value: 0)
173+
!19 = !DIEnumerator(name: "opt2", value: 1)
174+
!20 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !21, splitDebugInlining: false, nameTableKind: None)
175+
!21 = !{!0}
176+
!22 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !15, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !23, globals: !24, splitDebugInlining: false, nameTableKind: None)
177+
!23 = !{!16}
178+
!24 = !{!11}
179+
!25 = !{!"clang"}
180+
!26 = !{i32 7, !"Dwarf Version", i32 5}
181+
!27 = !{i32 2, !"Debug Info Version", i32 3}
182+
!28 = !{i32 1, !"wchar_size", i32 4}
183+
!29 = !{i32 8, !"PIC Level", i32 2}
184+
!30 = !{i32 7, !"PIE Level", i32 2}
185+
!31 = !{i32 7, !"uwtable", i32 2}
186+
!32 = !{i32 7, !"frame-pointer", i32 2}
187+
!33 = distinct !DISubprogram(name: "file1_func1", linkageName: "_Z11file1_func1N5file111file1_type1E", scope: !3, file: !3, line: 11, type: !34, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !20, retainedNodes: !36)
188+
!34 = !DISubroutineType(types: !35)
189+
!35 = !{!4, !4}
190+
!36 = !{}
191+
!37 = !DILocalVariable(name: "x", arg: 1, scope: !33, file: !3, line: 11, type: !4)
192+
!38 = !DILocation(line: 11, column: 51, scope: !33)
193+
!39 = !DILocation(line: 11, column: 63, scope: !33)
194+
!40 = !DILocation(line: 11, column: 56, scope: !33)
195+
!41 = distinct !DISubprogram(name: "file2_func1", linkageName: "_Z11file2_func1N5file211file2_type1ENS_11file2_type2E", scope: !15, file: !15, line: 15, type: !42, scopeLine: 15, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !22, retainedNodes: !36)
196+
!42 = !DISubroutineType(types: !43)
197+
!43 = !{!44, !44, !16}
198+
!44 = !DIDerivedType(tag: DW_TAG_typedef, name: "file2_type1", scope: !14, file: !15, line: 5, baseType: !45)
199+
!45 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "file2_type1", scope: !14, file: !15, line: 5, size: 64, flags: DIFlagTypePassByValue, elements: !46, identifier: "_ZTSN5file211file2_type1E")
200+
!46 = !{!47, !48}
201+
!47 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !45, file: !15, line: 5, baseType: !10, size: 32)
202+
!48 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !45, file: !15, line: 5, baseType: !10, size: 32, offset: 32)
203+
!49 = !DILocalVariable(name: "x", arg: 1, scope: !41, file: !15, line: 15, type: !44)
204+
!50 = !DILocation(line: 15, column: 51, scope: !41)
205+
!51 = !DILocalVariable(name: "y", arg: 2, scope: !41, file: !15, line: 15, type: !16)
206+
!52 = !DILocation(line: 15, column: 73, scope: !41)
207+
!53 = !DILocation(line: 15, column: 85, scope: !41)
208+
!54 = !DILocation(line: 15, column: 78, scope: !41)
209+
)""";
210+
} // namespace
211+
} // namespace llvm

0 commit comments

Comments
 (0)