1//===- AnalysisManagerTest.cpp - AnalysisManager 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 "mlir/Pass/AnalysisManager.h"
10#include "mlir/Dialect/Func/IR/FuncOps.h"
11#include "mlir/IR/Builders.h"
12#include "mlir/IR/BuiltinOps.h"
13#include "mlir/Pass/Pass.h"
14#include "mlir/Pass/PassManager.h"
15#include "gtest/gtest.h"
16
17using namespace mlir;
18using namespace mlir::detail;
19
20namespace {
21/// Minimal class definitions for two analyses.
22struct MyAnalysis {
23 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(MyAnalysis)
24
25 MyAnalysis(Operation *) {}
26};
27struct OtherAnalysis {
28 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OtherAnalysis)
29
30 OtherAnalysis(Operation *) {}
31};
32struct OpSpecificAnalysis {
33 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OpSpecificAnalysis)
34
35 OpSpecificAnalysis(ModuleOp) {}
36};
37
38TEST(AnalysisManagerTest, FineGrainModuleAnalysisPreservation) {
39 MLIRContext context;
40
41 // Test fine grain invalidation of the module analysis manager.
42 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
43 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
44 AnalysisManager am = mam;
45
46 // Query two different analyses, but only preserve one before invalidating.
47 am.getAnalysis<MyAnalysis>();
48 am.getAnalysis<OtherAnalysis>();
49
50 detail::PreservedAnalyses pa;
51 pa.preserve<MyAnalysis>();
52 am.invalidate(pa);
53
54 // Check that only MyAnalysis is preserved.
55 EXPECT_TRUE(am.getCachedAnalysis<MyAnalysis>().has_value());
56 EXPECT_FALSE(am.getCachedAnalysis<OtherAnalysis>().has_value());
57}
58
59TEST(AnalysisManagerTest, FineGrainFunctionAnalysisPreservation) {
60 MLIRContext context;
61 context.loadDialect<func::FuncDialect>();
62 Builder builder(&context);
63
64 // Create a function and a module.
65 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
66 func::FuncOp func1 = func::FuncOp::create(location: builder.getUnknownLoc(), name: "foo",
67 type: builder.getFunctionType(inputs: {}, results: {}));
68 func1.setPrivate();
69 module->push_back(op: func1);
70
71 // Test fine grain invalidation of the function analysis manager.
72 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
73 AnalysisManager am = mam;
74 AnalysisManager fam = am.nest(op: func1);
75
76 // Query two different analyses, but only preserve one before invalidating.
77 fam.getAnalysis<MyAnalysis>();
78 fam.getAnalysis<OtherAnalysis>();
79
80 detail::PreservedAnalyses pa;
81 pa.preserve<MyAnalysis>();
82 fam.invalidate(pa);
83
84 // Check that only MyAnalysis is preserved.
85 EXPECT_TRUE(fam.getCachedAnalysis<MyAnalysis>().has_value());
86 EXPECT_FALSE(fam.getCachedAnalysis<OtherAnalysis>().has_value());
87}
88
89TEST(AnalysisManagerTest, FineGrainChildFunctionAnalysisPreservation) {
90 MLIRContext context;
91 context.loadDialect<func::FuncDialect>();
92 Builder builder(&context);
93
94 // Create a function and a module.
95 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
96 func::FuncOp func1 = func::FuncOp::create(location: builder.getUnknownLoc(), name: "foo",
97 type: builder.getFunctionType(inputs: {}, results: {}));
98 func1.setPrivate();
99 module->push_back(op: func1);
100
101 // Test fine grain invalidation of a function analysis from within a module
102 // analysis manager.
103 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
104 AnalysisManager am = mam;
105
106 // Check that the analysis cache is initially empty.
107 EXPECT_FALSE(am.getCachedChildAnalysis<MyAnalysis>(func1).has_value());
108
109 // Query two different analyses, but only preserve one before invalidating.
110 am.getChildAnalysis<MyAnalysis>(child: func1);
111 am.getChildAnalysis<OtherAnalysis>(child: func1);
112
113 detail::PreservedAnalyses pa;
114 pa.preserve<MyAnalysis>();
115 am.invalidate(pa);
116
117 // Check that only MyAnalysis is preserved.
118 EXPECT_TRUE(am.getCachedChildAnalysis<MyAnalysis>(func1).has_value());
119 EXPECT_FALSE(am.getCachedChildAnalysis<OtherAnalysis>(func1).has_value());
120}
121
122/// Test analyses with custom invalidation logic.
123struct TestAnalysisSet {
124 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestAnalysisSet)
125};
126
127struct CustomInvalidatingAnalysis {
128 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CustomInvalidatingAnalysis)
129
130 CustomInvalidatingAnalysis(Operation *) {}
131
132 bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
133 return !pa.isPreserved<TestAnalysisSet>();
134 }
135};
136
137TEST(AnalysisManagerTest, CustomInvalidation) {
138 MLIRContext context;
139 Builder builder(&context);
140
141 // Create a function and a module.
142 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
143 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
144 AnalysisManager am = mam;
145
146 detail::PreservedAnalyses pa;
147
148 // Check that the analysis is invalidated properly.
149 am.getAnalysis<CustomInvalidatingAnalysis>();
150 am.invalidate(pa);
151 EXPECT_FALSE(am.getCachedAnalysis<CustomInvalidatingAnalysis>().has_value());
152
153 // Check that the analysis is preserved properly.
154 am.getAnalysis<CustomInvalidatingAnalysis>();
155 pa.preserve<TestAnalysisSet>();
156 am.invalidate(pa);
157 EXPECT_TRUE(am.getCachedAnalysis<CustomInvalidatingAnalysis>().has_value());
158}
159
160TEST(AnalysisManagerTest, OpSpecificAnalysis) {
161 MLIRContext context;
162
163 // Create a module.
164 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
165 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
166 AnalysisManager am = mam;
167
168 // Query the op specific analysis for the module and verify that its cached.
169 am.getAnalysis<OpSpecificAnalysis, ModuleOp>();
170 EXPECT_TRUE(am.getCachedAnalysis<OpSpecificAnalysis>().has_value());
171}
172
173struct AnalysisWithDependency {
174 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWithDependency)
175
176 AnalysisWithDependency(Operation *, AnalysisManager &am) {
177 am.getAnalysis<MyAnalysis>();
178 }
179
180 bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
181 return !pa.isPreserved<AnalysisWithDependency>() ||
182 !pa.isPreserved<MyAnalysis>();
183 }
184};
185
186TEST(AnalysisManagerTest, DependentAnalysis) {
187 MLIRContext context;
188
189 // Create a module.
190 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
191 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
192 AnalysisManager am = mam;
193
194 am.getAnalysis<AnalysisWithDependency>();
195 EXPECT_TRUE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
196 EXPECT_TRUE(am.getCachedAnalysis<MyAnalysis>().has_value());
197
198 detail::PreservedAnalyses pa;
199 pa.preserve<AnalysisWithDependency>();
200 am.invalidate(pa);
201
202 EXPECT_FALSE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
203 EXPECT_FALSE(am.getCachedAnalysis<MyAnalysis>().has_value());
204}
205
206struct AnalysisWithNestedDependency {
207 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWithNestedDependency)
208
209 AnalysisWithNestedDependency(Operation *, AnalysisManager &am) {
210 am.getAnalysis<AnalysisWithDependency>();
211 }
212
213 bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
214 return !pa.isPreserved<AnalysisWithNestedDependency>() ||
215 !pa.isPreserved<AnalysisWithDependency>();
216 }
217};
218
219TEST(AnalysisManagerTest, NestedDependentAnalysis) {
220 MLIRContext context;
221
222 // Create a module.
223 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
224 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
225 AnalysisManager am = mam;
226
227 am.getAnalysis<AnalysisWithNestedDependency>();
228 EXPECT_TRUE(am.getCachedAnalysis<AnalysisWithNestedDependency>().has_value());
229 EXPECT_TRUE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
230 EXPECT_TRUE(am.getCachedAnalysis<MyAnalysis>().has_value());
231
232 detail::PreservedAnalyses pa;
233 pa.preserve<AnalysisWithDependency>();
234 pa.preserve<AnalysisWithNestedDependency>();
235 am.invalidate(pa);
236
237 EXPECT_FALSE(
238 am.getCachedAnalysis<AnalysisWithNestedDependency>().has_value());
239 EXPECT_FALSE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
240 EXPECT_FALSE(am.getCachedAnalysis<MyAnalysis>().has_value());
241}
242
243struct AnalysisWith2Ctors {
244 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWith2Ctors)
245
246 AnalysisWith2Ctors(Operation *) { ctor1called = true; }
247
248 AnalysisWith2Ctors(Operation *, AnalysisManager &) { ctor2called = true; }
249
250 bool ctor1called = false;
251 bool ctor2called = false;
252};
253
254TEST(AnalysisManagerTest, DependentAnalysis2Ctors) {
255 MLIRContext context;
256
257 // Create a module.
258 OwningOpRef<ModuleOp> module(ModuleOp::create(loc: UnknownLoc::get(context: &context)));
259 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
260 AnalysisManager am = mam;
261
262 auto &an = am.getAnalysis<AnalysisWith2Ctors>();
263 EXPECT_FALSE(an.ctor1called);
264 EXPECT_TRUE(an.ctor2called);
265}
266
267} // namespace
268

source code of mlir/unittests/Pass/AnalysisManagerTest.cpp