1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00
llvm-mirror/unittests/Transforms/Scalar/LoopPassManagerTest.cpp
Arthur Eubanks 7a1762f190 [NewPM] Don't mark AA analyses as preserved
Currently all AA analyses marked as preserved are stateless, not taking
into account their dependent analyses. So there's no need to mark them
as preserved, they won't be invalidated unless their analyses are.

SCEVAAResults was the one exception to this, it was treated like a
typical analysis result. Make it like the others and don't invalidate
unless SCEV is invalidated.

Reviewed By: asbirlea

Differential Revision: https://reviews.llvm.org/D102032
2021-05-18 13:49:03 -07:00

1669 lines
71 KiB
C++

//===- llvm/unittest/Analysis/LoopPassManagerTest.cpp - LPM 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/Transforms/Scalar/LoopPassManager.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/SourceMgr.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
using testing::DoDefault;
using testing::Return;
using testing::Expectation;
using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::_;
template <typename DerivedT, typename IRUnitT,
typename AnalysisManagerT = AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class MockAnalysisHandleBase {
public:
class Analysis : public AnalysisInfoMixin<Analysis> {
friend AnalysisInfoMixin<Analysis>;
friend MockAnalysisHandleBase;
static AnalysisKey Key;
DerivedT *Handle;
Analysis(DerivedT &Handle) : Handle(&Handle) {
static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value,
"Must pass the derived type to this template!");
}
public:
class Result {
friend MockAnalysisHandleBase;
DerivedT *Handle;
Result(DerivedT &Handle) : Handle(&Handle) {}
public:
// Forward invalidation events to the mock handle.
bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA,
typename AnalysisManagerT::Invalidator &Inv) {
return Handle->invalidate(IR, PA, Inv);
}
};
Result run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) {
return Handle->run(IR, AM, ExtraArgs...);
}
};
Analysis getAnalysis() { return Analysis(static_cast<DerivedT &>(*this)); }
typename Analysis::Result getResult() {
return typename Analysis::Result(static_cast<DerivedT &>(*this));
}
protected:
// FIXME: MSVC seems unable to handle a lambda argument to Invoke from within
// the template, so we use a boring static function.
static bool invalidateCallback(IRUnitT &IR, const PreservedAnalyses &PA,
typename AnalysisManagerT::Invalidator &Inv) {
auto PAC = PA.template getChecker<Analysis>();
return !PAC.preserved() &&
!PAC.template preservedSet<AllAnalysesOn<IRUnitT>>();
}
/// Derived classes should call this in their constructor to set up default
/// mock actions. (We can't do this in our constructor because this has to
/// run after the DerivedT is constructed.)
void setDefaults() {
ON_CALL(static_cast<DerivedT &>(*this),
run(_, _, testing::Matcher<ExtraArgTs>(_)...))
.WillByDefault(Return(this->getResult()));
ON_CALL(static_cast<DerivedT &>(*this), invalidate(_, _, _))
.WillByDefault(Invoke(&invalidateCallback));
}
};
template <typename DerivedT, typename IRUnitT, typename AnalysisManagerT,
typename... ExtraArgTs>
AnalysisKey MockAnalysisHandleBase<DerivedT, IRUnitT, AnalysisManagerT,
ExtraArgTs...>::Analysis::Key;
/// Mock handle for loop analyses.
///
/// This is provided as a template accepting an (optional) integer. Because
/// analyses are identified and queried by type, this allows constructing
/// multiple handles with distinctly typed nested 'Analysis' types that can be
/// registered and queried. If you want to register multiple loop analysis
/// passes, you'll need to instantiate this type with different values for I.
/// For example:
///
/// MockLoopAnalysisHandleTemplate<0> h0;
/// MockLoopAnalysisHandleTemplate<1> h1;
/// typedef decltype(h0)::Analysis Analysis0;
/// typedef decltype(h1)::Analysis Analysis1;
template <size_t I = static_cast<size_t>(-1)>
struct MockLoopAnalysisHandleTemplate
: MockAnalysisHandleBase<MockLoopAnalysisHandleTemplate<I>, Loop,
LoopAnalysisManager,
LoopStandardAnalysisResults &> {
typedef typename MockLoopAnalysisHandleTemplate::Analysis Analysis;
MOCK_METHOD3_T(run, typename Analysis::Result(Loop &, LoopAnalysisManager &,
LoopStandardAnalysisResults &));
MOCK_METHOD3_T(invalidate, bool(Loop &, const PreservedAnalyses &,
LoopAnalysisManager::Invalidator &));
MockLoopAnalysisHandleTemplate() { this->setDefaults(); }
};
typedef MockLoopAnalysisHandleTemplate<> MockLoopAnalysisHandle;
struct MockFunctionAnalysisHandle
: MockAnalysisHandleBase<MockFunctionAnalysisHandle, Function> {
MOCK_METHOD2(run, Analysis::Result(Function &, FunctionAnalysisManager &));
MOCK_METHOD3(invalidate, bool(Function &, const PreservedAnalyses &,
FunctionAnalysisManager::Invalidator &));
MockFunctionAnalysisHandle() { setDefaults(); }
};
template <typename DerivedT, typename IRUnitT,
typename AnalysisManagerT = AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class MockPassHandleBase {
public:
class Pass : public PassInfoMixin<Pass> {
friend MockPassHandleBase;
DerivedT *Handle;
Pass(DerivedT &Handle) : Handle(&Handle) {
static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value,
"Must pass the derived type to this template!");
}
public:
PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
ExtraArgTs... ExtraArgs) {
return Handle->run(IR, AM, ExtraArgs...);
}
};
Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); }
protected:
/// Derived classes should call this in their constructor to set up default
/// mock actions. (We can't do this in our constructor because this has to
/// run after the DerivedT is constructed.)
void setDefaults() {
ON_CALL(static_cast<DerivedT &>(*this),
run(_, _, testing::Matcher<ExtraArgTs>(_)...))
.WillByDefault(Return(PreservedAnalyses::all()));
}
};
struct MockLoopPassHandle
: MockPassHandleBase<MockLoopPassHandle, Loop, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &> {
MOCK_METHOD4(run,
PreservedAnalyses(Loop &, LoopAnalysisManager &,
LoopStandardAnalysisResults &, LPMUpdater &));
MockLoopPassHandle() { setDefaults(); }
};
struct MockLoopNestPassHandle
: MockPassHandleBase<MockLoopNestPassHandle, LoopNest, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &> {
MOCK_METHOD4(run,
PreservedAnalyses(LoopNest &, LoopAnalysisManager &,
LoopStandardAnalysisResults &, LPMUpdater &));
MockLoopNestPassHandle() { setDefaults(); }
};
struct MockFunctionPassHandle
: MockPassHandleBase<MockFunctionPassHandle, Function> {
MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &));
MockFunctionPassHandle() { setDefaults(); }
};
struct MockModulePassHandle : MockPassHandleBase<MockModulePassHandle, Module> {
MOCK_METHOD2(run, PreservedAnalyses(Module &, ModuleAnalysisManager &));
MockModulePassHandle() { setDefaults(); }
};
/// Define a custom matcher for objects which support a 'getName' method
/// returning a StringRef.
///
/// LLVM often has IR objects or analysis objects which expose a StringRef name
/// and in tests it is convenient to match these by name for readability. This
/// matcher supports any type exposing a getName() method of this form.
///
/// It should be used as:
///
/// HasName("my_function")
///
/// No namespace or other qualification is required.
MATCHER_P(HasName, Name, "") {
// The matcher's name and argument are printed in the case of failure, but we
// also want to print out the name of the argument. This uses an implicitly
// avaiable std::ostream, so we have to construct a std::string.
*result_listener << "has name '" << arg.getName().str() << "'";
return Name == arg.getName();
}
std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
SMDiagnostic Err;
return parseAssemblyString(IR, Err, C);
}
class LoopPassManagerTest : public ::testing::Test {
protected:
LLVMContext Context;
std::unique_ptr<Module> M;
LoopAnalysisManager LAM;
FunctionAnalysisManager FAM;
ModuleAnalysisManager MAM;
MockLoopAnalysisHandle MLAHandle;
MockLoopPassHandle MLPHandle;
MockLoopNestPassHandle MLNPHandle;
MockFunctionPassHandle MFPHandle;
MockModulePassHandle MMPHandle;
static PreservedAnalyses
getLoopAnalysisResult(Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &) {
(void)AM.getResult<MockLoopAnalysisHandle::Analysis>(L, AR);
return PreservedAnalyses::all();
};
public:
LoopPassManagerTest()
: M(parseIR(Context,
"define void @f(i1* %ptr) {\n"
"entry:\n"
" br label %loop.0\n"
"loop.0:\n"
" %cond.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0, label %loop.0.0.ph, label %end\n"
"loop.0.0.ph:\n"
" br label %loop.0.0\n"
"loop.0.0:\n"
" %cond.0.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n"
"loop.0.1.ph:\n"
" br label %loop.0.1\n"
"loop.0.1:\n"
" %cond.0.1 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.1, label %loop.0.1, label %loop.0.latch\n"
"loop.0.latch:\n"
" br label %loop.0\n"
"end:\n"
" ret void\n"
"}\n"
"\n"
"define void @g(i1* %ptr) {\n"
"entry:\n"
" br label %loop.g.0\n"
"loop.g.0:\n"
" %cond.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0, label %loop.g.0, label %end\n"
"end:\n"
" ret void\n"
"}\n")),
LAM(), FAM(), MAM() {
// Register our mock analysis.
LAM.registerPass([&] { return MLAHandle.getAnalysis(); });
// We need DominatorTreeAnalysis for LoopAnalysis.
FAM.registerPass([&] { return DominatorTreeAnalysis(); });
FAM.registerPass([&] { return LoopAnalysis(); });
// We also allow loop passes to assume a set of other analyses and so need
// those.
FAM.registerPass([&] { return AAManager(); });
FAM.registerPass([&] { return AssumptionAnalysis(); });
FAM.registerPass([&] { return BlockFrequencyAnalysis(); });
FAM.registerPass([&] { return BranchProbabilityAnalysis(); });
FAM.registerPass([&] { return PostDominatorTreeAnalysis(); });
FAM.registerPass([&] { return MemorySSAAnalysis(); });
FAM.registerPass([&] { return ScalarEvolutionAnalysis(); });
FAM.registerPass([&] { return TargetLibraryAnalysis(); });
FAM.registerPass([&] { return TargetIRAnalysis(); });
// Register required pass instrumentation analysis.
LAM.registerPass([&] { return PassInstrumentationAnalysis(); });
FAM.registerPass([&] { return PassInstrumentationAnalysis(); });
MAM.registerPass([&] { return PassInstrumentationAnalysis(); });
// Cross-register proxies.
LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); });
FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); });
FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
}
};
TEST_F(LoopPassManagerTest, Basic) {
ModulePassManager MPM;
::testing::InSequence MakeExpectationsSequenced;
// First we just visit all the loops in all the functions and get their
// analysis results. This will run the analysis a total of four times,
// once for each loop.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
// Wire the loop pass through pass managers into the module pipeline.
{
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
FunctionPassManager FPM;
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
// Next we run two passes over the loops. The first one invalidates the
// analyses for one loop, the second ones try to get the analysis results.
// This should force only one analysis to re-run within the loop PM, but will
// also invalidate everything after the loop pass manager finishes.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(DoDefault())
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(InvokeWithoutArgs([] { return PreservedAnalyses::none(); }))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(DoDefault())
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _))
.WillOnce(DoDefault())
.WillOnce(Invoke(getLoopAnalysisResult));
// Wire two loop pass runs into the module pipeline.
{
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
FunctionPassManager FPM;
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
// And now run the pipeline across the module.
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, FunctionPassInvalidationOfLoopAnalyses) {
ModulePassManager MPM;
FunctionPassManager FPM;
// We process each function completely in sequence.
::testing::Sequence FSequence, GSequence;
// First, force the analysis result to be computed for each loop.
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _))
.InSequence(GSequence)
.WillOnce(DoDefault());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
// No need to re-run if we require again from a fresh loop pass manager.
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
// For 'f', preserve most things but not the specific loop analyses.
auto PA = getLoopPassPreservedAnalyses();
if (EnableMSSALoopDependency)
PA.preserve<MemorySSAAnalysis>();
EXPECT_CALL(MFPHandle, run(HasName("f"), _))
.InSequence(FSequence)
.WillOnce(Return(PA));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.0"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
// On one loop, skip the invalidation (as though we did an internal update).
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.1"), _, _))
.InSequence(FSequence)
.WillOnce(Return(false));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
// Now two loops still have to be recomputed.
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _))
.InSequence(FSequence)
.WillOnce(DoDefault());
// Preserve things in the second function to ensure invalidation remains
// isolated to one function.
EXPECT_CALL(MFPHandle, run(HasName("g"), _))
.InSequence(GSequence)
.WillOnce(DoDefault());
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
EXPECT_CALL(MFPHandle, run(HasName("f"), _))
.InSequence(FSequence)
.WillOnce(DoDefault());
// For 'g', fail to preserve anything, causing the loops themselves to be
// cleared. We don't get an invalidation event here as the loop is gone, but
// we should still have to recompute the analysis.
EXPECT_CALL(MFPHandle, run(HasName("g"), _))
.InSequence(GSequence)
.WillOnce(Return(PreservedAnalyses::none()));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _))
.InSequence(GSequence)
.WillOnce(DoDefault());
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
// Verify with a separate function pass run that we didn't mess up 'f's
// cache. No analysis runs should be necessary here.
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, ModulePassInvalidationOfLoopAnalyses) {
ModulePassManager MPM;
::testing::InSequence MakeExpectationsSequenced;
// First, force the analysis result to be computed for each loop.
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
// Walking all the way out and all the way back in doesn't re-run the
// analysis.
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
// But a module pass that doesn't preserve the actual mock loop analysis
// invalidates all the way down and forces recomputing.
EXPECT_CALL(MMPHandle, run(_, _)).WillOnce(InvokeWithoutArgs([] {
auto PA = getLoopPassPreservedAnalyses();
PA.preserve<FunctionAnalysisManagerModuleProxy>();
if (EnableMSSALoopDependency)
PA.preserve<MemorySSAAnalysis>();
return PA;
}));
// All the loop analyses from both functions get invalidated before we
// recompute anything.
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.0"), _, _));
// On one loop, again skip the invalidation (as though we did an internal
// update).
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.1"), _, _))
.WillOnce(Return(false));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0"), _, _));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.g.0"), _, _));
// Now all but one of the loops gets re-analyzed.
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
MPM.addPass(MMPHandle.getPass());
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
// Verify that the cached values persist.
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
// Now we fail to preserve the loop analysis and observe that the loop
// analyses are cleared (so no invalidation event) as the loops themselves
// are no longer valid.
EXPECT_CALL(MMPHandle, run(_, _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
PA.preserve<FunctionAnalysisManagerModuleProxy>();
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
MPM.addPass(MMPHandle.getPass());
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
// Verify that the cached values persist.
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
// Next, check that even if we preserve everything within the function itelf,
// if the function's module pass proxy isn't preserved and the potential set
// of functions changes, the clear reaches the loop analyses as well. This
// will again trigger re-runs but not invalidation events.
EXPECT_CALL(MMPHandle, run(_, _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
PA.preserveSet<AllAnalysesOn<Function>>();
PA.preserveSet<AllAnalysesOn<Loop>>();
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
MPM.addPass(MMPHandle.getPass());
MPM.addPass(createModuleToFunctionPassAdaptor(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())));
MPM.run(*M, MAM);
}
// Test that if any of the bundled analyses provided in the LPM's signature
// become invalid, the analysis proxy itself becomes invalid and we clear all
// loop analysis results.
TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) {
ModulePassManager MPM;
FunctionPassManager FPM;
::testing::InSequence MakeExpectationsSequenced;
// First, force the analysis result to be computed for each loop.
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
// No need to re-run if we require again from a fresh loop pass manager.
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
// Preserving everything but the loop analyses themselves results in
// invalidation and running.
EXPECT_CALL(MFPHandle, run(HasName("f"), _))
.WillOnce(Return(getLoopPassPreservedAnalyses()));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
// The rest don't invalidate analyses, they only trigger re-runs because we
// clear the cache completely.
EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
// Not preserving `AAManager`.
PA.preserve<DominatorTreeAnalysis>();
PA.preserve<LoopAnalysis>();
PA.preserve<LoopAnalysisManagerFunctionProxy>();
PA.preserve<ScalarEvolutionAnalysis>();
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
// Not preserving `DominatorTreeAnalysis`.
PA.preserve<LoopAnalysis>();
PA.preserve<LoopAnalysisManagerFunctionProxy>();
PA.preserve<ScalarEvolutionAnalysis>();
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
PA.preserve<DominatorTreeAnalysis>();
// Not preserving the `LoopAnalysis`.
PA.preserve<LoopAnalysisManagerFunctionProxy>();
PA.preserve<ScalarEvolutionAnalysis>();
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
PA.preserve<DominatorTreeAnalysis>();
PA.preserve<LoopAnalysis>();
// Not preserving the `LoopAnalysisManagerFunctionProxy`.
PA.preserve<ScalarEvolutionAnalysis>();
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = PreservedAnalyses::none();
PA.preserve<DominatorTreeAnalysis>();
PA.preserve<LoopAnalysis>();
PA.preserve<LoopAnalysisManagerFunctionProxy>();
// Not preserving `ScalarEvolutionAnalysis`.
return PA;
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(
RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>()));
// After all the churn on 'f', we'll compute the loop analysis results for
// 'g' once with a requires pass and then run our mock pass over g a bunch
// but just get cached results each time.
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
EXPECT_CALL(MFPHandle, run(HasName("g"), _)).Times(6);
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, IndirectInvalidation) {
// We need two distinct analysis types and handles.
enum { A, B };
MockLoopAnalysisHandleTemplate<A> MLAHandleA;
MockLoopAnalysisHandleTemplate<B> MLAHandleB;
LAM.registerPass([&] { return MLAHandleA.getAnalysis(); });
LAM.registerPass([&] { return MLAHandleB.getAnalysis(); });
typedef decltype(MLAHandleA)::Analysis AnalysisA;
typedef decltype(MLAHandleB)::Analysis AnalysisB;
// Set up AnalysisA to depend on our AnalysisB. For testing purposes we just
// need to get the AnalysisB results in AnalysisA's run method and check if
// AnalysisB gets invalidated in AnalysisA's invalidate method.
ON_CALL(MLAHandleA, run(_, _, _))
.WillByDefault(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR) {
(void)AM.getResult<AnalysisB>(L, AR);
return MLAHandleA.getResult();
}));
ON_CALL(MLAHandleA, invalidate(_, _, _))
.WillByDefault(Invoke([](Loop &L, const PreservedAnalyses &PA,
LoopAnalysisManager::Invalidator &Inv) {
auto PAC = PA.getChecker<AnalysisA>();
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Loop>>()) ||
Inv.invalidate<AnalysisB>(L, PA);
}));
::testing::InSequence MakeExpectationsSequenced;
// Compute the analyses across all of 'f' first.
EXPECT_CALL(MLAHandleA, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandleB, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandleA, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandleB, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandleA, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLAHandleB, run(HasName("loop.0"), _, _));
// Now we invalidate AnalysisB (but not AnalysisA) for one of the loops and
// preserve everything for the rest. This in turn triggers that one loop to
// recompute both AnalysisB *and* AnalysisA if indirect invalidation is
// working.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(InvokeWithoutArgs([] {
auto PA = getLoopPassPreservedAnalyses();
// Specifically preserve AnalysisA so that it would survive if it
// didn't depend on AnalysisB.
PA.preserve<AnalysisA>();
return PA;
}));
// It happens that AnalysisB is invalidated first. That shouldn't matter
// though, and we should still call AnalysisA's invalidation.
EXPECT_CALL(MLAHandleB, invalidate(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandleA, invalidate(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke([](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &) {
(void)AM.getResult<AnalysisA>(L, AR);
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLAHandleA, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandleB, run(HasName("loop.0.0"), _, _));
// The rest of the loops should run and get cached results.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke([](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &) {
(void)AM.getResult<AnalysisA>(L, AR);
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke([](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &) {
(void)AM.getResult<AnalysisA>(L, AR);
return PreservedAnalyses::all();
}));
// The run over 'g' should be boring, with us just computing the analyses once
// up front and then running loop passes and getting cached results.
EXPECT_CALL(MLAHandleA, run(HasName("loop.g.0"), _, _));
EXPECT_CALL(MLAHandleB, run(HasName("loop.g.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke([](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &) {
(void)AM.getResult<AnalysisA>(L, AR);
return PreservedAnalyses::all();
}));
// Build the pipeline and run it.
ModulePassManager MPM;
FunctionPassManager FPM;
FPM.addPass(
createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<AnalysisA>()));
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, IndirectOuterPassInvalidation) {
typedef decltype(MLAHandle)::Analysis LoopAnalysis;
MockFunctionAnalysisHandle MFAHandle;
FAM.registerPass([&] { return MFAHandle.getAnalysis(); });
typedef decltype(MFAHandle)::Analysis FunctionAnalysis;
// Set up the loop analysis to depend on both the function and module
// analysis.
ON_CALL(MLAHandle, run(_, _, _))
.WillByDefault(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR) {
auto &FAMP = AM.getResult<FunctionAnalysisManagerLoopProxy>(L, AR);
Function &F = *L.getHeader()->getParent();
// This call will assert when trying to get the actual analysis if the
// FunctionAnalysis can be invalidated. Only check its existence.
// Alternatively, use FAM above, for the purposes of this unittest.
if (FAMP.cachedResultExists<FunctionAnalysis>(F))
FAMP.registerOuterAnalysisInvalidation<FunctionAnalysis,
LoopAnalysis>();
return MLAHandle.getResult();
}));
::testing::InSequence MakeExpectationsSequenced;
// Compute the analyses across all of 'f' first.
EXPECT_CALL(MFPHandle, run(HasName("f"), _))
.WillOnce(Invoke([](Function &F, FunctionAnalysisManager &AM) {
// Force the computing of the function analysis so it is available in
// this function.
(void)AM.getResult<FunctionAnalysis>(F);
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
// Now invalidate the function analysis but preserve the loop analyses.
// This should trigger immediate invalidation of the loop analyses, despite
// the fact that they were preserved.
EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = getLoopPassPreservedAnalyses();
if (EnableMSSALoopDependency)
PA.preserve<MemorySSAAnalysis>();
PA.preserveSet<AllAnalysesOn<Loop>>();
return PA;
}));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, invalidate(HasName("loop.0"), _, _));
// And re-running a requires pass recomputes them.
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
// When we run over 'g' we don't populate the cache with the function
// analysis.
EXPECT_CALL(MFPHandle, run(HasName("g"), _))
.WillOnce(Return(PreservedAnalyses::all()));
EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _));
// Which means that no extra invalidation occurs and cached values are used.
EXPECT_CALL(MFPHandle, run(HasName("g"), _)).WillOnce(InvokeWithoutArgs([] {
auto PA = getLoopPassPreservedAnalyses();
if (EnableMSSALoopDependency)
PA.preserve<MemorySSAAnalysis>();
PA.preserveSet<AllAnalysesOn<Loop>>();
return PA;
}));
// Build the pipeline and run it.
ModulePassManager MPM;
FunctionPassManager FPM;
FPM.addPass(MFPHandle.getPass());
FPM.addPass(
createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<LoopAnalysis>()));
FPM.addPass(MFPHandle.getPass());
FPM.addPass(
createFunctionToLoopPassAdaptor(RequireAnalysisLoopPass<LoopAnalysis>()));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, LoopChildInsertion) {
// Super boring module with three loops in a single loop nest.
M = parseIR(Context, "define void @f(i1* %ptr) {\n"
"entry:\n"
" br label %loop.0\n"
"loop.0:\n"
" %cond.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0, label %loop.0.0.ph, label %end\n"
"loop.0.0.ph:\n"
" br label %loop.0.0\n"
"loop.0.0:\n"
" %cond.0.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n"
"loop.0.1.ph:\n"
" br label %loop.0.1\n"
"loop.0.1:\n"
" %cond.0.1 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n"
"loop.0.2.ph:\n"
" br label %loop.0.2\n"
"loop.0.2:\n"
" %cond.0.2 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n"
"loop.0.latch:\n"
" br label %loop.0\n"
"end:\n"
" ret void\n"
"}\n");
// Build up variables referring into the IR so we can rewrite it below
// easily.
Function &F = *M->begin();
ASSERT_THAT(F, HasName("f"));
Argument &Ptr = *F.arg_begin();
auto BBI = F.begin();
BasicBlock &EntryBB = *BBI++;
ASSERT_THAT(EntryBB, HasName("entry"));
BasicBlock &Loop0BB = *BBI++;
ASSERT_THAT(Loop0BB, HasName("loop.0"));
BasicBlock &Loop00PHBB = *BBI++;
ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph"));
BasicBlock &Loop00BB = *BBI++;
ASSERT_THAT(Loop00BB, HasName("loop.0.0"));
BasicBlock &Loop01PHBB = *BBI++;
ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph"));
BasicBlock &Loop01BB = *BBI++;
ASSERT_THAT(Loop01BB, HasName("loop.0.1"));
BasicBlock &Loop02PHBB = *BBI++;
ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph"));
BasicBlock &Loop02BB = *BBI++;
ASSERT_THAT(Loop02BB, HasName("loop.0.2"));
BasicBlock &Loop0LatchBB = *BBI++;
ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch"));
BasicBlock &EndBB = *BBI++;
ASSERT_THAT(EndBB, HasName("end"));
ASSERT_THAT(BBI, F.end());
auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB,
const char *Name, BasicBlock *BB) {
auto *Cond = new LoadInst(Type::getInt1Ty(Context), &Ptr, Name,
/*isVolatile*/ true, BB);
BranchInst::Create(TrueBB, FalseBB, Cond, BB);
};
// Build the pass managers and register our pipeline. We build a single loop
// pass pipeline consisting of three mock pass runs over each loop. After
// this we run both domtree and loop verification passes to make sure that
// the IR remained valid during our mutations.
ModulePassManager MPM;
FunctionPassManager FPM;
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
FPM.addPass(DominatorTreeVerifierPass());
FPM.addPass(LoopVerifierPass());
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
// All the visit orders are deterministic, so we use simple fully order
// expectations.
::testing::InSequence MakeExpectationsSequenced;
// We run loop passes three times over each of the loops.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
// When running over the middle loop, the second run inserts two new child
// loops, inserting them and itself into the worklist.
BasicBlock *NewLoop010BB, *NewLoop01LatchBB;
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &Updater) {
auto *NewLoop = AR.LI.AllocateLoop();
L.addChildLoop(NewLoop);
auto *NewLoop010PHBB =
BasicBlock::Create(Context, "loop.0.1.0.ph", &F, &Loop02PHBB);
NewLoop010BB =
BasicBlock::Create(Context, "loop.0.1.0", &F, &Loop02PHBB);
NewLoop01LatchBB =
BasicBlock::Create(Context, "loop.0.1.latch", &F, &Loop02PHBB);
Loop01BB.getTerminator()->replaceUsesOfWith(&Loop01BB, NewLoop010PHBB);
BranchInst::Create(NewLoop010BB, NewLoop010PHBB);
CreateCondBr(NewLoop01LatchBB, NewLoop010BB, "cond.0.1.0",
NewLoop010BB);
BranchInst::Create(&Loop01BB, NewLoop01LatchBB);
AR.DT.addNewBlock(NewLoop010PHBB, &Loop01BB);
AR.DT.addNewBlock(NewLoop010BB, NewLoop010PHBB);
AR.DT.addNewBlock(NewLoop01LatchBB, NewLoop010BB);
EXPECT_TRUE(AR.DT.verify());
L.addBasicBlockToLoop(NewLoop010PHBB, AR.LI);
NewLoop->addBasicBlockToLoop(NewLoop010BB, AR.LI);
L.addBasicBlockToLoop(NewLoop01LatchBB, AR.LI);
NewLoop->verifyLoop();
L.verifyLoop();
Updater.addChildLoops({NewLoop});
return PreservedAnalyses::all();
}));
// We should immediately drop down to fully visit the new inner loop.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// After visiting the inner loop, we should re-visit the second loop
// reflecting its new loop nest structure.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
// In the second run over the middle loop after we've visited the new child,
// we add another child to check that we can repeatedly add children, and add
// children to a loop that already has children.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &Updater) {
auto *NewLoop = AR.LI.AllocateLoop();
L.addChildLoop(NewLoop);
auto *NewLoop011PHBB = BasicBlock::Create(Context, "loop.0.1.1.ph", &F, NewLoop01LatchBB);
auto *NewLoop011BB = BasicBlock::Create(Context, "loop.0.1.1", &F, NewLoop01LatchBB);
NewLoop010BB->getTerminator()->replaceUsesOfWith(NewLoop01LatchBB,
NewLoop011PHBB);
BranchInst::Create(NewLoop011BB, NewLoop011PHBB);
CreateCondBr(NewLoop01LatchBB, NewLoop011BB, "cond.0.1.1",
NewLoop011BB);
AR.DT.addNewBlock(NewLoop011PHBB, NewLoop010BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop011BB, NewLoop011PHBB);
AR.DT.changeImmediateDominator(AR.DT[NewLoop01LatchBB], NewDTNode);
EXPECT_TRUE(AR.DT.verify());
L.addBasicBlockToLoop(NewLoop011PHBB, AR.LI);
NewLoop->addBasicBlockToLoop(NewLoop011BB, AR.LI);
NewLoop->verifyLoop();
L.verifyLoop();
Updater.addChildLoops({NewLoop});
return PreservedAnalyses::all();
}));
// Again, we should immediately drop down to visit the new, unvisited child
// loop. We don't need to revisit the other child though.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1.1"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1.1"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// And now we should pop back up to the second loop and do a full pipeline of
// three passes on its current form.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.Times(3)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.2"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// Now that all the expected actions are registered, run the pipeline over
// our module. All of our expectations are verified when the test finishes.
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, LoopPeerInsertion) {
// Super boring module with two loop nests and loop nest with two child
// loops.
M = parseIR(Context, "define void @f(i1* %ptr) {\n"
"entry:\n"
" br label %loop.0\n"
"loop.0:\n"
" %cond.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0, label %loop.0.0.ph, label %loop.2.ph\n"
"loop.0.0.ph:\n"
" br label %loop.0.0\n"
"loop.0.0:\n"
" %cond.0.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.0, label %loop.0.0, label %loop.0.2.ph\n"
"loop.0.2.ph:\n"
" br label %loop.0.2\n"
"loop.0.2:\n"
" %cond.0.2 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n"
"loop.0.latch:\n"
" br label %loop.0\n"
"loop.2.ph:\n"
" br label %loop.2\n"
"loop.2:\n"
" %cond.2 = load volatile i1, i1* %ptr\n"
" br i1 %cond.2, label %loop.2, label %end\n"
"end:\n"
" ret void\n"
"}\n");
// Build up variables referring into the IR so we can rewrite it below
// easily.
Function &F = *M->begin();
ASSERT_THAT(F, HasName("f"));
Argument &Ptr = *F.arg_begin();
auto BBI = F.begin();
BasicBlock &EntryBB = *BBI++;
ASSERT_THAT(EntryBB, HasName("entry"));
BasicBlock &Loop0BB = *BBI++;
ASSERT_THAT(Loop0BB, HasName("loop.0"));
BasicBlock &Loop00PHBB = *BBI++;
ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph"));
BasicBlock &Loop00BB = *BBI++;
ASSERT_THAT(Loop00BB, HasName("loop.0.0"));
BasicBlock &Loop02PHBB = *BBI++;
ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph"));
BasicBlock &Loop02BB = *BBI++;
ASSERT_THAT(Loop02BB, HasName("loop.0.2"));
BasicBlock &Loop0LatchBB = *BBI++;
ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch"));
BasicBlock &Loop2PHBB = *BBI++;
ASSERT_THAT(Loop2PHBB, HasName("loop.2.ph"));
BasicBlock &Loop2BB = *BBI++;
ASSERT_THAT(Loop2BB, HasName("loop.2"));
BasicBlock &EndBB = *BBI++;
ASSERT_THAT(EndBB, HasName("end"));
ASSERT_THAT(BBI, F.end());
auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB,
const char *Name, BasicBlock *BB) {
auto *Cond = new LoadInst(Type::getInt1Ty(Context), &Ptr, Name,
/*isVolatile*/ true, BB);
BranchInst::Create(TrueBB, FalseBB, Cond, BB);
};
// Build the pass managers and register our pipeline. We build a single loop
// pass pipeline consisting of three mock pass runs over each loop. After
// this we run both domtree and loop verification passes to make sure that
// the IR remained valid during our mutations.
ModulePassManager MPM;
FunctionPassManager FPM;
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
FPM.addPass(DominatorTreeVerifierPass());
FPM.addPass(LoopVerifierPass());
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
// All the visit orders are deterministic, so we use simple fully order
// expectations.
::testing::InSequence MakeExpectationsSequenced;
// We run loop passes three times over each of the loops.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
// On the second run, we insert a sibling loop.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &Updater) {
auto *NewLoop = AR.LI.AllocateLoop();
L.getParentLoop()->addChildLoop(NewLoop);
auto *NewLoop01PHBB = BasicBlock::Create(Context, "loop.0.1.ph", &F, &Loop02PHBB);
auto *NewLoop01BB = BasicBlock::Create(Context, "loop.0.1", &F, &Loop02PHBB);
BranchInst::Create(NewLoop01BB, NewLoop01PHBB);
CreateCondBr(&Loop02PHBB, NewLoop01BB, "cond.0.1", NewLoop01BB);
Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02PHBB, NewLoop01PHBB);
AR.DT.addNewBlock(NewLoop01PHBB, &Loop00BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, NewLoop01PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop02PHBB], NewDTNode);
EXPECT_TRUE(AR.DT.verify());
L.getParentLoop()->addBasicBlockToLoop(NewLoop01PHBB, AR.LI);
NewLoop->addBasicBlockToLoop(NewLoop01BB, AR.LI);
L.getParentLoop()->verifyLoop();
Updater.addSiblingLoops({NewLoop});
return PreservedAnalyses::all();
}));
// We finish processing this loop as sibling loops don't perturb the
// postorder walk.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
// We visit the inserted sibling next.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.2"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
// Next, on the third pass run on the last inner loop we add more new
// siblings, more than one, and one with nested child loops. By doing this at
// the end we make sure that edge case works well.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &Updater) {
Loop *NewLoops[] = {AR.LI.AllocateLoop(), AR.LI.AllocateLoop(),
AR.LI.AllocateLoop()};
L.getParentLoop()->addChildLoop(NewLoops[0]);
L.getParentLoop()->addChildLoop(NewLoops[1]);
NewLoops[1]->addChildLoop(NewLoops[2]);
auto *NewLoop03PHBB =
BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB);
auto *NewLoop03BB =
BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB);
auto *NewLoop04PHBB =
BasicBlock::Create(Context, "loop.0.4.ph", &F, &Loop0LatchBB);
auto *NewLoop04BB =
BasicBlock::Create(Context, "loop.0.4", &F, &Loop0LatchBB);
auto *NewLoop040PHBB =
BasicBlock::Create(Context, "loop.0.4.0.ph", &F, &Loop0LatchBB);
auto *NewLoop040BB =
BasicBlock::Create(Context, "loop.0.4.0", &F, &Loop0LatchBB);
auto *NewLoop04LatchBB =
BasicBlock::Create(Context, "loop.0.4.latch", &F, &Loop0LatchBB);
Loop02BB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB, NewLoop03PHBB);
BranchInst::Create(NewLoop03BB, NewLoop03PHBB);
CreateCondBr(NewLoop04PHBB, NewLoop03BB, "cond.0.3", NewLoop03BB);
BranchInst::Create(NewLoop04BB, NewLoop04PHBB);
CreateCondBr(&Loop0LatchBB, NewLoop040PHBB, "cond.0.4", NewLoop04BB);
BranchInst::Create(NewLoop040BB, NewLoop040PHBB);
CreateCondBr(NewLoop04LatchBB, NewLoop040BB, "cond.0.4.0", NewLoop040BB);
BranchInst::Create(NewLoop04BB, NewLoop04LatchBB);
AR.DT.addNewBlock(NewLoop03PHBB, &Loop02BB);
AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB);
AR.DT.addNewBlock(NewLoop04PHBB, NewLoop03BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop04BB, NewLoop04PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB], NewDTNode);
AR.DT.addNewBlock(NewLoop040PHBB, NewLoop04BB);
AR.DT.addNewBlock(NewLoop040BB, NewLoop040PHBB);
AR.DT.addNewBlock(NewLoop04LatchBB, NewLoop040BB);
EXPECT_TRUE(AR.DT.verify());
L.getParentLoop()->addBasicBlockToLoop(NewLoop03PHBB, AR.LI);
NewLoops[0]->addBasicBlockToLoop(NewLoop03BB, AR.LI);
L.getParentLoop()->addBasicBlockToLoop(NewLoop04PHBB, AR.LI);
NewLoops[1]->addBasicBlockToLoop(NewLoop04BB, AR.LI);
NewLoops[1]->addBasicBlockToLoop(NewLoop040PHBB, AR.LI);
NewLoops[2]->addBasicBlockToLoop(NewLoop040BB, AR.LI);
NewLoops[1]->addBasicBlockToLoop(NewLoop04LatchBB, AR.LI);
L.getParentLoop()->verifyLoop();
Updater.addSiblingLoops({NewLoops[0], NewLoops[1]});
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.3"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// Note that we need to visit the inner loop of this added sibling before the
// sibling itself!
EXPECT_CALL(MLPHandle, run(HasName("loop.0.4.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.4.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.4.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.4"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.4"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.4"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// And only now do we visit the outermost loop of the nest.
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
// On the second pass, we add sibling loops which become new top-level loops.
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &Updater) {
auto *NewLoop = AR.LI.AllocateLoop();
AR.LI.addTopLevelLoop(NewLoop);
auto *NewLoop1PHBB = BasicBlock::Create(Context, "loop.1.ph", &F, &Loop2BB);
auto *NewLoop1BB = BasicBlock::Create(Context, "loop.1", &F, &Loop2BB);
BranchInst::Create(NewLoop1BB, NewLoop1PHBB);
CreateCondBr(&Loop2PHBB, NewLoop1BB, "cond.1", NewLoop1BB);
Loop0BB.getTerminator()->replaceUsesOfWith(&Loop2PHBB, NewLoop1PHBB);
AR.DT.addNewBlock(NewLoop1PHBB, &Loop0BB);
auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, NewLoop1PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop2PHBB], NewDTNode);
EXPECT_TRUE(AR.DT.verify());
NewLoop->addBasicBlockToLoop(NewLoop1BB, AR.LI);
NewLoop->verifyLoop();
Updater.addSiblingLoops({NewLoop});
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.1"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.1"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.2"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.2"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.2"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// Now that all the expected actions are registered, run the pipeline over
// our module. All of our expectations are verified when the test finishes.
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, LoopDeletion) {
// Build a module with a single loop nest that contains one outer loop with
// three subloops, and one of those with its own subloop. We will
// incrementally delete all of these to test different deletion scenarios.
M = parseIR(Context, "define void @f(i1* %ptr) {\n"
"entry:\n"
" br label %loop.0\n"
"loop.0:\n"
" %cond.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0, label %loop.0.0.ph, label %end\n"
"loop.0.0.ph:\n"
" br label %loop.0.0\n"
"loop.0.0:\n"
" %cond.0.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n"
"loop.0.1.ph:\n"
" br label %loop.0.1\n"
"loop.0.1:\n"
" %cond.0.1 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n"
"loop.0.2.ph:\n"
" br label %loop.0.2\n"
"loop.0.2:\n"
" %cond.0.2 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.2, label %loop.0.2.0.ph, label %loop.0.latch\n"
"loop.0.2.0.ph:\n"
" br label %loop.0.2.0\n"
"loop.0.2.0:\n"
" %cond.0.2.0 = load volatile i1, i1* %ptr\n"
" br i1 %cond.0.2.0, label %loop.0.2.0, label %loop.0.2.latch\n"
"loop.0.2.latch:\n"
" br label %loop.0.2\n"
"loop.0.latch:\n"
" br label %loop.0\n"
"end:\n"
" ret void\n"
"}\n");
// Build up variables referring into the IR so we can rewrite it below
// easily.
Function &F = *M->begin();
ASSERT_THAT(F, HasName("f"));
Argument &Ptr = *F.arg_begin();
auto BBI = F.begin();
BasicBlock &EntryBB = *BBI++;
ASSERT_THAT(EntryBB, HasName("entry"));
BasicBlock &Loop0BB = *BBI++;
ASSERT_THAT(Loop0BB, HasName("loop.0"));
BasicBlock &Loop00PHBB = *BBI++;
ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph"));
BasicBlock &Loop00BB = *BBI++;
ASSERT_THAT(Loop00BB, HasName("loop.0.0"));
BasicBlock &Loop01PHBB = *BBI++;
ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph"));
BasicBlock &Loop01BB = *BBI++;
ASSERT_THAT(Loop01BB, HasName("loop.0.1"));
BasicBlock &Loop02PHBB = *BBI++;
ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph"));
BasicBlock &Loop02BB = *BBI++;
ASSERT_THAT(Loop02BB, HasName("loop.0.2"));
BasicBlock &Loop020PHBB = *BBI++;
ASSERT_THAT(Loop020PHBB, HasName("loop.0.2.0.ph"));
BasicBlock &Loop020BB = *BBI++;
ASSERT_THAT(Loop020BB, HasName("loop.0.2.0"));
BasicBlock &Loop02LatchBB = *BBI++;
ASSERT_THAT(Loop02LatchBB, HasName("loop.0.2.latch"));
BasicBlock &Loop0LatchBB = *BBI++;
ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch"));
BasicBlock &EndBB = *BBI++;
ASSERT_THAT(EndBB, HasName("end"));
ASSERT_THAT(BBI, F.end());
// Helper to do the actual deletion of a loop. We directly encode this here
// to isolate ourselves from the rest of LLVM and for simplicity. Here we can
// egregiously cheat based on knowledge of the test case. For example, we
// have no PHI nodes and there is always a single i-dom.
auto EraseLoop = [](Loop &L, BasicBlock &IDomBB,
LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {
assert(L.isInnermost() && "Can only delete leaf loops with this routine!");
SmallVector<BasicBlock *, 4> LoopBBs(L.block_begin(), L.block_end());
Updater.markLoopAsDeleted(L, L.getName());
IDomBB.getTerminator()->replaceUsesOfWith(L.getHeader(),
L.getUniqueExitBlock());
for (BasicBlock *LoopBB : LoopBBs) {
SmallVector<DomTreeNode *, 4> ChildNodes(AR.DT[LoopBB]->begin(),
AR.DT[LoopBB]->end());
for (DomTreeNode *ChildNode : ChildNodes)
AR.DT.changeImmediateDominator(ChildNode, AR.DT[&IDomBB]);
AR.DT.eraseNode(LoopBB);
AR.LI.removeBlock(LoopBB);
LoopBB->dropAllReferences();
}
for (BasicBlock *LoopBB : LoopBBs)
LoopBB->eraseFromParent();
AR.LI.erase(&L);
};
// Build up the pass managers.
ModulePassManager MPM;
FunctionPassManager FPM;
// We run several loop pass pipelines across the loop nest, but they all take
// the same form of three mock pass runs in a loop pipeline followed by
// domtree and loop verification. We use a lambda to stamp this out each
// time.
auto AddLoopPipelineAndVerificationPasses = [&] {
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
FPM.addPass(DominatorTreeVerifierPass());
FPM.addPass(LoopVerifierPass());
};
// All the visit orders are deterministic so we use simple fully order
// expectations.
::testing::InSequence MakeExpectationsSequenced;
// We run the loop pipeline with three passes over each of the loops. When
// running over the middle loop, the second pass in the pipeline deletes it.
// This should prevent the third pass from visiting it but otherwise leave
// the process unimpacted.
AddLoopPipelineAndVerificationPasses();
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.WillOnce(
Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {
Loop *ParentL = L.getParentLoop();
AR.SE.forgetLoop(&L);
EraseLoop(L, Loop01PHBB, AR, Updater);
ParentL->verifyLoop();
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.2.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.2"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// Run the loop pipeline again. This time we delete the last loop, which
// contains a nested loop within it and insert a new loop into the nest. This
// makes sure we can handle nested loop deletion.
AddLoopPipelineAndVerificationPasses();
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.Times(3)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2.0"), _, _, _))
.Times(3)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
BasicBlock *NewLoop03PHBB;
EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _))
.WillOnce(
Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {
AR.SE.forgetLoop(*L.begin());
EraseLoop(**L.begin(), Loop020PHBB, AR, Updater);
auto *ParentL = L.getParentLoop();
AR.SE.forgetLoop(&L);
EraseLoop(L, Loop02PHBB, AR, Updater);
// Now insert a new sibling loop.
auto *NewSibling = AR.LI.AllocateLoop();
ParentL->addChildLoop(NewSibling);
NewLoop03PHBB =
BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB);
auto *NewLoop03BB =
BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB);
BranchInst::Create(NewLoop03BB, NewLoop03PHBB);
auto *Cond =
new LoadInst(Type::getInt1Ty(Context), &Ptr, "cond.0.3",
/*isVolatile*/ true, NewLoop03BB);
BranchInst::Create(&Loop0LatchBB, NewLoop03BB, Cond, NewLoop03BB);
Loop02PHBB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB,
NewLoop03PHBB);
AR.DT.addNewBlock(NewLoop03PHBB, &Loop02PHBB);
AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB);
AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB],
AR.DT[NewLoop03BB]);
EXPECT_TRUE(AR.DT.verify());
ParentL->addBasicBlockToLoop(NewLoop03PHBB, AR.LI);
NewSibling->addBasicBlockToLoop(NewLoop03BB, AR.LI);
NewSibling->verifyLoop();
ParentL->verifyLoop();
Updater.addSiblingLoops({NewSibling});
return PreservedAnalyses::all();
}));
// To respect our inner-to-outer traversal order, we must visit the
// newly-inserted sibling of the loop we just deleted before we visit the
// outer loop. When we do so, this must compute a fresh analysis result, even
// though our new loop has the same pointer value as the loop we deleted.
EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLAHandle, run(HasName("loop.0.3"), _, _));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _))
.Times(2)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.Times(3)
.WillRepeatedly(Invoke(getLoopAnalysisResult));
// In the final loop pipeline run we delete every loop, including the last
// loop of the nest. We do this again in the second pass in the pipeline, and
// as a consequence we never make it to three runs on any loop. We also cover
// deleting multiple loops in a single pipeline, deleting the first loop and
// deleting the (last) top level loop.
AddLoopPipelineAndVerificationPasses();
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.WillOnce(
Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {
AR.SE.forgetLoop(&L);
EraseLoop(L, Loop00PHBB, AR, Updater);
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0.3"), _, _, _))
.WillOnce(
Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {
AR.SE.forgetLoop(&L);
EraseLoop(L, *NewLoop03PHBB, AR, Updater);
return PreservedAnalyses::all();
}));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(Invoke(getLoopAnalysisResult));
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _))
.WillOnce(
Invoke([&](Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR, LPMUpdater &Updater) {
AR.SE.forgetLoop(&L);
EraseLoop(L, EntryBB, AR, Updater);
return PreservedAnalyses::all();
}));
// Add the function pass pipeline now that it is fully built up and run it
// over the module's one function.
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(*M, MAM);
}
TEST_F(LoopPassManagerTest, HandleLoopNestPass) {
::testing::Sequence FSequence, GSequence;
EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _))
.Times(2)
.InSequence(FSequence);
EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _))
.Times(2)
.InSequence(FSequence);
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _))
.InSequence(FSequence);
EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _))
.InSequence(FSequence);
EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _))
.InSequence(GSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _))
.InSequence(GSequence);
EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _))
.InSequence(GSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _))
.InSequence(GSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _))
.InSequence(FSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _))
.InSequence(GSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _))
.InSequence(FSequence);
EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _))
.InSequence(GSequence);
ModulePassManager MPM;
FunctionPassManager FPM;
{
LoopPassManager LPM;
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLNPHandle.getPass());
LPM.addPass(MLPHandle.getPass());
LPM.addPass(MLNPHandle.getPass());
auto Adaptor = createFunctionToLoopPassAdaptor(std::move(LPM));
ASSERT_FALSE(Adaptor.isLoopNestMode());
FPM.addPass(std::move(Adaptor));
}
{
auto Adaptor = createFunctionToLoopPassAdaptor(MLNPHandle.getPass());
ASSERT_TRUE(Adaptor.isLoopNestMode());
FPM.addPass(std::move(Adaptor));
}
{
LoopPassManager LPM;
LPM.addPass(MLNPHandle.getPass());
auto Adaptor = createFunctionToLoopPassAdaptor(MLNPHandle.getPass());
ASSERT_TRUE(Adaptor.isLoopNestMode());
FPM.addPass(std::move(Adaptor));
}
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(*M, MAM);
}
} // namespace