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