From 7d38fe334bd527dfb932f1a2a481f1ac3bfdbebf Mon Sep 17 00:00:00 2001 From: Artem Pianykh Date: Sun, 15 Sep 2024 10:51:38 -0700 Subject: [PATCH 1/3] [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 common debug info metadata (required for coroutine function cloning) much faster. Specifically, this means paying 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: https://github.com/llvm/llvm-project/pull/118629, branch: users/artempyanykh/fast-coro-upstream/10 --- llvm/include/llvm/Analysis/DebugInfoCache.h | 50 +++++ llvm/include/llvm/IR/DebugInfo.h | 4 +- llvm/lib/Analysis/CMakeLists.txt | 1 + llvm/lib/Analysis/DebugInfoCache.cpp | 47 ++++ llvm/lib/Passes/PassBuilder.cpp | 1 + llvm/lib/Passes/PassRegistry.def | 1 + llvm/unittests/Analysis/CMakeLists.txt | 1 + .../unittests/Analysis/DebugInfoCacheTest.cpp | 211 ++++++++++++++++++ 8 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 llvm/include/llvm/Analysis/DebugInfoCache.h create mode 100644 llvm/lib/Analysis/DebugInfoCache.cpp create mode 100644 llvm/unittests/Analysis/DebugInfoCacheTest.cpp diff --git a/llvm/include/llvm/Analysis/DebugInfoCache.h b/llvm/include/llvm/Analysis/DebugInfoCache.h new file mode 100644 index 0000000000000..dbd6802c99ea0 --- /dev/null +++ b/llvm/include/llvm/Analysis/DebugInfoCache.h @@ -0,0 +1,50 @@ +//===- llvm/Analysis/DebugInfoCache.h - debug info cache --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains an analysis that builds a cache of debug info for each +// DICompileUnit in a module. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_DEBUGINFOCACHE_H +#define LLVM_ANALYSIS_DEBUGINFOCACHE_H + +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +/// Processes and caches debug info for each DICompileUnit in a module. +/// +/// The result of the analysis is a set of DebugInfoFinders primed on their +/// respective DICompileUnit. Such DebugInfoFinders can be used to speed up +/// function cloning which otherwise requires an expensive traversal of +/// DICompileUnit-level debug info. See an example usage in CoroSplit. +class DebugInfoCache { +public: + using DIFinderCache = SmallDenseMap; + DIFinderCache Result; + + DebugInfoCache(const Module &M); + + bool invalidate(Module &, const PreservedAnalyses &, + ModuleAnalysisManager::Invalidator &); +}; + +class DebugInfoCacheAnalysis + : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + +public: + using Result = DebugInfoCache; + Result run(Module &M, ModuleAnalysisManager &); +}; +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h index 73f45c3769be4..11907fbb7f20b 100644 --- a/llvm/include/llvm/IR/DebugInfo.h +++ b/llvm/include/llvm/IR/DebugInfo.h @@ -120,11 +120,13 @@ class DebugInfoFinder { /// Process subprogram. void processSubprogram(DISubprogram *SP); + /// Process a compile unit. + void processCompileUnit(DICompileUnit *CU); + /// Clear all lists. void reset(); private: - void processCompileUnit(DICompileUnit *CU); void processScope(DIScope *Scope); void processType(DIType *DT); bool addCompileUnit(DICompileUnit *CU); diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index 0db5b80f336cb..db9a569e30156 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -52,6 +52,7 @@ add_llvm_component_library(LLVMAnalysis DDGPrinter.cpp ConstraintSystem.cpp Delinearization.cpp + DebugInfoCache.cpp DemandedBits.cpp DependenceAnalysis.cpp DependenceGraphBuilder.cpp diff --git a/llvm/lib/Analysis/DebugInfoCache.cpp b/llvm/lib/Analysis/DebugInfoCache.cpp new file mode 100644 index 0000000000000..c1a3e89f0a6cc --- /dev/null +++ b/llvm/lib/Analysis/DebugInfoCache.cpp @@ -0,0 +1,47 @@ +//===- llvm/Analysis/DebugInfoCache.cpp - debug info cache ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains an analysis that builds a cache of debug info for each +// DICompileUnit in a module. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/DebugInfoCache.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +namespace { +DebugInfoFinder processCompileUnit(DICompileUnit *CU) { + DebugInfoFinder DIFinder; + DIFinder.processCompileUnit(CU); + + return DIFinder; +} +} // namespace + +DebugInfoCache::DebugInfoCache(const Module &M) { + for (const auto CU : M.debug_compile_units()) { + auto DIFinder = processCompileUnit(CU); + Result[CU] = std::move(DIFinder); + } +} + +bool DebugInfoCache::invalidate(Module &M, const PreservedAnalyses &PA, + ModuleAnalysisManager::Invalidator &) { + // Check whether the analysis has been explicitly invalidated. Otherwise, it's + // stateless and remains preserved. + auto PAC = PA.getChecker(); + return !PAC.preservedWhenStateless(); +} + +AnalysisKey DebugInfoCacheAnalysis::Key; + +DebugInfoCache DebugInfoCacheAnalysis::run(Module &M, ModuleAnalysisManager &) { + return DebugInfoCache(M); +} diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 1e97cef22045d..24de17ffcd509 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -34,6 +34,7 @@ #include "llvm/Analysis/DDGPrinter.h" #include "llvm/Analysis/DXILMetadataAnalysis.h" #include "llvm/Analysis/DXILResource.h" +#include "llvm/Analysis/DebugInfoCache.h" #include "llvm/Analysis/Delinearization.h" #include "llvm/Analysis/DemandedBits.h" #include "llvm/Analysis/DependenceAnalysis.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 0eb050c8adb04..80d6610f42d79 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -21,6 +21,7 @@ MODULE_ANALYSIS("callgraph", CallGraphAnalysis()) MODULE_ANALYSIS("collector-metadata", CollectorMetadataAnalysis()) MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis()) +MODULE_ANALYSIS("debug-info-cache", DebugInfoCacheAnalysis()) MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis()) MODULE_ANALYSIS("dxil-resource-binding", DXILResourceBindingAnalysis()) MODULE_ANALYSIS("dxil-resource-type", DXILResourceTypeAnalysis()) diff --git a/llvm/unittests/Analysis/CMakeLists.txt b/llvm/unittests/Analysis/CMakeLists.txt index 76d16513d9341..73694a1d6ba29 100644 --- a/llvm/unittests/Analysis/CMakeLists.txt +++ b/llvm/unittests/Analysis/CMakeLists.txt @@ -25,6 +25,7 @@ set(ANALYSIS_TEST_SOURCES ConstraintSystemTest.cpp CtxProfAnalysisTest.cpp DDGTest.cpp + DebugInfoCacheTest.cpp DomTreeUpdaterTest.cpp DXILResourceTest.cpp GraphWriterTest.cpp diff --git a/llvm/unittests/Analysis/DebugInfoCacheTest.cpp b/llvm/unittests/Analysis/DebugInfoCacheTest.cpp new file mode 100644 index 0000000000000..b32e4cb543158 --- /dev/null +++ b/llvm/unittests/Analysis/DebugInfoCacheTest.cpp @@ -0,0 +1,211 @@ +//===- DebugInfoCacheTest.cpp - DebugInfoCache unit tests -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/DebugInfoCache.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace { + +// Forward declare the assembly +extern StringRef MultiCUModule; + +const DICompileUnit *findCU(const Module &M, StringRef FileName) { + for (const auto CU : M.debug_compile_units()) { + if (CU->getFilename() == FileName) + return CU; + } + + return nullptr; +} + +class DebugInfoCacheTest : public testing::Test { +protected: + LLVMContext C; + + std::unique_ptr makeModule(StringRef Assembly) { + SMDiagnostic Err; + auto M = parseAssemblyString(Assembly, Err, C); + if (!M) + Err.print("DebugInfoCacheTest", errs()); + + verifyModule(*M, &errs()); + return M; + } +}; + +TEST_F(DebugInfoCacheTest, TestEmpty) { + auto M = makeModule(""); + DebugInfoCache DIC{*M}; + EXPECT_EQ(DIC.Result.size(), 0u); +} + +TEST_F(DebugInfoCacheTest, TestMultiCU) { + auto M = makeModule(MultiCUModule); + DebugInfoCache DIC{*M}; + EXPECT_EQ(DIC.Result.size(), 2u); + + auto *File1CU = findCU(*M, "file1.cpp"); + EXPECT_NE(File1CU, nullptr); + + auto File1DIFinder = DIC.Result.find(File1CU); + EXPECT_NE(File1DIFinder, DIC.Result.end()); + + EXPECT_EQ(File1DIFinder->getSecond().compile_unit_count(), 1u); + EXPECT_EQ(File1DIFinder->getSecond().type_count(), 6u); + EXPECT_EQ(File1DIFinder->getSecond().subprogram_count(), 0u); + EXPECT_EQ(File1DIFinder->getSecond().scope_count(), 1u); + + auto *File2CU = findCU(*M, "file2.cpp"); + EXPECT_NE(File1CU, nullptr); + + auto File2DIFinder = DIC.Result.find(File2CU); + EXPECT_NE(File2DIFinder, DIC.Result.end()); + + EXPECT_EQ(File2DIFinder->getSecond().compile_unit_count(), 1u); + EXPECT_EQ(File2DIFinder->getSecond().type_count(), 2u); + EXPECT_EQ(File2DIFinder->getSecond().subprogram_count(), 0u); + EXPECT_EQ(File2DIFinder->getSecond().scope_count(), 2u); +} + +/* Generated roughly by +file1.cpp: +struct file1_extern_type1; +struct file1_extern_type2; + +namespace file1 { +typedef struct file1_type1 { int x; float y; } file1_type1; +file1_type1 global{0, 1.}; +} // file1 + +extern struct file1_extern_type1 *file1_extern_func1(struct +file1_extern_type2*); + +file1::file1_type1 file1_func1(file1::file1_type1 x) { return x; } +-------- +file2.cpp: +struct file2_extern_type1; +struct file2_extern_type2; + +namespace file2 { +typedef struct file2_type1 { float x; float y; } file2_type1; +enum class file2_type2 { opt1, opt2 }; + +namespace inner { +file2_type2 inner_global{file2_type2::opt2}; +} // inner +} // file2 + +extern struct file2_extern_type1 *file2_extern_func1(struct +file2_extern_type2*); + +file2::file2_type1 file2_func1(file2::file2_type1 x, file2::file2_type2 y) { +return x; } +-------- +$ clang -S -emit-llvm file*.cpp +$ llvm-link -S -o single.ll file*.ll +*/ +StringRef MultiCUModule = R"""( +%"struct.file1::file1_type1" = type { i32, float } +%"struct.file2::file2_type1" = type { float, float } + +@_ZN5file16globalE = dso_local global %"struct.file1::file1_type1" { i32 0, float 1.000000e+00 }, align 4, !dbg !0 +@_ZN5file25inner12inner_globalE = dso_local global i32 1, align 4, !dbg !11 + +define dso_local i64 @_Z11file1_func1N5file111file1_type1E(i64 %0) !dbg !33 { + %2 = alloca %"struct.file1::file1_type1", align 4 + %3 = alloca %"struct.file1::file1_type1", align 4 + store i64 %0, ptr %3, align 4 + #dbg_declare(ptr %3, !37, !DIExpression(), !38) + call void @llvm.memcpy.p0.p0.i64(ptr align 4 %2, ptr align 4 %3, i64 8, i1 false), !dbg !39 + %4 = load i64, ptr %2, align 4, !dbg !40 + ret i64 %4, !dbg !40 +} + +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) + +define dso_local <2 x float> @_Z11file2_func1N5file211file2_type1ENS_11file2_type2E(<2 x float> %0, i32 noundef %1) !dbg !41 { + %3 = alloca %"struct.file2::file2_type1", align 4 + %4 = alloca %"struct.file2::file2_type1", align 4 + %5 = alloca i32, align 4 + store <2 x float> %0, ptr %4, align 4 + #dbg_declare(ptr %4, !49, !DIExpression(), !50) + store i32 %1, ptr %5, align 4 + #dbg_declare(ptr %5, !51, !DIExpression(), !52) + call void @llvm.memcpy.p0.p0.i64(ptr align 4 %3, ptr align 4 %4, i64 8, i1 false), !dbg !53 + %6 = load <2 x float>, ptr %3, align 4, !dbg !54 + ret <2 x float> %6, !dbg !54 +} + +!llvm.dbg.cu = !{!20, !22} +!llvm.ident = !{!25, !25} +!llvm.module.flags = !{!26, !27, !28, !29, !30, !31, !32} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "global", linkageName: "_ZN5file16globalE", scope: !2, file: !3, line: 6, type: !4, isLocal: false, isDefinition: true) +!2 = !DINamespace(name: "file1", scope: null) +!3 = !DIFile(filename: "file1.cpp", directory: "") +!4 = !DIDerivedType(tag: DW_TAG_typedef, name: "file1_type1", scope: !2, file: !3, line: 5, baseType: !5) +!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") +!6 = !{!7, !9} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !5, file: !3, line: 5, baseType: !8, size: 32) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !5, file: !3, line: 5, baseType: !10, size: 32, offset: 32) +!10 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!11 = !DIGlobalVariableExpression(var: !12, expr: !DIExpression()) +!12 = distinct !DIGlobalVariable(name: "inner_global", linkageName: "_ZN5file25inner12inner_globalE", scope: !13, file: !15, line: 9, type: !16, isLocal: false, isDefinition: true) +!13 = !DINamespace(name: "inner", scope: !14) +!14 = !DINamespace(name: "file2", scope: null) +!15 = !DIFile(filename: "file2.cpp", directory: "") +!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") +!17 = !{!18, !19} +!18 = !DIEnumerator(name: "opt1", value: 0) +!19 = !DIEnumerator(name: "opt2", value: 1) +!20 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !21, splitDebugInlining: false, nameTableKind: None) +!21 = !{!0} +!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) +!23 = !{!16} +!24 = !{!11} +!25 = !{!"clang"} +!26 = !{i32 7, !"Dwarf Version", i32 5} +!27 = !{i32 2, !"Debug Info Version", i32 3} +!28 = !{i32 1, !"wchar_size", i32 4} +!29 = !{i32 8, !"PIC Level", i32 2} +!30 = !{i32 7, !"PIE Level", i32 2} +!31 = !{i32 7, !"uwtable", i32 2} +!32 = !{i32 7, !"frame-pointer", i32 2} +!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) +!34 = !DISubroutineType(types: !35) +!35 = !{!4, !4} +!36 = !{} +!37 = !DILocalVariable(name: "x", arg: 1, scope: !33, file: !3, line: 11, type: !4) +!38 = !DILocation(line: 11, column: 51, scope: !33) +!39 = !DILocation(line: 11, column: 63, scope: !33) +!40 = !DILocation(line: 11, column: 56, scope: !33) +!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) +!42 = !DISubroutineType(types: !43) +!43 = !{!44, !44, !16} +!44 = !DIDerivedType(tag: DW_TAG_typedef, name: "file2_type1", scope: !14, file: !15, line: 5, baseType: !45) +!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") +!46 = !{!47, !48} +!47 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !45, file: !15, line: 5, baseType: !10, size: 32) +!48 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !45, file: !15, line: 5, baseType: !10, size: 32, offset: 32) +!49 = !DILocalVariable(name: "x", arg: 1, scope: !41, file: !15, line: 15, type: !44) +!50 = !DILocation(line: 15, column: 51, scope: !41) +!51 = !DILocalVariable(name: "y", arg: 2, scope: !41, file: !15, line: 15, type: !16) +!52 = !DILocation(line: 15, column: 73, scope: !41) +!53 = !DILocation(line: 15, column: 85, scope: !41) +!54 = !DILocation(line: 15, column: 78, scope: !41) +)"""; +} // namespace +} // namespace llvm From d7736a0f47c8d2839e9c84944b54394804e5a905 Mon Sep 17 00:00:00 2001 From: Artem Pianykh Date: Wed, 29 Jan 2025 13:36:07 -0800 Subject: [PATCH 2/3] fixup! [Analysis] Add DebugInfoCache analysis --- llvm/include/llvm/Analysis/DebugInfoCache.h | 3 +- llvm/lib/Analysis/DebugInfoCache.cpp | 4 +- .../unittests/Analysis/DebugInfoCacheTest.cpp | 63 ++++++++++--------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/llvm/include/llvm/Analysis/DebugInfoCache.h b/llvm/include/llvm/Analysis/DebugInfoCache.h index dbd6802c99ea0..a566b95f70acb 100644 --- a/llvm/include/llvm/Analysis/DebugInfoCache.h +++ b/llvm/include/llvm/Analysis/DebugInfoCache.h @@ -27,8 +27,7 @@ namespace llvm { /// DICompileUnit-level debug info. See an example usage in CoroSplit. class DebugInfoCache { public: - using DIFinderCache = SmallDenseMap; - DIFinderCache Result; + SmallDenseMap Result; DebugInfoCache(const Module &M); diff --git a/llvm/lib/Analysis/DebugInfoCache.cpp b/llvm/lib/Analysis/DebugInfoCache.cpp index c1a3e89f0a6cc..d8bcd22bd878d 100644 --- a/llvm/lib/Analysis/DebugInfoCache.cpp +++ b/llvm/lib/Analysis/DebugInfoCache.cpp @@ -26,9 +26,9 @@ DebugInfoFinder processCompileUnit(DICompileUnit *CU) { } // namespace DebugInfoCache::DebugInfoCache(const Module &M) { - for (const auto CU : M.debug_compile_units()) { + for (auto *CU : M.debug_compile_units()) { auto DIFinder = processCompileUnit(CU); - Result[CU] = std::move(DIFinder); + Result.insert_or_assign(CU, std::move(DIFinder)); } } diff --git a/llvm/unittests/Analysis/DebugInfoCacheTest.cpp b/llvm/unittests/Analysis/DebugInfoCacheTest.cpp index b32e4cb543158..df311f4d9f80a 100644 --- a/llvm/unittests/Analysis/DebugInfoCacheTest.cpp +++ b/llvm/unittests/Analysis/DebugInfoCacheTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/DebugInfoCache.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" @@ -17,25 +18,46 @@ namespace llvm { namespace { -// Forward declare the assembly +// Forward declare the IR string extern StringRef MultiCUModule; -const DICompileUnit *findCU(const Module &M, StringRef FileName) { - for (const auto CU : M.debug_compile_units()) { - if (CU->getFilename() == FileName) - return CU; - } +DICompileUnit *findCU(const Module &M, StringRef FileName) { + auto CUs = M.debug_compile_units(); + auto Matching = llvm::find_if( + CUs, [&](auto *CU) { return CU->getFilename() == FileName; }); + return Matching != CUs.end() ? *Matching : nullptr; +} + +void checkEqualDI(const DebugInfoFinder &DIFinder1, + const DebugInfoFinder &DIFinder2) { + EXPECT_TRUE( + llvm::equal(DIFinder1.compile_units(), DIFinder2.compile_units())); + EXPECT_TRUE(llvm::equal(DIFinder1.types(), DIFinder2.types())); + EXPECT_TRUE(llvm::equal(DIFinder1.subprograms(), DIFinder2.subprograms())); + EXPECT_TRUE(llvm::equal(DIFinder1.scopes(), DIFinder2.scopes())); +} + +void checkCachedDISameAsFromScratch(llvm::Module &M, const DebugInfoCache &DIC, + StringRef CUName) { + auto *CU = findCU(M, CUName); + EXPECT_NE(CU, nullptr); + + auto CachedDIFinder = DIC.Result.find(CU); + EXPECT_NE(CachedDIFinder, DIC.Result.end()); - return nullptr; + DebugInfoFinder ExpectedDIFinder; + ExpectedDIFinder.processCompileUnit(CU); + + checkEqualDI(CachedDIFinder->getSecond(), ExpectedDIFinder); } class DebugInfoCacheTest : public testing::Test { protected: LLVMContext C; - std::unique_ptr makeModule(StringRef Assembly) { + std::unique_ptr makeModule(StringRef IR) { SMDiagnostic Err; - auto M = parseAssemblyString(Assembly, Err, C); + auto M = parseAssemblyString(IR, Err, C); if (!M) Err.print("DebugInfoCacheTest", errs()); @@ -55,27 +77,8 @@ TEST_F(DebugInfoCacheTest, TestMultiCU) { DebugInfoCache DIC{*M}; EXPECT_EQ(DIC.Result.size(), 2u); - auto *File1CU = findCU(*M, "file1.cpp"); - EXPECT_NE(File1CU, nullptr); - - auto File1DIFinder = DIC.Result.find(File1CU); - EXPECT_NE(File1DIFinder, DIC.Result.end()); - - EXPECT_EQ(File1DIFinder->getSecond().compile_unit_count(), 1u); - EXPECT_EQ(File1DIFinder->getSecond().type_count(), 6u); - EXPECT_EQ(File1DIFinder->getSecond().subprogram_count(), 0u); - EXPECT_EQ(File1DIFinder->getSecond().scope_count(), 1u); - - auto *File2CU = findCU(*M, "file2.cpp"); - EXPECT_NE(File1CU, nullptr); - - auto File2DIFinder = DIC.Result.find(File2CU); - EXPECT_NE(File2DIFinder, DIC.Result.end()); - - EXPECT_EQ(File2DIFinder->getSecond().compile_unit_count(), 1u); - EXPECT_EQ(File2DIFinder->getSecond().type_count(), 2u); - EXPECT_EQ(File2DIFinder->getSecond().subprogram_count(), 0u); - EXPECT_EQ(File2DIFinder->getSecond().scope_count(), 2u); + checkCachedDISameAsFromScratch(*M, DIC, "file1.cpp"); + checkCachedDISameAsFromScratch(*M, DIC, "file2.cpp"); } /* Generated roughly by From 9bdf9264262698da9da556c806daff5ee9f6f186 Mon Sep 17 00:00:00 2001 From: Artem Pianykh Date: Thu, 30 Jan 2025 12:13:32 -0800 Subject: [PATCH 3/3] fixup! fixup! [Analysis] Add DebugInfoCache analysis --- llvm/include/llvm/Analysis/DebugInfoCache.h | 3 --- llvm/lib/Analysis/DebugInfoCache.cpp | 8 -------- 2 files changed, 11 deletions(-) diff --git a/llvm/include/llvm/Analysis/DebugInfoCache.h b/llvm/include/llvm/Analysis/DebugInfoCache.h index a566b95f70acb..9d95d23ea3b3f 100644 --- a/llvm/include/llvm/Analysis/DebugInfoCache.h +++ b/llvm/include/llvm/Analysis/DebugInfoCache.h @@ -30,9 +30,6 @@ class DebugInfoCache { SmallDenseMap Result; DebugInfoCache(const Module &M); - - bool invalidate(Module &, const PreservedAnalyses &, - ModuleAnalysisManager::Invalidator &); }; class DebugInfoCacheAnalysis diff --git a/llvm/lib/Analysis/DebugInfoCache.cpp b/llvm/lib/Analysis/DebugInfoCache.cpp index d8bcd22bd878d..fb981fc3429cb 100644 --- a/llvm/lib/Analysis/DebugInfoCache.cpp +++ b/llvm/lib/Analysis/DebugInfoCache.cpp @@ -32,14 +32,6 @@ DebugInfoCache::DebugInfoCache(const Module &M) { } } -bool DebugInfoCache::invalidate(Module &M, const PreservedAnalyses &PA, - ModuleAnalysisManager::Invalidator &) { - // Check whether the analysis has been explicitly invalidated. Otherwise, it's - // stateless and remains preserved. - auto PAC = PA.getChecker(); - return !PAC.preservedWhenStateless(); -} - AnalysisKey DebugInfoCacheAnalysis::Key; DebugInfoCache DebugInfoCacheAnalysis::run(Module &M, ModuleAnalysisManager &) {