llvm/llvm/unittests/IR/PassManagerTest.cpp

//===- llvm/unittest/IR/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/IR/PassManager.h"
#include "llvm/Analysis/AssumptionCache.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/PassManagerImpl.h"
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Scalar/SimplifyCFG.h"
#include "gtest/gtest.h"

usingnamespacellvm;

namespace {

class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> {};

AnalysisKey TestFunctionAnalysis::Key;

class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> {};

AnalysisKey TestModuleAnalysis::Key;

struct TestModulePass : PassInfoMixin<TestModulePass> {};

struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> {};

struct TestFunctionPass : PassInfoMixin<TestFunctionPass> {};

// A test function pass that invalidates all function analyses for a function
// with a specific name.
struct TestInvalidationFunctionPass
    : PassInfoMixin<TestInvalidationFunctionPass> {};

std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) {}

class PassManagerTest : public ::testing::Test {};

TEST(PreservedAnalysesTest, Basic) {}

TEST(PreservedAnalysesTest, Preserve) {}

TEST(PreservedAnalysesTest, PreserveSets) {}

TEST(PreservedAnalysisTest, Intersect) {}

TEST(PreservedAnalysisTest, Abandon) {}

TEST_F(PassManagerTest, Basic) {}

// A customized pass manager that passes extra arguments through the
// infrastructure.
CustomizedAnalysisManager;
CustomizedPassManager;

class CustomizedAnalysis : public AnalysisInfoMixin<CustomizedAnalysis> {};

AnalysisKey CustomizedAnalysis::Key;

struct CustomizedPass : PassInfoMixin<CustomizedPass> {};

TEST_F(PassManagerTest, CustomizedPassManagerArgs) {}

/// A test analysis pass which caches in its result another analysis pass and
/// uses it to serve queries. This requires the result to invalidate itself
/// when its dependency is invalidated.
struct TestIndirectFunctionAnalysis
    : public AnalysisInfoMixin<TestIndirectFunctionAnalysis> {};

AnalysisKey TestIndirectFunctionAnalysis::Key;

/// A test analysis pass which chaches in its result the result from the above
/// indirect analysis pass.
///
/// This allows us to ensure that whenever an analysis pass is invalidated due
/// to dependencies (especially dependencies across IR units that trigger
/// asynchronous invalidation) we correctly detect that this may in turn cause
/// other analysis to be invalidated.
struct TestDoublyIndirectFunctionAnalysis
    : public AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis> {};

AnalysisKey TestDoublyIndirectFunctionAnalysis::Key;

struct LambdaPass : public PassInfoMixin<LambdaPass> {};

TEST_F(PassManagerTest, IndirectAnalysisInvalidation) {}

// Run SimplifyCFGPass that makes CFG changes and reports PreservedAnalyses
// without CFGAnalyses. So the CFGChecker does not complain.
TEST_F(PassManagerTest, FunctionPassCFGChecker) {}

// FunctionPass that manually invalidates analyses and always returns
// PreservedAnalyses::all().
struct TestSimplifyCFGInvalidatingAnalysisPass
    : PassInfoMixin<TestSimplifyCFGInvalidatingAnalysisPass> {};

// Run TestSimplifyCFGInvalidatingAnalysisPass which changes CFG by running
// SimplifyCFGPass then manually invalidates analyses and always returns
// PreservedAnalyses::all(). CFGChecker does not complain because it resets
// its saved CFG snapshot when the analyses are invalidated manually.
TEST_F(PassManagerTest, FunctionPassCFGCheckerInvalidateAnalysis) {}

// Wrap a FunctionPassManager running SimplifyCFG pass with another
// FunctionPassManager.
struct TestSimplifyCFGWrapperPass : PassInfoMixin<TestSimplifyCFGWrapperPass> {};

