[llvm] 07af0e2 - Reapply "[InlineAdvisor] Allow loading advisors as plugins"

Mircea Trofin via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 17 10:35:31 PST 2022


Author: ibricchi
Date: 2022-12-17T10:35:14-08:00
New Revision: 07af0e2d3e8485ad6f84da1ad9851538b62d2942

URL: https://github.com/llvm/llvm-project/commit/07af0e2d3e8485ad6f84da1ad9851538b62d2942
DIFF: https://github.com/llvm/llvm-project/commit/07af0e2d3e8485ad6f84da1ad9851538b62d2942.diff

LOG: Reapply "[InlineAdvisor] Allow loading advisors as plugins"

This reverts commit 8d22a63e2c8b4931113ca9d1ee8b17f7ff453e81.

Fix was missing dependency.

Added: 
    llvm/unittests/Analysis/InlineAdvisorPlugin.cpp
    llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp

Modified: 
    llvm/include/llvm/Analysis/InlineAdvisor.h
    llvm/lib/Analysis/InlineAdvisor.cpp
    llvm/unittests/Analysis/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/InlineAdvisor.h b/llvm/include/llvm/Analysis/InlineAdvisor.h
index 861cf63098927..c67698777775b 100644
--- a/llvm/include/llvm/Analysis/InlineAdvisor.h
+++ b/llvm/include/llvm/Analysis/InlineAdvisor.h
@@ -26,7 +26,7 @@ class ImportedFunctionsInliningStatistics;
 class OptimizationRemarkEmitter;
 struct ReplayInlinerSettings;
 
-/// There are 3 scenarios we can use the InlineAdvisor:
+/// There are 4 scenarios we can use the InlineAdvisor:
 /// - Default - use manual heuristics.
 ///
 /// - Release mode, the expected mode for production, day to day deployments.
@@ -39,6 +39,8 @@ struct ReplayInlinerSettings;
 /// requires the full C Tensorflow API library, and evaluates models
 /// dynamically. This mode also permits generating training logs, for offline
 /// training.
+///
+/// - Dynamically load an advisor via a plugin (PluginInlineAdvisorAnalysis)
 enum class InliningAdvisorMode : int { Default, Release, Development };
 
 // Each entry represents an inline driver.
@@ -238,6 +240,79 @@ class DefaultInlineAdvisor : public InlineAdvisor {
   InlineParams Params;
 };
 
