From: Rahul Joshi Date: Fri, 14 Aug 2020 03:43:47 +0000 (-0700) Subject: [MLIR] Add support for defining and using Op specific analysis X-Git-Tag: llvmorg-13-init~14522 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9a4b30cf84298887f0a7bf70b865493d767abdc9;p=platform%2Fupstream%2Fllvm.git [MLIR] Add support for defining and using Op specific analysis - Add variants of getAnalysis() and friends that operate on a specific derived operation types. - Add OpPassManager::getAnalysis() to always call the base getAnalysis() with OpT. - With this, an OperationPass can call getAnalysis<> using an analysis type that is generic (works on Operation *) or specific to the OpT for the pass. Anything else will fail to compile. - Extend AnalysisManager unit test to test this, and add a new PassManager unit test to test this functionality in the context of an OperationPass. Differential Revision: https://reviews.llvm.org/D84897 --- diff --git a/mlir/include/mlir/Pass/AnalysisManager.h b/mlir/include/mlir/Pass/AnalysisManager.h index 4e9b3f2..37036e2 100644 --- a/mlir/include/mlir/Pass/AnalysisManager.h +++ b/mlir/include/mlir/Pass/AnalysisManager.h @@ -128,25 +128,18 @@ public: explicit AnalysisMap(Operation *ir) : ir(ir) {} /// Get an analysis for the current IR unit, computing it if necessary. - template AnalysisT &getAnalysis(PassInstrumentor *pi) { - TypeID id = TypeID::get(); - - typename ConceptMap::iterator it; - bool wasInserted; - std::tie(it, wasInserted) = analyses.try_emplace(id); - - // If we don't have a cached analysis for this function, compute it directly - // and add it to the cache. - if (wasInserted) { - if (pi) - pi->runBeforeAnalysis(getAnalysisName(), id, ir); - - it->second = std::make_unique>(ir); + template + AnalysisT &getAnalysis(PassInstrumentor *pi) { + return getAnalysisImpl(pi, ir); + } - if (pi) - pi->runAfterAnalysis(getAnalysisName(), id, ir); - } - return static_cast &>(*it->second).analysis; + /// Get an analysis for the current IR unit assuming it's of specific derived + /// operation type. + template + typename std::enable_if::value, + AnalysisT &>::type + getAnalysis(PassInstrumentor *pi) { + return getAnalysisImpl(pi, cast(ir)); } /// Get a cached analysis instance if one exists, otherwise return null. @@ -176,6 +169,28 @@ public: } private: + template + AnalysisT &getAnalysisImpl(PassInstrumentor *pi, OpT op) { + TypeID id = TypeID::get(); + + typename ConceptMap::iterator it; + bool wasInserted; + std::tie(it, wasInserted) = analyses.try_emplace(id); + + // If we don't have a cached analysis for this function, compute it directly + // and add it to the cache. + if (wasInserted) { + if (pi) + pi->runBeforeAnalysis(getAnalysisName(), id, ir); + + it->second = std::make_unique>(op); + + if (pi) + pi->runAfterAnalysis(getAnalysisName(), id, ir); + } + return static_cast &>(*it->second).analysis; + } + Operation *ir; ConceptMap analyses; }; @@ -216,8 +231,8 @@ class AnalysisManager { public: using PreservedAnalyses = detail::PreservedAnalyses; - // Query for a cached analysis on the given parent operation. The analysis may - // not exist and if it does it may be out-of-date. + /// Query for a cached analysis on the given parent operation. The analysis + /// may not exist and if it does it may be out-of-date. template Optional> getCachedParentAnalysis(Operation *parentOp) const { @@ -230,12 +245,19 @@ public: return None; } - // Query for the given analysis for the current operation. + /// Query for the given analysis for the current operation. template AnalysisT &getAnalysis() { return impl->analyses.getAnalysis(getPassInstrumentor()); } - // Query for a cached entry of the given analysis on the current operation. + /// Query for the given analysis for the current operation of a specific + /// derived operation type. + template + AnalysisT &getAnalysis() { + return impl->analyses.getAnalysis(getPassInstrumentor()); + } + + /// Query for a cached entry of the given analysis on the current operation. template Optional> getCachedAnalysis() const { return impl->analyses.getCachedAnalysis(); @@ -246,6 +268,13 @@ public: return slice(op).template getAnalysis(); } + /// Query for an analysis of a child operation of a specifc derived operation + /// type, constructing it if necessary. + template + AnalysisT &getChildAnalysis(OpT child) { + return slice(child).template getAnalysis(); + } + /// Query for a cached analysis of a child operation, or return null. template Optional> diff --git a/mlir/include/mlir/Pass/Pass.h b/mlir/include/mlir/Pass/Pass.h index 7c0f9bd..8de31d9 100644 --- a/mlir/include/mlir/Pass/Pass.h +++ b/mlir/include/mlir/Pass/Pass.h @@ -167,6 +167,13 @@ protected: return getAnalysisManager().getAnalysis(); } + /// Query an analysis for the current ir unit of a specific derived operation + /// type. + template + AnalysisT &getAnalysis() { + return getAnalysisManager().getAnalysis(); + } + /// Query a cached instance of an analysis for the current ir unit if one /// exists. template @@ -187,12 +194,14 @@ protected: getPassState().preservedAnalyses.preserve(id); } - /// Returns the analysis for the parent operation if it exists. + /// Returns the analysis for the given parent operation if it exists. template Optional> getCachedParentAnalysis(Operation *parent) { return getAnalysisManager().getCachedParentAnalysis(parent); } + + /// Returns the analysis for the parent operation if it exists. template Optional> getCachedParentAnalysis() { return getAnalysisManager().getCachedParentAnalysis( @@ -212,6 +221,13 @@ protected: return getAnalysisManager().getChildAnalysis(child); } + /// Returns the analysis for the given child operation of specific derived + /// operation type, or creates it if it doesn't exist. + template + AnalysisT &getChildAnalysis(OpTy child) { + return getAnalysisManager().getChildAnalysis(child); + } + /// Returns the current analysis manager. AnalysisManager getAnalysisManager() { return getPassState().analysisManager; @@ -286,6 +302,13 @@ protected: /// Return the current operation being transformed. OpT getOperation() { return cast(Pass::getOperation()); } + + /// Query an analysis for the current operation of the specific derived + /// operation type. + template + AnalysisT &getAnalysis() { + return Pass::getAnalysis(); + } }; /// Pass to transform an operation. diff --git a/mlir/unittests/Pass/AnalysisManagerTest.cpp b/mlir/unittests/Pass/AnalysisManagerTest.cpp index a99df39..41a9064 100644 --- a/mlir/unittests/Pass/AnalysisManagerTest.cpp +++ b/mlir/unittests/Pass/AnalysisManagerTest.cpp @@ -9,6 +9,8 @@ #include "mlir/Pass/AnalysisManager.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Function.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" #include "gtest/gtest.h" using namespace mlir; @@ -22,6 +24,9 @@ struct MyAnalysis { struct OtherAnalysis { OtherAnalysis(Operation *) {} }; +struct OpSpecificAnalysis { + OpSpecificAnalysis(ModuleOp) {} +}; TEST(AnalysisManagerTest, FineGrainModuleAnalysisPreservation) { MLIRContext context; @@ -138,4 +143,18 @@ TEST(AnalysisManagerTest, CustomInvalidation) { am.invalidate(pa); EXPECT_TRUE(am.getCachedAnalysis().hasValue()); } + +TEST(AnalysisManagerTest, OpSpecificAnalysis) { + MLIRContext context; + + // Create a module. + OwningModuleRef 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().hasValue()); +} + } // end namespace diff --git a/mlir/unittests/Pass/CMakeLists.txt b/mlir/unittests/Pass/CMakeLists.txt index a5aaee3..52cee34 100644 --- a/mlir/unittests/Pass/CMakeLists.txt +++ b/mlir/unittests/Pass/CMakeLists.txt @@ -1,5 +1,6 @@ add_mlir_unittest(MLIRPassTests AnalysisManagerTest.cpp + PassManagerTest.cpp ) target_link_libraries(MLIRPassTests PRIVATE diff --git a/mlir/unittests/Pass/PassManagerTest.cpp b/mlir/unittests/Pass/PassManagerTest.cpp new file mode 100644 index 0000000..29086a2 --- /dev/null +++ b/mlir/unittests/Pass/PassManagerTest.cpp @@ -0,0 +1,77 @@ +//===- PassManagerTest.cpp - PassManager 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/PassManager.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Function.h" +#include "mlir/Pass/Pass.h" +#include "gtest/gtest.h" + +using namespace mlir; +using namespace mlir::detail; + +namespace { +/// Analysis that operates on any operation. +struct GenericAnalysis { + GenericAnalysis(Operation *op) : isFunc(isa(op)) {} + const bool isFunc; +}; + +/// Analysis that operates on a specific operation. +struct OpSpecificAnalysis { + OpSpecificAnalysis(FuncOp op) : isSecret(op.getName() == "secret") {} + const bool isSecret; +}; + +/// Simple pass to annotate a FuncOp with the results of analysis. +/// Note: not using FunctionPass as it skip external functions. +struct AnnotateFunctionPass + : public PassWrapper> { + void runOnOperation() override { + FuncOp op = getOperation(); + Builder builder(op.getParentOfType()); + + auto &ga = getAnalysis(); + auto &sa = getAnalysis(); + + op.setAttr("isFunc", builder.getBoolAttr(ga.isFunc)); + op.setAttr("isSecret", builder.getBoolAttr(sa.isSecret)); + } +}; + +TEST(PassManagerTest, OpSpecificAnalysis) { + MLIRContext context; + Builder builder(&context); + + // Create a module with 2 functions. + OwningModuleRef module(ModuleOp::create(UnknownLoc::get(&context))); + for (StringRef name : {"secret", "not_secret"}) { + FuncOp func = + FuncOp::create(builder.getUnknownLoc(), name, + builder.getFunctionType(llvm::None, llvm::None)); + module->push_back(func); + } + + // Instantiate and run our pass. + PassManager pm(&context); + pm.addNestedPass(std::make_unique()); + LogicalResult result = pm.run(module.get()); + EXPECT_TRUE(succeeded(result)); + + // Verify that each function got annotated with expected attributes. + for (FuncOp func : module->getOps()) { + ASSERT_TRUE(func.getAttr("isFunc").isa()); + EXPECT_TRUE(func.getAttr("isFunc").cast().getValue()); + + bool isSecret = func.getName() == "secret"; + ASSERT_TRUE(func.getAttr("isSecret").isa()); + EXPECT_EQ(func.getAttr("isSecret").cast().getValue(), isSecret); + } +} + +} // end namespace