// Run TestSimplifyCFGWrapperPass which simulates behavior of
// FunctionPassManager::run() except that it runs all passes at once by calling
// an inner pass manager's passes with PassManager::run(). This is how one pass
// manager is expected to wrap another pass manager.
// SimplifyCFGPass, which is called by the inner pass manager, changes the CFG.
// The CFGChecker's AfterPassCallback, run right after SimplifyCFGPass, does not
// complain because CFGAnalyses is not in the PreservedAnalises set returned by
// SimplifyCFGPass. Then the CFG analysis is invalidated by the analysis manager
// according to the PreservedAnalises set. Further calls to CFGChecker's
// AfterPassCallback see that all analyses for the current function are
// preserved but there is no CFG snapshot available (i.e.
// AM.getCachedResult<PreservedCFGCheckerAnalysis>(F) returns nullptr).
TEST_F(PassManagerTest, FunctionPassCFGCheckerWrapped) {}

#ifdef EXPENSIVE_CHECKS

struct WrongFunctionPass : PassInfoMixin<WrongFunctionPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
    F.getEntryBlock().begin()->eraseFromParent();
    return PreservedAnalyses::all();
  }
  static StringRef name() { return "WrongFunctionPass"; }
};

TEST_F(PassManagerTest, FunctionPassMissedFunctionAnalysisInvalidation) {
  LLVMContext Context;
  auto M = parseIR(Context, "define void @foo() {\n"
                            "  %a = add i32 0, 0\n"
                            "  ret void\n"
                            "}\n");

  FunctionAnalysisManager FAM;
  ModuleAnalysisManager MAM;
  PassInstrumentationCallbacks PIC;
  StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ false);
  SI.registerCallbacks(PIC, &MAM);
  MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
  MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
  FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
  FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });

  FunctionPassManager FPM;
  FPM.addPass(WrongFunctionPass());

  auto *F = M->getFunction("foo");
  EXPECT_DEATH(FPM.run(*F, FAM), "Function @foo changed by WrongFunctionPass without invalidating analyses");
}

struct WrongModulePass : PassInfoMixin<WrongModulePass> {
  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
    for (Function &F : M)
      F.getEntryBlock().begin()->eraseFromParent();

    PreservedAnalyses PA;
    PA.preserveSet<AllAnalysesOn<Function>>();
    PA.preserve<FunctionAnalysisManagerModuleProxy>();
    return PA;
  }
  static StringRef name() { return "WrongModulePass"; }
};

TEST_F(PassManagerTest, ModulePassMissedFunctionAnalysisInvalidation) {
  LLVMContext Context;
  auto M = parseIR(Context, "define void @foo() {\n"
                            "  %a = add i32 0, 0\n"
                            "  ret void\n"
                            "}\n");

  FunctionAnalysisManager FAM;
  ModuleAnalysisManager MAM;
  PassInstrumentationCallbacks PIC;
  StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ false);
  SI.registerCallbacks(PIC, &MAM);
  MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
  MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
  FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
  FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });

  ModulePassManager MPM;
  MPM.addPass(WrongModulePass());

  EXPECT_DEATH(
      MPM.run(*M, MAM),
      "Function @foo changed by WrongModulePass without invalidating analyses");
}

struct WrongModulePass2 : PassInfoMixin<WrongModulePass2> {
  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
    for (Function &F : M)
      F.getEntryBlock().begin()->eraseFromParent();

    PreservedAnalyses PA;
    PA.preserveSet<AllAnalysesOn<Module>>();
    PA.abandon<FunctionAnalysisManagerModuleProxy>();
    return PA;
  }
  static StringRef name() { return "WrongModulePass2"; }
};

TEST_F(PassManagerTest, ModulePassMissedModuleAnalysisInvalidation) {
  LLVMContext Context;
  auto M = parseIR(Context, "define void @foo() {\n"
                            "  %a = add i32 0, 0\n"
                            "  ret void\n"
                            "}\n");

  FunctionAnalysisManager FAM;
  ModuleAnalysisManager MAM;
  PassInstrumentationCallbacks PIC;
  StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ false);
  SI.registerCallbacks(PIC, &MAM);
  MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
  MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
  FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
  FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });

  ModulePassManager MPM;
  MPM.addPass(WrongModulePass2());

  EXPECT_DEATH(
      MPM.run(*M, MAM),
      "Module changed by WrongModulePass2 without invalidating analyses");
}

#endif
}