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(UnknownLoc::get(&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(UnknownLoc::get(&context)));
66 func::FuncOp func1 =
67 func::FuncOp::create(builder.getUnknownLoc(), "foo",
68 builder.getFunctionType(std::nullopt, std::nullopt));
69 func1.setPrivate();
70 module->push_back(func1);
71
72 // Test fine grain invalidation of the function analysis manager.
73 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
74 AnalysisManager am = mam;
75 AnalysisManager fam = am.nest(op: func1);
76
77 // Query two different analyses, but only preserve one before invalidating.
78 fam.getAnalysis<MyAnalysis>();
79 fam.getAnalysis<OtherAnalysis>();
80
81 detail::PreservedAnalyses pa;
82 pa.preserve<MyAnalysis>();
83 fam.invalidate(pa);
84
85 // Check that only MyAnalysis is preserved.
86 EXPECT_TRUE(fam.getCachedAnalysis<MyAnalysis>().has_value());
87 EXPECT_FALSE(fam.getCachedAnalysis<OtherAnalysis>().has_value());
88}
89
90TEST(AnalysisManagerTest, FineGrainChildFunctionAnalysisPreservation) {
91 MLIRContext context;
92 context.loadDialect<func::FuncDialect>();
93 Builder builder(&context);
94
95 // Create a function and a module.
96 OwningOpRef<ModuleOp> module(ModuleOp::create(UnknownLoc::get(&context)));
97 func::FuncOp func1 =
98 func::FuncOp::create(builder.getUnknownLoc(), "foo",
99 builder.getFunctionType(std::nullopt, std::nullopt));
100 func1.setPrivate();
101 module->push_back(func1);
102
103 // Test fine grain invalidation of a function analysis from within a module
104 // analysis manager.
105 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
106 AnalysisManager am = mam;
107
108 // Check that the analysis cache is initially empty.
109 EXPECT_FALSE(am.getCachedChildAnalysis<MyAnalysis>(func1).has_value());
110
111 // Query two different analyses, but only preserve one before invalidating.
112 am.getChildAnalysis<MyAnalysis>(func1);
113 am.getChildAnalysis<OtherAnalysis>(func1);
114
115 detail::PreservedAnalyses pa;
116 pa.preserve<MyAnalysis>();
117 am.invalidate(pa);
118
119 // Check that only MyAnalysis is preserved.
120 EXPECT_TRUE(am.getCachedChildAnalysis<MyAnalysis>(func1).has_value());
121 EXPECT_FALSE(am.getCachedChildAnalysis<OtherAnalysis>(func1).has_value());
122}
123
124/// Test analyses with custom invalidation logic.
125struct TestAnalysisSet {
126 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestAnalysisSet)
127};
128
129struct CustomInvalidatingAnalysis {
130 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CustomInvalidatingAnalysis)
131
132 CustomInvalidatingAnalysis(Operation *) {}
133
134 bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
135 return !pa.isPreserved<TestAnalysisSet>();
136 }
137};
138
139TEST(AnalysisManagerTest, CustomInvalidation) {
140 MLIRContext context;
141 Builder builder(&context);
142
143 // Create a function and a module.
144 OwningOpRef<ModuleOp> module(ModuleOp::create(UnknownLoc::get(&context)));
145 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
146 AnalysisManager am = mam;
147
148 detail::PreservedAnalyses pa;
149
150 // Check that the analysis is invalidated properly.
151 am.getAnalysis<CustomInvalidatingAnalysis>();
152 am.invalidate(pa);
153 EXPECT_FALSE(am.getCachedAnalysis<CustomInvalidatingAnalysis>().has_value());
154
155 // Check that the analysis is preserved properly.
156 am.getAnalysis<CustomInvalidatingAnalysis>();
157 pa.preserve<TestAnalysisSet>();
158 am.invalidate(pa);
159 EXPECT_TRUE(am.getCachedAnalysis<CustomInvalidatingAnalysis>().has_value());
160}
161
162TEST(AnalysisManagerTest, OpSpecificAnalysis) {
163 MLIRContext context;
164
165 // Create a module.
166 OwningOpRef<ModuleOp> module(ModuleOp::create(UnknownLoc::get(&context)));
167 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
168 AnalysisManager am = mam;
169
170 // Query the op specific analysis for the module and verify that its cached.
171 am.getAnalysis<OpSpecificAnalysis, ModuleOp>();
172 EXPECT_TRUE(am.getCachedAnalysis<OpSpecificAnalysis>().has_value());
173}
174
175struct AnalysisWithDependency {
176 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWithDependency)
177
178 AnalysisWithDependency(Operation *, AnalysisManager &am) {
179 am.getAnalysis<MyAnalysis>();
180 }
181
182 bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
183 return !pa.isPreserved<AnalysisWithDependency>() ||
184 !pa.isPreserved<MyAnalysis>();
185 }
186};
187
188TEST(AnalysisManagerTest, DependentAnalysis) {
189 MLIRContext context;
190
191 // Create a module.
192 OwningOpRef<ModuleOp> module(ModuleOp::create(UnknownLoc::get(&context)));
193 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
194 AnalysisManager am = mam;
195
196 am.getAnalysis<AnalysisWithDependency>();
197 EXPECT_TRUE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
198 EXPECT_TRUE(am.getCachedAnalysis<MyAnalysis>().has_value());
199
200 detail::PreservedAnalyses pa;
201 pa.preserve<AnalysisWithDependency>();
202 am.invalidate(pa);
203
204 EXPECT_FALSE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
205 EXPECT_FALSE(am.getCachedAnalysis<MyAnalysis>().has_value());
206}
207
208struct AnalysisWithNestedDependency {
209 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWithNestedDependency)
210
211 AnalysisWithNestedDependency(Operation *, AnalysisManager &am) {
212 am.getAnalysis<AnalysisWithDependency>();
213 }
214
215 bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
216 return !pa.isPreserved<AnalysisWithNestedDependency>() ||
217 !pa.isPreserved<AnalysisWithDependency>();
218 }
219};
220
221TEST(AnalysisManagerTest, NestedDependentAnalysis) {
222 MLIRContext context;
223
224 // Create a module.
225 OwningOpRef<ModuleOp> module(ModuleOp::create(UnknownLoc::get(&context)));
226 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
227 AnalysisManager am = mam;
228
229 am.getAnalysis<AnalysisWithNestedDependency>();
230 EXPECT_TRUE(am.getCachedAnalysis<AnalysisWithNestedDependency>().has_value());
231 EXPECT_TRUE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
232 EXPECT_TRUE(am.getCachedAnalysis<MyAnalysis>().has_value());
233
234 detail::PreservedAnalyses pa;
235 pa.preserve<AnalysisWithDependency>();
236 pa.preserve<AnalysisWithNestedDependency>();
237 am.invalidate(pa);
238
239 EXPECT_FALSE(
240 am.getCachedAnalysis<AnalysisWithNestedDependency>().has_value());
241 EXPECT_FALSE(am.getCachedAnalysis<AnalysisWithDependency>().has_value());
242 EXPECT_FALSE(am.getCachedAnalysis<MyAnalysis>().has_value());
243}
244
245struct AnalysisWith2Ctors {
246 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWith2Ctors)
247
248 AnalysisWith2Ctors(Operation *) { ctor1called = true; }
249
250 AnalysisWith2Ctors(Operation *, AnalysisManager &) { ctor2called = true; }
251
252 bool ctor1called = false;
253 bool ctor2called = false;
254};
255
256TEST(AnalysisManagerTest, DependentAnalysis2Ctors) {
257 MLIRContext context;
258
259 // Create a module.
260 OwningOpRef<ModuleOp> module(ModuleOp::create(UnknownLoc::get(&context)));
261 ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr);
262 AnalysisManager am = mam;
263
264 auto &an = am.getAnalysis<AnalysisWith2Ctors>();
265 EXPECT_FALSE(an.ctor1called);
266 EXPECT_TRUE(an.ctor2called);
267}
268
269} // namespace
270

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