//===--- LastRunTrackingAnalysisTest.cpp - LastRunTrackingAnalysis 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/Analysis/LastRunTrackingAnalysis.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Passes/PassBuilder.h" #include "gtest/gtest.h" namespace { using namespace llvm; class LastRunTrackingAnalysisTest : public testing::Test { protected: LLVMContext C; Module M; PassBuilder PB; LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModulePassManager MPM; ModuleAnalysisManager MAM; LastRunTrackingAnalysisTest() : M("LastRunTrackingAnalysisTest", C) { PB.registerModuleAnalyses(MAM); PB.registerCGSCCAnalyses(CGAM); PB.registerFunctionAnalyses(FAM); PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); } }; struct PassOption final { uint32_t Threshold; /// Assume that this pass doesn't make changes with threshold A if we already /// know it doesn't make changes with a larger threshold B. bool isCompatibleWith(const PassOption &LastOpt) const { return Threshold <= LastOpt.Threshold; } }; class ModuleNoopPass : public PassInfoMixin { uint32_t &ExecutedBitMap; uint32_t RunID; void *PassID; bool ShouldChange; std::optional Option; bool shouldSkip(LastRunTrackingInfo &LRT) { if (Option.has_value()) return LRT.shouldSkip(PassID, *Option); return LRT.shouldSkip(PassID); } void update(LastRunTrackingInfo &LRT) { if (Option.has_value()) return LRT.update(PassID, ShouldChange, *Option); return LRT.update(PassID, ShouldChange); } public: explicit ModuleNoopPass(uint32_t &ExecutedBitMapRef, uint32_t RunIDVal, void *PassIDVal, bool ShouldChangeVal, std::optional OptionVal = std::nullopt) : ExecutedBitMap(ExecutedBitMapRef), RunID(RunIDVal), PassID(PassIDVal), ShouldChange(ShouldChangeVal), Option(OptionVal) {} PreservedAnalyses run(Module &F, ModuleAnalysisManager &AM) { auto &LRT = AM.getResult(F); if (shouldSkip(LRT)) { EXPECT_FALSE(ShouldChange) << "This pass is incorrectly skipped."; return PreservedAnalyses::all(); } ExecutedBitMap |= 1U << RunID; update(LRT); PreservedAnalyses PA; PA.preserve(); return PA; } }; static char PassA, PassB; TEST_F(LastRunTrackingAnalysisTest, SkipTest) { uint32_t BitMap = 0; // Executed. This is first run of PassA. MPM.addPass(ModuleNoopPass(BitMap, 0, &PassA, true)); // Skipped since PassA has just been executed. MPM.addPass(ModuleNoopPass(BitMap, 1, &PassA, false)); // Skipped since PassA has just been executed. MPM.addPass(ModuleNoopPass(BitMap, 2, &PassA, false)); // Executed. This is first run of PassB. MPM.addPass(ModuleNoopPass(BitMap, 3, &PassB, false, PassOption{2})); // Skipped. PassB doesn't make changes with lower threshold. MPM.addPass(ModuleNoopPass(BitMap, 4, &PassB, false, PassOption{1})); // Executed. PassB may make changes with higher threshold. MPM.addPass(ModuleNoopPass(BitMap, 5, &PassB, false, PassOption{3})); // Skipped. We don't make changes since last run of PassA. MPM.addPass(ModuleNoopPass(BitMap, 6, &PassA, false)); // Executed. PassB may make changes with higher threshold. MPM.addPass(ModuleNoopPass(BitMap, 7, &PassB, true, PassOption{4})); // Executed. This module has been modified by PassB. MPM.addPass(ModuleNoopPass(BitMap, 8, &PassA, false)); MPM.run(M, MAM); ASSERT_EQ(BitMap, 0b110101001U); } } // namespace