llvm.org GIT mirror llvm / b86a95f
[PM] Add unittesting of the call graph update logic with complex dependencies between analyses. This uncovers even more issues with the proxies and the splitting apart of SCCs which are fixed in this patch. I discovered this while trying to add more rigorous testing for a change I'm making to the call graph update invalidation logic. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@307497 91177308-0d34-0410-b5e6-96231b3b80d8 Chandler Carruth 3 years ago
4 changed file(s) with 250 addition(s) and 17 deletion(s). Raw diff Collapse all Expand all
576576 // analyses will eventually occur when the module pass completes.
577577 PA.intersect(std::move(PassPA));
578578
579 // Update the call graph based on this function pass. This may also
580 // update the current SCC to point to a smaller, more refined SCC.
581 CurrentC = &updateCGAndAnalysisManagerForFunctionPass(
582 CG, *CurrentC, *N, AM, UR, DebugLogging);
583 assert(CG.lookupSCC(*N) == CurrentC &&
584 "Current SCC not updated to the SCC containing the current node!");
579 // If the call graph hasn't been preserved, update it based on this
580 // function pass. This may also update the current SCC to point to
581 // a smaller, more refined SCC.
582 auto PAC = PA.getChecker();
583 if (!PAC.preserved() && !PAC.preservedSet>()) {
584 CurrentC = &updateCGAndAnalysisManagerForFunctionPass(
585 CG, *CurrentC, *N, AM, UR, DebugLogging);
586 assert(
587 CG.lookupSCC(*N) == CurrentC &&
588 "Current SCC not updated to the SCC containing the current node!");
589 }
585590 }
586591
587592 // By definition we preserve the proxy. And we preserve all analyses on
10691069
10701070 const AnalysisManagerT &getManager() const { return *AM; }
10711071
1072 /// \brief Handle invalidation by ignoring it; this pass is immutable.
1072 /// When invalidation occurs, remove any registered invalidation events.
10731073 bool invalidate(
1074 IRUnitT &, const PreservedAnalyses &,
1075 typename AnalysisManager::Invalidator &) {
1074 IRUnitT &IRUnit, const PreservedAnalyses &PA,
1075 typename AnalysisManager::Invalidator &Inv) {
1076 // Loop over the set of registered outer invalidation mappings and if any
1077 // of them map to an analysis that is now invalid, clear it out.
1078 SmallVector DeadKeys;
1079 for (auto &KeyValuePair : OuterAnalysisInvalidationMap) {
1080 AnalysisKey *OuterID = KeyValuePair.first;
1081 auto &InnerIDs = KeyValuePair.second;
1082 InnerIDs.erase(llvm::remove_if(InnerIDs, [&](AnalysisKey *InnerID) {
1083 return Inv.invalidate(InnerID, IRUnit, PA); }),
1084 InnerIDs.end());
1085 if (InnerIDs.empty())
1086 DeadKeys.push_back(OuterID);
1087 }
1088
1089 for (auto OuterID : DeadKeys)
1090 OuterAnalysisInvalidationMap.erase(OuterID);
1091
1092 // The proxy itself remains valid regardless of anything else.
10761093 return false;
10771094 }
10781095
260260 }
261261
262262 } // End llvm namespace
263
264 /// When a new SCC is created for the graph and there might be function
265 /// analysis results cached for the functions now in that SCC two forms of
266 /// updates are required.
267 ///
268 /// First, a proxy from the SCC to the FunctionAnalysisManager needs to be
269 /// created so that any subsequent invalidation events to the SCC are
270 /// propagated to the function analysis results cached for functions within it.
271 ///
272 /// Second, if any of the functions within the SCC have analysis results with
273 /// outer analysis dependencies, then those dependencies would point to the
274 /// *wrong* SCC's analysis result. We forcibly invalidate the necessary
275 /// function analyses so that they don't retain stale handles.
276 static void updateNewSCCFunctionAnalyses(LazyCallGraph::SCC &C,
277 LazyCallGraph &G,
278 CGSCCAnalysisManager &AM) {
279 // Get the relevant function analysis manager.
280 auto &FAM =
281 AM.getResult(C, G).getManager();
282
283 // Now walk the functions in this SCC and invalidate any function analysis
284 // results that might have outer dependencies on an SCC analysis.
285 for (LazyCallGraph::Node &N : C) {
286 Function &F = N.getFunction();
287
288 auto *OuterProxy =
289 FAM.getCachedResult(F);
290 if (!OuterProxy)
291 // No outer analyses were queried, nothing to do.
292 continue;
293
294 // Forcibly abandon all the inner analyses with dependencies, but
295 // invalidate nothing else.
296 auto PA = PreservedAnalyses::all();
297 for (const auto &OuterInvalidationPair :
298 OuterProxy->getOuterInvalidations()) {
299 const auto &InnerAnalysisIDs = OuterInvalidationPair.second;
300 for (AnalysisKey *InnerAnalysisID : InnerAnalysisIDs)
301 PA.abandon(InnerAnalysisID);
302 }
303
304 // Now invalidate anything we found.
305 FAM.invalidate(F, PA);
306 }
307 }
263308
264309 namespace {
265310 /// Helper function to update both the \c CGSCCAnalysisManager \p AM and the \c
313358 PA.preserve();
314359 AM.invalidate(*OldC, PA);
315360
316 // Ensure we have a proxy for the now-current SCC if needed.
361 // Ensure the now-current SCC's function analyses are updated.
317362 if (NeedFAMProxy)
318 (void)AM.getResult(*C, G);
363 updateNewSCCFunctionAnalyses(*C, G, AM);
319364
320365 for (SCC &NewC :
321366 reverse(make_range(std::next(NewSCCRange.begin()), NewSCCRange.end()))) {
325370 if (DebugLogging)
326371 dbgs() << "Enqueuing a newly formed SCC:" << NewC << "\n";
327372
328 // Ensure new SCCs have a FAM proxy if needed.
373 // Ensure new SCCs' function analyses are updated.
329374 if (NeedFAMProxy)
330 (void)AM.getResult(*C, G);
331
332 // And propagate an invalidation to the new SCC as only the current will
333 // get one from the pass manager infrastructure.
375 updateNewSCCFunctionAnalyses(*C, G, AM);
376
377 // Also propagate a normal invalidation to the new SCC as only the current
378 // will get one from the pass manager infrastructure.
334379 AM.invalidate(NewC, PA);
335380 }
336381 return C;
11001100 // Four passes count each of six functions once (via SCCs).
11011101 EXPECT_EQ(4 * 6, FunctionCount);
11021102 }
1103 }
1103
1104 TEST_F(CGSCCPassManagerTest, TestAnalysisInvalidationCGSCCUpdate) {
1105 int ModuleAnalysisRuns = 0;
1106 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });
1107
1108 int SCCAnalysisRuns = 0, IndirectSCCAnalysisRuns = 0,
1109 DoublyIndirectSCCAnalysisRuns = 0;
1110 CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });
1111 CGAM.registerPass(
1112 [&] { return TestIndirectSCCAnalysis(IndirectSCCAnalysisRuns); });
1113 CGAM.registerPass([&] {
1114 return TestDoublyIndirectSCCAnalysis(DoublyIndirectSCCAnalysisRuns);
1115 });
1116
1117 int FunctionAnalysisRuns = 0, IndirectFunctionAnalysisRuns = 0;
1118 FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });
1119 FAM.registerPass([&] {
1120 return TestIndirectFunctionAnalysis(IndirectFunctionAnalysisRuns);
1121 });
1122
1123 ModulePassManager MPM(/*DebugLogging*/ true);
1124
1125 CGSCCPassManager CGPM(/*DebugLogging*/ true);
1126 // First just use the analysis to get the function count and preserve
1127 // everything.
1128 using RequireTestIndirectFunctionAnalysisPass =
1129 RequireAnalysisPass;
1130 using RequireTestDoublyIndirectSCCAnalysisPass =
1131 RequireAnalysisPass
1132 CGSCCAnalysisManager, LazyCallGraph &,
1133 CGSCCUpdateResult &>;
1134 CGPM.addPass(RequireTestDoublyIndirectSCCAnalysisPass());
1135 CGPM.addPass(createCGSCCToFunctionPassAdaptor(
1136 RequireTestIndirectFunctionAnalysisPass()));
1137
1138 // Next, we inject an SCC pass that invalidates everything for the `(h3, h1,
1139 // h2)` SCC but also deletes the call edge from `h2` to `h3` and updates the
1140 // CG. This should successfully invalidate (and force to be re-run) all the
1141 // analyses for that SCC and for the functions.
1142 CGPM.addPass(
1143 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
1144 LazyCallGraph &CG, CGSCCUpdateResult &UR) {
1145 (void)AM.getResult(C, CG);
1146 if (C.getName() != "(h3, h1, h2)")
1147 return PreservedAnalyses::all();
1148
1149 // Build the preserved set.
1150 auto PA = PreservedAnalyses::none();
1151 PA.preserve();
1152 PA.preserve();
1153 PA.preserve();
1154
1155 // Delete the call from `h2` to `h3`.
1156 auto &H2N = *llvm::find_if(
1157 C, [](LazyCallGraph::Node &N) { return N.getName() == "h2"; });
1158 auto &H2F = H2N.getFunction();
1159 auto &H3F = *cast(H2F.begin()->begin())->getCalledFunction();
1160 assert(H3F.getName() == "h3" && "Wrong called function!");
1161 H2F.begin()->begin()->eraseFromParent();
1162 // Insert a bitcast of `h3` so that we retain a ref edge to it.
1163 (void)CastInst::CreatePointerCast(&H3F,
1164 Type::getInt8PtrTy(H2F.getContext()),
1165 "dummy", &*H2F.begin()->begin());
1166
1167 // Now update the call graph.
1168 auto &NewC = updateCGAndAnalysisManagerForFunctionPass(
1169 CG, C, H2N, AM, UR, /*DebugLogging*/ true);
1170 assert(&NewC != &C && "Should get a new SCC due to update!");
1171
1172 return PA;
1173 }));
1174 // Now use the analysis again on each SCC and function, forcing
1175 // re-computation for all of them.
1176 CGPM.addPass(RequireTestDoublyIndirectSCCAnalysisPass());
1177 CGPM.addPass(createCGSCCToFunctionPassAdaptor(
1178 RequireTestIndirectFunctionAnalysisPass()));
1179
1180 // Create another CGSCC pipeline that requires all the analyses again.
1181 CGSCCPassManager CGPM2(/*DebugLogging*/ true);
1182 CGPM2.addPass(RequireTestDoublyIndirectSCCAnalysisPass());
1183 CGPM2.addPass(createCGSCCToFunctionPassAdaptor(
1184 RequireTestIndirectFunctionAnalysisPass()));
1185
1186 // Next we inject an SCC pass that finds the `(h2)` SCC, adds a call to `h3`
1187 // back to `h2`, and then invalidates everything for what will then be the
1188 // `(h3, h1, h2)` SCC again.
1189 CGSCCPassManager CGPM3(/*DebugLogging*/ true);
1190 CGPM3.addPass(
1191 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
1192 LazyCallGraph &CG, CGSCCUpdateResult &UR) {
1193 (void)AM.getResult(C, CG);
1194 if (C.getName() != "(h2)")
1195 return PreservedAnalyses::all();
1196
1197 // Build the preserved set.
1198 auto PA = PreservedAnalyses::none();
1199 PA.preserve();
1200 PA.preserve();
1201 PA.preserve();
1202
1203 // Delete the bitcast of `h3` that we added earlier.
1204 auto &H2N = *C.begin();
1205 auto &H2F = H2N.getFunction();
1206 auto &H3F = *cast(cast(H2F.begin()->begin())->getOperand(0));
1207 assert(H3F.getName() == "h3" && "Wrong called function!");
1208 H2F.begin()->begin()->eraseFromParent();
1209 // And insert a call to `h3`.
1210 (void)CallInst::Create(&H3F, {}, "", &*H2F.begin()->begin());
1211
1212 // Now update the call graph.
1213 auto &NewC = updateCGAndAnalysisManagerForFunctionPass(
1214 CG, C, H2N, AM, UR, /*DebugLogging*/ true);
1215 assert(&NewC != &C && "Should get a new SCC due to update!");
1216
1217 return PA;
1218 }));
1219 // Now use the analysis again on each SCC and function, forcing
1220 // re-computation for all of them.
1221 CGPM3.addPass(RequireTestDoublyIndirectSCCAnalysisPass());
1222 CGPM3.addPass(createCGSCCToFunctionPassAdaptor(
1223 RequireTestIndirectFunctionAnalysisPass()));
1224
1225 // Create a second CGSCC pass manager. This will cause the module-level
1226 // invalidation to occur, which will force yet another invalidation of the
1227 // indirect SCC-level analysis as the module analysis it depends on gets
1228 // invalidated.
1229 CGSCCPassManager CGPM4(/*DebugLogging*/ true);
1230 CGPM4.addPass(RequireTestDoublyIndirectSCCAnalysisPass());
1231 CGPM4.addPass(createCGSCCToFunctionPassAdaptor(
1232 RequireTestIndirectFunctionAnalysisPass()));
1233
1234 // Add a requires pass to populate the module analysis and then one of our
1235 // CGSCC pipelines. Repeat for all four CGSCC pipelines.
1236 MPM.addPass(RequireAnalysisPass());
1237 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));
1238 MPM.addPass(RequireAnalysisPass());
1239 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2)));
1240 MPM.addPass(RequireAnalysisPass());
1241 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3)));
1242 MPM.addPass(RequireAnalysisPass());
1243 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM4)));
1244 MPM.run(*M, MAM);
1245
1246 // We run over four SCCs the first time. But then we split an SCC into three.
1247 // And then we merge those three back into one.
1248 EXPECT_EQ(4 + 3 + 1, SCCAnalysisRuns);
1249 // The module analysis pass should be run three times.
1250 EXPECT_EQ(3, ModuleAnalysisRuns);
1251 // We run over four SCCs the first time. Then over the two new ones. Then the
1252 // entire module is invalidated causing a full run over all seven. Then we
1253 // fold three SCCs back to one, and then run over the whole module again.
1254 EXPECT_EQ(4 + 2 + 7 + 1 + 4, IndirectSCCAnalysisRuns);
1255 EXPECT_EQ(4 + 2 + 7 + 1 + 4, DoublyIndirectSCCAnalysisRuns);
1256
1257 // First we run over all six functions. Then we re-run it over three when we
1258 // split their SCCs. Then we re-run over the whole module. Then we re-run
1259 // over three functions merged back into a single SCC, and then over the
1260 // whole module again.
1261 EXPECT_EQ(6 + 2 + 6 + 3 + 6, FunctionAnalysisRuns);
1262
1263 // Re run the function analysis twice over the entire module, and then re-run
1264 // it over the `(h3, h1, h2)` SCC due to invalidation. Then we re-un over
1265 // three functions merged back into a single SCC, and then over the whole
1266 // module.
1267 EXPECT_EQ(6 + 2 + 6 + 3 + 6, IndirectFunctionAnalysisRuns);
1268 }
1269 }