//===- AnalysisManagerTest.cpp - AnalysisManager 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 "mlir/Pass/AnalysisManager.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "gtest/gtest.h" using namespace mlir; using namespace mlir::detail; namespace { /// Minimal class definitions for two analyses. struct MyAnalysis { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(MyAnalysis) MyAnalysis(Operation *) {} }; struct OtherAnalysis { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OtherAnalysis) OtherAnalysis(Operation *) {} }; struct OpSpecificAnalysis { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OpSpecificAnalysis) OpSpecificAnalysis(ModuleOp) {} }; TEST(AnalysisManagerTest, FineGrainModuleAnalysisPreservation) { MLIRContext context; // Test fine grain invalidation of the module analysis manager. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; // Query two different analyses, but only preserve one before invalidating. am.getAnalysis(); am.getAnalysis(); detail::PreservedAnalyses pa; pa.preserve(); am.invalidate(pa); // Check that only MyAnalysis is preserved. EXPECT_TRUE(am.getCachedAnalysis().has_value()); EXPECT_FALSE(am.getCachedAnalysis().has_value()); } TEST(AnalysisManagerTest, FineGrainFunctionAnalysisPreservation) { MLIRContext context; context.loadDialect(); Builder builder(&context); // Create a function and a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); func::FuncOp func1 = func::FuncOp::create(builder.getUnknownLoc(), "foo", builder.getFunctionType(std::nullopt, std::nullopt)); func1.setPrivate(); module->push_back(func1); // Test fine grain invalidation of the function analysis manager. ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; AnalysisManager fam = am.nest(func1); // Query two different analyses, but only preserve one before invalidating. fam.getAnalysis(); fam.getAnalysis(); detail::PreservedAnalyses pa; pa.preserve(); fam.invalidate(pa); // Check that only MyAnalysis is preserved. EXPECT_TRUE(fam.getCachedAnalysis().has_value()); EXPECT_FALSE(fam.getCachedAnalysis().has_value()); } TEST(AnalysisManagerTest, FineGrainChildFunctionAnalysisPreservation) { MLIRContext context; context.loadDialect(); Builder builder(&context); // Create a function and a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); func::FuncOp func1 = func::FuncOp::create(builder.getUnknownLoc(), "foo", builder.getFunctionType(std::nullopt, std::nullopt)); func1.setPrivate(); module->push_back(func1); // Test fine grain invalidation of a function analysis from within a module // analysis manager. ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; // Check that the analysis cache is initially empty. EXPECT_FALSE(am.getCachedChildAnalysis(func1).has_value()); // Query two different analyses, but only preserve one before invalidating. am.getChildAnalysis(func1); am.getChildAnalysis(func1); detail::PreservedAnalyses pa; pa.preserve(); am.invalidate(pa); // Check that only MyAnalysis is preserved. EXPECT_TRUE(am.getCachedChildAnalysis(func1).has_value()); EXPECT_FALSE(am.getCachedChildAnalysis(func1).has_value()); } /// Test analyses with custom invalidation logic. struct TestAnalysisSet { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestAnalysisSet) }; struct CustomInvalidatingAnalysis { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CustomInvalidatingAnalysis) CustomInvalidatingAnalysis(Operation *) {} bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) { return !pa.isPreserved(); } }; TEST(AnalysisManagerTest, CustomInvalidation) { MLIRContext context; Builder builder(&context); // Create a function and a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; detail::PreservedAnalyses pa; // Check that the analysis is invalidated properly. am.getAnalysis(); am.invalidate(pa); EXPECT_FALSE(am.getCachedAnalysis().has_value()); // Check that the analysis is preserved properly. am.getAnalysis(); pa.preserve(); am.invalidate(pa); EXPECT_TRUE(am.getCachedAnalysis().has_value()); } TEST(AnalysisManagerTest, OpSpecificAnalysis) { MLIRContext context; // Create a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; // Query the op specific analysis for the module and verify that its cached. am.getAnalysis(); EXPECT_TRUE(am.getCachedAnalysis().has_value()); } struct AnalysisWithDependency { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWithDependency) AnalysisWithDependency(Operation *, AnalysisManager &am) { am.getAnalysis(); } bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) { return !pa.isPreserved() || !pa.isPreserved(); } }; TEST(AnalysisManagerTest, DependentAnalysis) { MLIRContext context; // Create a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; am.getAnalysis(); EXPECT_TRUE(am.getCachedAnalysis().has_value()); EXPECT_TRUE(am.getCachedAnalysis().has_value()); detail::PreservedAnalyses pa; pa.preserve(); am.invalidate(pa); EXPECT_FALSE(am.getCachedAnalysis().has_value()); EXPECT_FALSE(am.getCachedAnalysis().has_value()); } struct AnalysisWithNestedDependency { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWithNestedDependency) AnalysisWithNestedDependency(Operation *, AnalysisManager &am) { am.getAnalysis(); } bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) { return !pa.isPreserved() || !pa.isPreserved(); } }; TEST(AnalysisManagerTest, NestedDependentAnalysis) { MLIRContext context; // Create a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; am.getAnalysis(); EXPECT_TRUE(am.getCachedAnalysis().has_value()); EXPECT_TRUE(am.getCachedAnalysis().has_value()); EXPECT_TRUE(am.getCachedAnalysis().has_value()); detail::PreservedAnalyses pa; pa.preserve(); pa.preserve(); am.invalidate(pa); EXPECT_FALSE( am.getCachedAnalysis().has_value()); EXPECT_FALSE(am.getCachedAnalysis().has_value()); EXPECT_FALSE(am.getCachedAnalysis().has_value()); } struct AnalysisWith2Ctors { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AnalysisWith2Ctors) AnalysisWith2Ctors(Operation *) { ctor1called = true; } AnalysisWith2Ctors(Operation *, AnalysisManager &) { ctor2called = true; } bool ctor1called = false; bool ctor2called = false; }; TEST(AnalysisManagerTest, DependentAnalysis2Ctors) { MLIRContext context; // Create a module. OwningOpRef module(ModuleOp::create(UnknownLoc::get(&context))); ModuleAnalysisManager mam(*module, /*passInstrumentor=*/nullptr); AnalysisManager am = mam; auto &an = am.getAnalysis(); EXPECT_FALSE(an.ctor1called); EXPECT_TRUE(an.ctor2called); } } // namespace