+/// Used for dynamically registering InlineAdvisors as plugins
+///
+/// An advisor plugin adds a new advisor at runtime by registering an instance
+/// of PluginInlineAdvisorAnalysis in the current ModuleAnalysisManager.
+/// For example, the following code dynamically registers a
+/// DefaultInlineAdvisor:
+///
+/// namespace {
+///
+/// InlineAdvisor *defaultAdvisorFactory(Module &M, FunctionAnalysisManager
+/// &FAM,
+///                                      InlineParams Params, InlineContext IC)
+///                                      {
+///   return new DefaultInlineAdvisor(M, FAM, Params, IC);
+/// }
+///
+/// struct DefaultDynamicAdvisor : PassInfoMixin<DefaultDynamicAdvisor> {
+///   PreservedAnalyses run(Module &, ModuleAnalysisManager &MAM) {
+///     PluginInlineAdvisorAnalysis PA(defaultAdvisorFactory);
+///     MAM.registerPass([&] { return PA; });
+///     return PreservedAnalyses::all();
+///   }
+/// };
+///
+/// } // namespace
+///
+/// extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
+/// llvmGetPassPluginInfo() {
+///   return {LLVM_PLUGIN_API_VERSION, "DynamicDefaultAdvisor",
+///   LLVM_VERSION_STRING,
+///           [](PassBuilder &PB) {
+///             PB.registerPipelineStartEPCallback(
+///                 [](ModulePassManager &MPM, OptimizationLevel Level) {
+///                   MPM.addPass(DefaultDynamicAdvisor());
+///                 });
+///           }};
+/// }
+///
+/// A plugin must implement an AdvisorFactory and register it with a
+/// PluginInlineAdvisorAnlysis to the provided ModuleanAlysisManager.
+///
+/// If such a plugin has been registered
+/// InlineAdvisorAnalysis::Result::tryCreate will return the dynamically loaded
+/// advisor.
+///
+class PluginInlineAdvisorAnalysis
+    : public AnalysisInfoMixin<PluginInlineAdvisorAnalysis> {
+public:
+  static AnalysisKey Key;
+  static bool HasBeenRegistered;
+
+  typedef InlineAdvisor *(*AdvisorFactory)(Module &M,
+                                           FunctionAnalysisManager &FAM,
+                                           InlineParams Params,
+                                           InlineContext IC);
+
+  PluginInlineAdvisorAnalysis(AdvisorFactory Factory) : Factory(Factory) {
+    HasBeenRegistered = true;
+    assert(Factory != nullptr &&
+           "The plugin advisor factory should not be a null pointer.");
+  }
+
+  struct Result {
+    AdvisorFactory Factory;
+  };
+
+  Result run(Module &M, ModuleAnalysisManager &MAM) { return {Factory}; }
+  Result getResult() { return {Factory}; }
+
+private:
+  AdvisorFactory Factory;
+};
+
 /// The InlineAdvisorAnalysis is a module pass because the InlineAdvisor
 /// needs to capture state right before inlining commences over a module.
 class InlineAdvisorAnalysis : public AnalysisInfoMixin<InlineAdvisorAnalysis> {

diff  --git a/llvm/lib/Analysis/InlineAdvisor.cpp b/llvm/lib/Analysis/InlineAdvisor.cpp
index cb21f062f9f4d..540aad7ee0c0c 100644
--- a/llvm/lib/Analysis/InlineAdvisor.cpp
+++ b/llvm/lib/Analysis/InlineAdvisor.cpp
@@ -196,11 +196,18 @@ void InlineAdvice::recordInliningWithCalleeDeleted() {
 }
 
 AnalysisKey InlineAdvisorAnalysis::Key;
+AnalysisKey PluginInlineAdvisorAnalysis::Key;
+bool PluginInlineAdvisorAnalysis::HasBeenRegistered = false;
 
 bool InlineAdvisorAnalysis::Result::tryCreate(
     InlineParams Params, InliningAdvisorMode Mode,
     const ReplayInlinerSettings &ReplaySettings, InlineContext IC) {
   auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+  if (PluginInlineAdvisorAnalysis::HasBeenRegistered) {
+    auto &DA = MAM.getResult<PluginInlineAdvisorAnalysis>(M);
+    Advisor.reset(DA.Factory(M, FAM, Params, IC));
+    return !!Advisor;
+  }
   switch (Mode) {
   case InliningAdvisorMode::Default:
     LLVM_DEBUG(dbgs() << "Using default inliner heuristic.\n");

diff  --git a/llvm/unittests/Analysis/CMakeLists.txt b/llvm/unittests/Analysis/CMakeLists.txt
index d93f249962f8b..7ed557613a094 100644
--- a/llvm/unittests/Analysis/CMakeLists.txt
+++ b/llvm/unittests/Analysis/CMakeLists.txt
@@ -2,18 +2,12 @@ set(LLVM_LINK_COMPONENTS
   Analysis
   AsmParser
   Core
+  Passes
   Support
   TransformUtils
   )
 
-set(MLGO_TESTS TFUtilsTest.cpp TrainingLoggerTest.cpp)
-if (LLVM_HAVE_TFLITE)
-  LIST(APPEND EXTRA_TESTS ${MLGO_TESTS})
-else()
-  LIST(APPEND LLVM_OPTIONAL_SOURCES ${MLGO_TESTS})
-endif()
-
-add_llvm_unittest_with_input_files(AnalysisTests
+set(ANALYSIS_TEST_SOURCES
   AliasAnalysisTest.cpp
   AliasSetTrackerTest.cpp
   AssumeBundleQueriesTest.cpp
@@ -42,6 +36,7 @@ add_llvm_unittest_with_input_files(AnalysisTests
   MemorySSATest.cpp
   MLModelRunnerTest.cpp
   PhiValuesTest.cpp
+  PluginInlineAdvisorAnalysisTest.cpp
   ProfileSummaryInfoTest.cpp
   ScalarEvolutionTest.cpp
   VectorFunctionABITest.cpp
@@ -53,7 +48,55 @@ add_llvm_unittest_with_input_files(AnalysisTests
   ValueLatticeTest.cpp
   ValueTrackingTest.cpp
   VectorUtilsTest.cpp
-  ${EXTRA_TESTS}
   )
 
- target_link_libraries(AnalysisTests PRIVATE LLVMTestingSupport)
+# The unit tests target does not use InlineAdvisorPlugin.cpp
+# so if not added to LLVM_OPTIONAL_SOURCES, FileCheck will
+# complain about unused file.
+set(LLVM_OPTIONAL_SOURCES InlineAdvisorPlugin.cpp)
+
+set(MLGO_TESTS TFUtilsTest.cpp TrainingLoggerTest.cpp)
+if (LLVM_HAVE_TFLITE)
+  LIST(APPEND ANALYSIS_TEST_SOURCES ${MLGO_TESTS})
+else()
+  LIST(APPEND LLVM_OPTIONAL_SOURCES ${MLGO_TESTS})
+endif()
+
+add_llvm_unittest_with_input_files(AnalysisTests
+  ${ANALYSIS_TEST_SOURCES}
+  )
+
+target_link_libraries(AnalysisTests PRIVATE LLVMTestingSupport)
+
+# The advisor plugin expects to not link against the Analysis, Support and Core 
+# libraries, but expects them to exist in the process loading the plugin. This 
+# doesn't work with DLLs on Windows (where a shared library can't have undefined
+# references), so just skip this testcase on Windows.
+if (NOT WIN32)
+  # On AIX, enable run-time linking to allow symbols from the plugins shared
+  # objects to be properly bound.
+  if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-brtl")
+  endif()
+
+  # This plugin is built as a stand-alone plugin, so since we don't use the
+  # ANALYSIS_TEST_SOURCES files, we have to add them to LLVM_OPTIONAL_SOURCES
+  # so that FileCheck doesn't complain about unsued files.
+  LIST(APPEND LLVM_OPTIONAL_SOURCES ${ANALYSIS_TEST_SOURCES})
+
+  unset(LLVM_LINK_COMPONENTS)
+  add_llvm_library(InlineAdvisorPlugin MODULE BUILDTREE_ONLY InlineAdvisorPlugin.cpp)
+  # Put PLUGIN next to the unit test executable.
+  set_output_directory(InlineAdvisorPlugin
+      BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
+      LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
+      )
+  set_target_properties(InlineAdvisorPlugin PROPERTIES FOLDER "Tests")
+
+  export_executable_symbols_for_plugins(AnalysisTests)
+  # The plugin depends on some of the output files of llvm-tblgen, so make sure
+  # it is built before the plugin.
+  add_dependencies(InlineAdvisorPlugin llvm-tblgen)  
+  add_dependencies(AnalysisTests InlineAdvisorPlugin)
+  set_property(TARGET InlineAdvisorPlugin PROPERTY FOLDER "Tests/UnitTests/AnalysisTests")
+endif()

diff  --git a/llvm/unittests/Analysis/InlineAdvisorPlugin.cpp b/llvm/unittests/Analysis/InlineAdvisorPlugin.cpp
new file mode 100644
index 0000000000000..6431fda86c9dc
--- /dev/null
+++ b/llvm/unittests/Analysis/InlineAdvisorPlugin.cpp
@@ -0,0 +1,53 @@
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "llvm/Analysis/InlineAdvisor.h"
+
+using namespace llvm;
+
+namespace {
+
+InlineAdvisor *DefaultAdvisorFactory(Module &M, FunctionAnalysisManager &FAM,
+                                     InlineParams Params, InlineContext IC) {
+  return new DefaultInlineAdvisor(M, FAM, Params, IC);
+}
+
+struct DefaultDynamicAdvisor : PassInfoMixin<DefaultDynamicAdvisor> {
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
+    PluginInlineAdvisorAnalysis DA(DefaultAdvisorFactory);
+    MAM.registerPass([&] { return DA; });
+    return PreservedAnalyses::all();
+  }
+};
+
+} // namespace
+
+/* New PM Registration */
+llvm::PassPluginLibraryInfo getDefaultDynamicAdvisorPluginInfo() {
+  return {LLVM_PLUGIN_API_VERSION, "DynamicDefaultAdvisor", LLVM_VERSION_STRING,
+          [](PassBuilder &PB) {
+            PB.registerPipelineStartEPCallback(
+                [](ModulePassManager &MPM, OptimizationLevel Level) {
+                  MPM.addPass(DefaultDynamicAdvisor());
+                });
+            PB.registerPipelineParsingCallback(
+                [](StringRef Name, ModulePassManager &PM,
+                   ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
+                  if (Name == "dynamic-inline-advisor") {
+                    PM.addPass(DefaultDynamicAdvisor());
+                    return true;
+                  }
+                  return false;
+                });
+          }};
+}
+
+extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
+llvmGetPassPluginInfo() {
+  return getDefaultDynamicAdvisorPluginInfo();
+}

diff  --git a/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp b/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp
new file mode 100644
index 0000000000000..7f295f90e9613
--- /dev/null
+++ b/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp
@@ -0,0 +1,298 @@
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Config/config.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+
+void anchor() {}
+
+static std::string libPath(const std::string Name = "InlineAdvisorPlugin") {
+  const auto &Argvs = testing::internal::GetArgvs();
+  const char *Argv0 =
+      Argvs.size() > 0 ? Argvs[0].c_str() : "PluginInlineAdvisorAnalysisTest";
+  void *Ptr = (void *)(intptr_t)anchor;
+  std::string Path = sys::fs::getMainExecutable(Argv0, Ptr);
+  llvm::SmallString<256> Buf{sys::path::parent_path(Path)};
+  sys::path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str());
+  return std::string(Buf.str());
+}
+
+// Example of a custom InlineAdvisor that only inlines calls to functions called
+// "foo".
+class FooOnlyInlineAdvisor : public InlineAdvisor {
+public:
+  FooOnlyInlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
+                       InlineParams Params, InlineContext IC)
+      : InlineAdvisor(M, FAM, IC) {}
+
+  std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override {
+    if (CB.getCalledFunction()->getName() == "foo")
+      return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), true);
+    return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), false);
+  }
+};
+
+static InlineAdvisor *fooOnlyFactory(Module &M, FunctionAnalysisManager &FAM,
+                                     InlineParams Params, InlineContext IC) {
+  return new FooOnlyInlineAdvisor(M, FAM, Params, IC);
+}
+
+struct CompilerInstance {
+  LLVMContext Ctx;
+  ModulePassManager MPM;
+  InlineParams IP;
+
+  PassBuilder PB;
+  LoopAnalysisManager LAM;
+  FunctionAnalysisManager FAM;
+  CGSCCAnalysisManager CGAM;
+  ModuleAnalysisManager MAM;
+
+  SMDiagnostic Error;
+
+  // connect the plugin to our compiler instance
+  void setupPlugin() {
+    auto PluginPath = libPath();
+    ASSERT_NE("", PluginPath);
+    Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath);
+    ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath;
+    Plugin->registerPassBuilderCallbacks(PB);
+    ASSERT_THAT_ERROR(PB.parsePassPipeline(MPM, "dynamic-inline-advisor"),
+                      Succeeded());
+  }
+
+  // connect the FooOnlyInlineAdvisor to our compiler instance
+  void setupFooOnly() {
+    MAM.registerPass(
+        [&] { return PluginInlineAdvisorAnalysis(fooOnlyFactory); });
+  }
+
+  CompilerInstance() {
+    IP = getInlineParams(3, 0);
+    PB.registerModuleAnalyses(MAM);
+    PB.registerCGSCCAnalyses(CGAM);
+    PB.registerFunctionAnalyses(FAM);
+    PB.registerLoopAnalyses(LAM);
+    PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+    MPM.addPass(ModuleInlinerPass(IP, InliningAdvisorMode::Default,
+                                  ThinOrFullLTOPhase::None));
+  }
+
+  std::string output;
+  std::unique_ptr<Module> outputM;
+
+  // run with the default inliner
+  auto run_default(StringRef IR) {
+    PluginInlineAdvisorAnalysis::HasBeenRegistered = false;
+    outputM = parseAssemblyString(IR, Error, Ctx);
+    MPM.run(*outputM, MAM);
+    ASSERT_TRUE(outputM);
+    output.clear();
+    raw_string_ostream o_stream{output};
+    outputM->print(o_stream, nullptr);
+    ASSERT_TRUE(true);
+  }
+
+  // run with the dnamic inliner
+  auto run_dynamic(StringRef IR) {
+    // note typically the constructor for the DynamicInlineAdvisorAnalysis
+    // will automatically set this to true, we controll it here only to
+    // altenate between the default and dynamic inliner in our test
+    PluginInlineAdvisorAnalysis::HasBeenRegistered = true;
+    outputM = parseAssemblyString(IR, Error, Ctx);
+    MPM.run(*outputM, MAM);
+    ASSERT_TRUE(outputM);
+    output.clear();
+    raw_string_ostream o_stream{output};
+    outputM->print(o_stream, nullptr);
+    ASSERT_TRUE(true);
+  }
+};
+
+StringRef TestIRS[] = {
+    // Simple 3 function inline case
+    R"(
+define void @f1() {
+  call void @foo()
+  ret void
+}
+define void @foo() {
+  call void @f3()
+  ret void
+}
+define void @f3() {
+  ret void
+}
+  )",
+    // Test that has 5 functions of which 2 are recursive
+    R"(
+define void @f1() {
+  call void @foo()
+  ret void
+}
+define void @f2() {
+  call void @foo()
+  ret void
+}
+define void @foo() {
+  call void @f4()
+  call void @f5()
+  ret void
+}
+define void @f4() {
+  ret void
+}
+define void @f5() {
+  call void @foo()
+  ret void
+}
+  )",
+    // test with 2 mutually recursive functions and 1 function with a loop
+    R"(
+define void @f1() {
+  call void @f2()
+  ret void
+}
+define void @f2() {
+  call void @f3()
+  ret void
+}
+define void @f3() {
+  call void @f1()
+  ret void
+}
+define void @f4() {
+  br label %loop
+loop:
+  call void @f5()
+  br label %loop
+}
+define void @f5() {
+  ret void
+}
+  )",
+    // test that has a function that computes fibonacci in a loop, one in a
+    // recurisve manner, and one that calls both and compares them
+    R"(
+define i32 @fib_loop(i32 %n){
+    %curr = alloca i32
+    %last = alloca i32
+    %i = alloca i32
+    store i32 1, i32* %curr
+    store i32 1, i32* %last
+    store i32 2, i32* %i
+    br label %loop_cond
+  loop_cond:
+    %i_val = load i32, i32* %i
+    %cmp = icmp slt i32 %i_val, %n
+    br i1 %cmp, label %loop_body, label %loop_end
+  loop_body:
+    %curr_val = load i32, i32* %curr
+    %last_val = load i32, i32* %last
+    %add = add i32 %curr_val, %last_val
+    store i32 %add, i32* %last
+    store i32 %curr_val, i32* %curr
+    %i_val2 = load i32, i32* %i
+    %add2 = add i32 %i_val2, 1
+    store i32 %add2, i32* %i
+    br label %loop_cond
+  loop_end:
+    %curr_val3 = load i32, i32* %curr
+    ret i32 %curr_val3
+}
+
+define i32 @fib_rec(i32 %n){
+    %cmp = icmp eq i32 %n, 0
+    %cmp2 = icmp eq i32 %n, 1
+    %or = or i1 %cmp, %cmp2
+    br i1 %or, label %if_true, label %if_false
+  if_true:
+    ret i32 1
+  if_false:
+    %sub = sub i32 %n, 1
+    %call = call i32 @fib_rec(i32 %sub)
+    %sub2 = sub i32 %n, 2
+    %call2 = call i32 @fib_rec(i32 %sub2)
+    %add = add i32 %call, %call2
+    ret i32 %add
+}
+
+define i32 @fib_check(){
+    %correct = alloca i32
+    %i = alloca i32
+    store i32 1, i32* %correct
+    store i32 0, i32* %i
+    br label %loop_cond
+  loop_cond:
+    %i_val = load i32, i32* %i
+    %cmp = icmp slt i32 %i_val, 10
+    br i1 %cmp, label %loop_body, label %loop_end
+  loop_body:
+    %i_val2 = load i32, i32* %i
+    %call = call i32 @fib_loop(i32 %i_val2)
+    %i_val3 = load i32, i32* %i
+    %call2 = call i32 @fib_rec(i32 %i_val3)
+    %cmp2 = icmp ne i32 %call, %call2
+    br i1 %cmp2, label %if_true, label %if_false
+  if_true:
+    store i32 0, i32* %correct
+    br label %if_end
+  if_false:
+    br label %if_end
+  if_end:
+    %i_val4 = load i32, i32* %i
+    %add = add i32 %i_val4, 1
+    store i32 %add, i32* %i
+    br label %loop_cond
+  loop_end:
+    %correct_val = load i32, i32* %correct
+    ret i32 %correct_val
+}
+  )"};
+
+// check that loading a plugin works
+// the plugin being loaded acts identically to the default inliner
+TEST(PluginInlineAdvisorTest, PluginLoad) {
+#if !defined(LLVM_ENABLE_PLUGINS)
+  // Disable the test if plugins are disabled.
+  return;
+#endif
+  CompilerInstance CI{};
+  CI.setupPlugin();
+
+  for (StringRef IR : TestIRS) {
+    CI.run_default(IR);
+    std::string default_output = CI.output;
+    CI.run_dynamic(IR);
+    std::string dynamic_output = CI.output;
+    ASSERT_EQ(default_output, dynamic_output);
+  }
+}
+
+// check that the behaviour of a custom inliner is correct
+// the custom inliner inlines all functions that are not named "foo"
+// this testdoes not require plugins to be enabled
+TEST(PluginInlineAdvisorTest, CustomAdvisor) {
+  CompilerInstance CI{};
+  CI.setupFooOnly();
+
+  for (StringRef IR : TestIRS) {
+    CI.run_dynamic(IR);
+    CallGraph CGraph = CallGraph(*CI.outputM);
+    for (auto &node : CGraph) {
+      for (auto &edge : *node.second) {
+        if (!edge.first)
+          continue;
+        ASSERT_NE(edge.second->getFunction()->getName(), "foo");
+      }
+    }
+  }
+}
+
+} // namespace llvm


        


More information about the llvm-commits mailing list