diff --git a/llvm/include/llvm/CodeGen/MachinePassManager.h b/llvm/include/llvm/CodeGen/MachinePassManager.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachinePassManager.h @@ -0,0 +1,252 @@ +//===- PassManager.h --- Pass management for CodeGen ------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This header defines the pass manager interface for codegen. The codegen +// pipeline consists of only machine function passes. There is no container +// relationship between IR module/function and machine function in terms of pass +// manager organization. So there is no need for adaptor classes (for example +// ModuleToMachineFunctionAdaptor). Since invalidation could only happen among +// machine function passes, there is no proxy classes to handle cross-IR-unit +// invalidation. IR analysis results are provided for machine function passes by +// their respective analysis managers such as ModuleAnalysisManager and +// FunctionAnalysisManager. +// +// TODO: Add MachineFunctionProperties support. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_MACHINEPASSMANAGER_H +#define LLVM_CODEGEN_MACHINEPASSMANAGER_H + +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/type_traits.h" + +namespace llvm { +class Module; + +extern template class AnalysisManager; + +/// An AnalysisManager that also exposes IR analysis results. +class MachineFunctionAnalysisManager : public AnalysisManager { +public: + using Base = AnalysisManager; + + MachineFunctionAnalysisManager() : Base(false), FAM(nullptr), MAM(nullptr) {} + MachineFunctionAnalysisManager(FunctionAnalysisManager &FAM, + ModuleAnalysisManager &MAM, + bool DebugLogging = false) + : Base(DebugLogging), FAM(&FAM), MAM(&MAM) {} + MachineFunctionAnalysisManager(MachineFunctionAnalysisManager &&) = default; + MachineFunctionAnalysisManager & + operator=(MachineFunctionAnalysisManager &&) = default; + + /// Get the result of an analysis pass for a Function. + /// + /// Runs the analysis if a cached result is not available. + template typename PassT::Result &getResult(Function &F) { + return FAM->getResult(F); + } + + /// Get the cached result of an analysis pass for a Function. + /// + /// This method never runs the analysis. + /// + /// \returns null if there is no cached result. + template + typename PassT::Result *getCachedResult(Function &F) { + return FAM->getCachedResult(F); + } + + /// Get the result of an analysis pass for a Module. + /// + /// Runs the analysis if a cached result is not available. + template typename PassT::Result &getResult(Module &M) { + return MAM->getResult(M); + } + + /// Get the cached result of an analysis pass for a Module. + /// + /// This method never runs the analysis. + /// + /// \returns null if there is no cached result. + template typename PassT::Result *getCachedResult(Module &M) { + return MAM->getCachedResult(M); + } + + /// Get the result of an analysis pass for a MachineFunction. + /// + /// Runs the analysis if a cached result is not available. + using Base::getResult; + + /// Get the cached result of an analysis pass for a MachineFunction. + /// + /// This method never runs the analysis. + /// + /// \returns null if there is no cached result. + using Base::getCachedResult; + + // FIXME: Add LoopAnalysisManager or CGSCCAnalysisManager if needed. + FunctionAnalysisManager *FAM; + ModuleAnalysisManager *MAM; +}; + +extern template class PassManager; + +/// MachineFunctionPassManager adds/removes below features to/from the base +/// PassManager template instantiation. +/// +/// - Support passes that implement doInitialization/doFinalization. This is for +/// machine function passes to work on module level constructs. One such pass +/// is AsmPrinter. +/// +/// - Support machine module pass which runs over the module (for example, +/// MachineOutliner). A machine module pass needs to define the method: +/// +/// ```Error run(Module &, MachineFunctionAnalysisManager &)``` +/// +/// FIXME: machine module passes still need to define the usual machine +/// function pass interface, namely, +/// `PreservedAnalyses run(MachineFunction &, +/// MachineFunctionAnalysisManager &)` +/// But this interface wouldn't be executed. It is just a placeholder +/// to satisfy the pass manager type-erased inteface. This +/// special-casing of machine module pass is due to its limited use +/// cases and the unnecessary complexity it may bring to the machine +/// pass manager. +/// +/// - The base class `run` method is replaced by an alternative `run` method. +/// See details below. +/// +/// - Support codegening in the SCC order. Users include interprocedural +/// register allocation (IPRA). +class MachineFunctionPassManager + : public PassManager { + using Base = PassManager; + +public: + MachineFunctionPassManager(bool DebugLogging = false, + bool RequireCodeGenSCCOrder = false) + : Base(DebugLogging), RequireCodeGenSCCOrder(RequireCodeGenSCCOrder) {} + MachineFunctionPassManager(MachineFunctionPassManager &&) = default; + MachineFunctionPassManager & + operator=(MachineFunctionPassManager &&) = default; + + /// Run machine passes for a Module. + /// + /// The intended use is to start the codegen pipeline for a Module. The base + /// class's `run` method is deliberately hidden by this due to the observation + /// that we don't yet have the use cases of compositing two instances of + /// machine pass managers, or compositing machine pass managers with other + /// types of pass managers. + Error run(Module &M, MachineFunctionAnalysisManager &MFAM); + + template void addPass(PassT &&Pass) { + Base::addPass(std::forward(Pass)); + PassConceptT *P = Passes.back().get(); + addDoInitialization(P); + addDoFinalization(P); + + // Add machine module pass. + addRunOnModule(P); + } + +private: + template + using has_init_t = decltype(std::declval().doInitialization( + std::declval(), + std::declval())); + + template + std::enable_if_t::value> + addDoInitialization(PassConceptT *Pass) {} + + template + std::enable_if_t::value> + addDoInitialization(PassConceptT *Pass) { + using PassModelT = + detail::PassModel; + auto *P = static_cast(Pass); + InitializationFuncs.emplace_back( + [=](Module &M, MachineFunctionAnalysisManager &MFAM) { + return P->Pass.doInitialization(M, MFAM); + }); + } + + template + using has_fini_t = decltype(std::declval().doFinalization( + std::declval(), + std::declval())); + + template + std::enable_if_t::value> + addDoFinalization(PassConceptT *Pass) {} + + template + std::enable_if_t::value> + addDoFinalization(PassConceptT *Pass) { + using PassModelT = + detail::PassModel; + auto *P = static_cast(Pass); + FinalizationFuncs.emplace_back( + [=](Module &M, MachineFunctionAnalysisManager &MFAM) { + return P->Pass.doFinalization(M, MFAM); + }); + } + + template + using is_machine_module_pass_t = decltype(std::declval().run( + std::declval(), + std::declval())); + + template + using is_machine_function_pass_t = decltype(std::declval().run( + std::declval(), + std::declval())); + + template + std::enable_if_t::value> + addRunOnModule(PassConceptT *Pass) {} + + template + std::enable_if_t::value> + addRunOnModule(PassConceptT *Pass) { + static_assert(is_detected::value, + "machine module pass needs to define machine function pass " + "api. sorry."); + + using PassModelT = + detail::PassModel; + auto *P = static_cast(Pass); + MachineModulePasses.emplace( + Passes.size() - 1, + [=](Module &M, MachineFunctionAnalysisManager &MFAM) { + return P->Pass.run(M, MFAM); + }); + } + + using FuncTy = Error(Module &, MachineFunctionAnalysisManager &); + SmallVector, 4> InitializationFuncs; + SmallVector, 4> FinalizationFuncs; + + using PassIndex = decltype(Passes)::size_type; + std::map> MachineModulePasses; + + // Run codegen in the SCC order. + bool RequireCodeGenSCCOrder; +}; + +} // end namespace llvm + +#endif // LLVM_CODEGEN_MACHINEPASSMANAGER_H diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h --- a/llvm/include/llvm/IR/PassManager.h +++ b/llvm/include/llvm/IR/PassManager.h @@ -558,7 +558,7 @@ static bool isRequired() { return true; } -private: +protected: using PassConceptT = detail::PassConcept; diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -92,6 +92,7 @@ MachineOperand.cpp MachineOptimizationRemarkEmitter.cpp MachineOutliner.cpp + MachinePassManager.cpp MachinePipeliner.cpp MachinePostDominators.cpp MachineRegionInfo.cpp diff --git a/llvm/lib/CodeGen/LLVMBuild.txt b/llvm/lib/CodeGen/LLVMBuild.txt --- a/llvm/lib/CodeGen/LLVMBuild.txt +++ b/llvm/lib/CodeGen/LLVMBuild.txt @@ -21,4 +21,4 @@ type = Library name = CodeGen parent = Libraries -required_libraries = Analysis BitReader BitWriter Core MC ProfileData Scalar Support Target TransformUtils +required_libraries = Analysis BitReader BitWriter Core MC Passes ProfileData Scalar Support Target TransformUtils diff --git a/llvm/lib/CodeGen/MachinePassManager.cpp b/llvm/lib/CodeGen/MachinePassManager.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MachinePassManager.cpp @@ -0,0 +1,104 @@ +//===---------- MachinePassManager.cpp ------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains the pass management machinery for machine functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/PassManagerImpl.h" + +using namespace llvm; + +namespace llvm { +template class AllAnalysesOn; +template class AnalysisManager; +template class PassManager; + +Error MachineFunctionPassManager::run(Module &M, + MachineFunctionAnalysisManager &MFAM) { + // MachineModuleAnalysis is a module analysis pass that is never invalidated + // because we don't run any module pass in codegen pipeline. This is very + // important because the codegen state is stored in MMI which is the analysis + // result of MachineModuleAnalysis. MMI should not be recomputed. + auto &MMI = MFAM.getResult(M); + + (void)RequireCodeGenSCCOrder; + assert(!RequireCodeGenSCCOrder && "not implemented"); + + if (DebugLogging) { + dbgs() << "Starting " << getTypeName() + << " pass manager run.\n"; + } + + for (auto &F : InitializationFuncs) { + if (auto Err = F(M, MFAM)) + return Err; + } + + unsigned Idx = 0; + size_t Size = Passes.size(); + do { + // Run machine module passes + for (; MachineModulePasses.count(Idx) && Idx != Size; ++Idx) { + if (DebugLogging) + dbgs() << "Running pass: " << Passes[Idx]->name() << " on " + << M.getName() << '\n'; + if (auto Err = MachineModulePasses.at(Idx)(M, MFAM)) + return Err; + } + + // Finish running all passes. + if (Idx == Size) + break; + + // Run machine function passes + + // Get index range of machine function passes. + unsigned Begin = Idx; + for (; !MachineModulePasses.count(Idx) && Idx != Size; ++Idx) + ; + + for (Function &F : M) { + // Do not codegen any 'available_externally' functions at all, they have + // definitions outside the translation unit. + if (F.hasAvailableExternallyLinkage()) + continue; + + MachineFunction &MF = MMI.getOrCreateMachineFunction(F); + PassInstrumentation PI = MFAM.getResult(MF); + + for (unsigned I = Begin, E = Idx; I != E; ++I) { + auto *P = Passes[I].get(); + + if (!PI.runBeforePass(*P, MF)) + continue; + + // TODO: EmitSizeRemarks + PreservedAnalyses PassPA = P->run(MF, MFAM); + PI.runAfterPass(*P, MF); + MFAM.invalidate(MF, PassPA); + } + } + } while (true); + + for (auto &F : FinalizationFuncs) { + if (auto Err = F(M, MFAM)) + return Err; + } + + if (DebugLogging) { + dbgs() << "Finished " << getTypeName() + << " pass manager run.\n"; + } + + return Error::success(); +} + +} // namespace llvm diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt --- a/llvm/unittests/CodeGen/CMakeLists.txt +++ b/llvm/unittests/CodeGen/CMakeLists.txt @@ -7,6 +7,7 @@ Core MC MIRParser + Passes SelectionDAG Support Target @@ -20,6 +21,7 @@ MachineInstrBundleIteratorTest.cpp MachineInstrTest.cpp MachineOperandTest.cpp + PassManagerTest.cpp ScalableVectorMVTsTest.cpp TypeTraitsTest.cpp TargetOptionsTest.cpp diff --git a/llvm/unittests/CodeGen/PassManagerTest.cpp b/llvm/unittests/CodeGen/PassManagerTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/CodeGen/PassManagerTest.cpp @@ -0,0 +1,308 @@ +//===- llvm/unittest/CodeGen/PassManager.cpp - PassManager 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 "llvm/ADT/Triple.h" +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LoopAnalysisManager.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class TestFunctionAnalysis : public AnalysisInfoMixin { +public: + struct Result { + Result(int Count) : InstructionCount(Count) {} + int InstructionCount; + }; + + /// Run the analysis pass over the function and return a result. + Result run(Function &F, FunctionAnalysisManager &AM) { + int Count = 0; + for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) + for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE; + ++II) + ++Count; + return Result(Count); + } + +private: + friend AnalysisInfoMixin; + static AnalysisKey Key; +}; + +AnalysisKey TestFunctionAnalysis::Key; + +class TestMachineFunctionAnalysis + : public AnalysisInfoMixin { +public: + struct Result { + Result(int Count) : InstructionCount(Count) {} + int InstructionCount; + }; + + /// Run the analysis pass over the machine function and return a result. + Result run(MachineFunction &MF, MachineFunctionAnalysisManager::Base &AM) { + auto &MFAM = static_cast(AM); + // Query function analysis result. + TestFunctionAnalysis::Result &FAR = + MFAM.getResult(MF.getFunction()); + // + 5 + return FAR.InstructionCount; + } + +private: + friend AnalysisInfoMixin; + static AnalysisKey Key; +}; + +AnalysisKey TestMachineFunctionAnalysis::Key; + +const std::string DoInitErrMsg = "doInitialization failed"; +const std::string DoFinalErrMsg = "doFinalization failed"; + +struct TestMachineFunctionPass : public PassInfoMixin { + TestMachineFunctionPass(int &Count, std::vector &BeforeInitialization, + std::vector &BeforeFinalization, + std::vector &MachineFunctionPassCount) + : Count(Count), BeforeInitialization(BeforeInitialization), + BeforeFinalization(BeforeFinalization), + MachineFunctionPassCount(MachineFunctionPassCount) {} + + Error doInitialization(Module &M, MachineFunctionAnalysisManager &MFAM) { + // Force doInitialization fail by starting with big `Count`. + if (Count > 10000) + return make_error(DoInitErrMsg, inconvertibleErrorCode()); + + // + 1 + ++Count; + BeforeInitialization.push_back(Count); + return Error::success(); + } + Error doFinalization(Module &M, MachineFunctionAnalysisManager &MFAM) { + // Force doFinalization fail by starting with big `Count`. + if (Count > 1000) + return make_error(DoFinalErrMsg, inconvertibleErrorCode()); + + // + 1 + ++Count; + BeforeFinalization.push_back(Count); + return Error::success(); + } + + PreservedAnalyses run(MachineFunction &MF, + MachineFunctionAnalysisManager &MFAM) { + // Query function analysis result. + TestFunctionAnalysis::Result &FAR = + MFAM.getResult(MF.getFunction()); + // 3 + 1 + 1 = 5 + Count += FAR.InstructionCount; + + // Query module analysis result. + MachineModuleInfo &MMI = + MFAM.getResult(*MF.getFunction().getParent()); + // 1 + 1 + 1 = 3 + Count += (MMI.getModule() == MF.getFunction().getParent()); + + // Query machine function analysis result. + TestMachineFunctionAnalysis::Result &MFAR = + MFAM.getResult(MF); + // 3 + 1 + 1 = 5 + Count += MFAR.InstructionCount; + + MachineFunctionPassCount.push_back(Count); + + return PreservedAnalyses::none(); + } + + int &Count; + std::vector &BeforeInitialization; + std::vector &BeforeFinalization; + std::vector &MachineFunctionPassCount; +}; + +struct TestMachineModulePass : public PassInfoMixin { + TestMachineModulePass(int &Count, std::vector &MachineModulePassCount) + : Count(Count), MachineModulePassCount(MachineModulePassCount) {} + + Error run(Module &M, MachineFunctionAnalysisManager &MFAM) { + MachineModuleInfo &MMI = MFAM.getResult(M); + // + 1 + Count += (MMI.getModule() == &M); + MachineModulePassCount.push_back(Count); + return Error::success(); + } + + PreservedAnalyses run(MachineFunction &MF, + MachineFunctionAnalysisManager &AM) { + llvm_unreachable( + "This should never be reached because this is machine module pass"); + } + + int &Count; + std::vector &MachineModulePassCount; +}; + +std::unique_ptr parseIR(LLVMContext &Context, const char *IR) { + SMDiagnostic Err; + return parseAssemblyString(IR, Err, Context); +} + +class PassManagerTest : public ::testing::Test { +protected: + LLVMContext Context; + std::unique_ptr M; + std::unique_ptr TM; + +public: + PassManagerTest() + : M(parseIR(Context, "define void @f() {\n" + "entry:\n" + " call void @g()\n" + " call void @h()\n" + " ret void\n" + "}\n" + "define void @g() {\n" + " ret void\n" + "}\n" + "define void @h() {\n" + " ret void\n" + "}\n")) { + // MachineModuleAnalysis needs a TargetMachine instance. + llvm::InitializeAllTargets(); + + std::string TripleName = Triple::normalize(sys::getDefaultTargetTriple()); + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); + if (!TheTarget) + return; + + TargetOptions Options; + TM.reset(TheTarget->createTargetMachine(TripleName, "", "", Options, None)); + } +}; + +TEST_F(PassManagerTest, Basic) { + if (!TM) + return; + + LLVMTargetMachine *LLVMTM = static_cast(TM.get()); + M->setDataLayout(TM->createDataLayout()); + + LoopAnalysisManager LAM(/*DebugLogging=*/true); + FunctionAnalysisManager FAM(/*DebugLogging=*/true); + CGSCCAnalysisManager CGAM(/*DebugLogging=*/true); + ModuleAnalysisManager MAM(/*DebugLogging=*/true); + PassBuilder PB(TM.get()); + PB.registerModuleAnalyses(MAM); + PB.registerFunctionAnalyses(FAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + FAM.registerPass([&] { return TestFunctionAnalysis(); }); + FAM.registerPass([&] { return PassInstrumentationAnalysis(); }); + MAM.registerPass([&] { return MachineModuleAnalysis(LLVMTM); }); + MAM.registerPass([&] { return PassInstrumentationAnalysis(); }); + + MachineFunctionAnalysisManager MFAM; + { + // Test move assignment. + MachineFunctionAnalysisManager NestedMFAM(FAM, MAM, + /*DebugLogging*/ true); + NestedMFAM.registerPass([&] { return PassInstrumentationAnalysis(); }); + NestedMFAM.registerPass([&] { return TestMachineFunctionAnalysis(); }); + MFAM = std::move(NestedMFAM); + } + + int Count = 0; + std::vector BeforeInitialization[2]; + std::vector BeforeFinalization[2]; + std::vector TestMachineFunctionCount[2]; + std::vector TestMachineModuleCount[2]; + + MachineFunctionPassManager MFPM; + { + // Test move assignment. + MachineFunctionPassManager NestedMFPM(/*DebugLogging*/ true); + NestedMFPM.addPass(TestMachineModulePass(Count, TestMachineModuleCount[0])); + NestedMFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[0], + BeforeFinalization[0], + TestMachineFunctionCount[0])); + NestedMFPM.addPass(TestMachineModulePass(Count, TestMachineModuleCount[1])); + NestedMFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[1], + BeforeFinalization[1], + TestMachineFunctionCount[1])); + MFPM = std::move(NestedMFPM); + } + + ASSERT_FALSE(errorToBool(MFPM.run(*M, MFAM))); + + // Check first machine module pass + EXPECT_EQ(1u, TestMachineModuleCount[0].size()); + EXPECT_EQ(3, TestMachineModuleCount[0][0]); + + // Check first machine function pass + EXPECT_EQ(1u, BeforeInitialization[0].size()); + EXPECT_EQ(1, BeforeInitialization[0][0]); + EXPECT_EQ(3u, TestMachineFunctionCount[0].size()); + EXPECT_EQ(10, TestMachineFunctionCount[0][0]); + EXPECT_EQ(13, TestMachineFunctionCount[0][1]); + EXPECT_EQ(16, TestMachineFunctionCount[0][2]); + EXPECT_EQ(1u, BeforeFinalization[0].size()); + EXPECT_EQ(31, BeforeFinalization[0][0]); + + // Check second machine module pass + EXPECT_EQ(1u, TestMachineModuleCount[1].size()); + EXPECT_EQ(17, TestMachineModuleCount[1][0]); + + // Check second machine function pass + EXPECT_EQ(1u, BeforeInitialization[1].size()); + EXPECT_EQ(2, BeforeInitialization[1][0]); + EXPECT_EQ(3u, TestMachineFunctionCount[1].size()); + EXPECT_EQ(24, TestMachineFunctionCount[1][0]); + EXPECT_EQ(27, TestMachineFunctionCount[1][1]); + EXPECT_EQ(30, TestMachineFunctionCount[1][2]); + EXPECT_EQ(1u, BeforeFinalization[1].size()); + EXPECT_EQ(32, BeforeFinalization[1][0]); + + EXPECT_EQ(32, Count); + + // doInitialization returns error + Count = 10000; + MFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[1], + BeforeFinalization[1], + TestMachineFunctionCount[1])); + std::string Message; + llvm::handleAllErrors(MFPM.run(*M, MFAM), [&](llvm::StringError &Error) { + Message = Error.getMessage(); + }); + EXPECT_EQ(Message, DoInitErrMsg); + + // doFinalization returns error + Count = 1000; + MFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization[1], + BeforeFinalization[1], + TestMachineFunctionCount[1])); + llvm::handleAllErrors(MFPM.run(*M, MFAM), [&](llvm::StringError &Error) { + Message = Error.getMessage(); + }); + EXPECT_EQ(Message, DoFinalErrMsg); +} + +} // namespace