Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PGO] Preserve analysis results when nothing was instrumented #93421

Merged

Conversation

samolisov
Copy link
Contributor

@samolisov samolisov commented May 26, 2024

The PGOInstrumentationGen pass should preserve all analysis results
when nothing was actually instrumented. Currently, only modules that
contain at least a single function definition are instrumented. When a
module contains only function declarations and, optionally, global
variable definitions (a module for the regular-LTO phase for thin-LTO
when LTOUnit splitting is enabled, for example), such module is not
instrumented (yet?) and there is no reason to invalidate any analysis
results.

NFC.

@llvmbot llvmbot added PGO Profile Guided Optimizations llvm:transforms labels May 26, 2024
@llvmbot
Copy link
Member

llvmbot commented May 26, 2024

@llvm/pr-subscribers-pgo

@llvm/pr-subscribers-llvm-transforms

Author: Pavel Samolysov (samolisov)

Changes

It makes sense to instrument modules that contain at least a single function definition only. When a module contains only function declarations and, optionally, global variable definitions (a module for the regular-LTO phase for thin-LTO when LTOUnit splitting is enabled, for example), it makes no sense to instrument such module and moreover, to introduce the __llvm_profile_raw_version variable into the module.

The patch also introduces the gmock-based unittest infrastructure for PGO Instrumentation and adds some test cases to check whether the instrumentation has taken place. The testing infrastructure for analysis modules was borrowed from the LoopPassManagerTest unittest and simplified a bit to handle module analysis passes only. Actually, we are testing whether the result of a trivial analysis pass was invalidated by the PGOInstrumentGen one: we exploit the fact the pass invalidates all the analysis results after a module was instrumented.


Full diff: https://github.com/llvm/llvm-project/pull/93421.diff

7 Files Affected:

  • (modified) llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp (+18-7)
  • (added) llvm/test/Transforms/PGOProfile/declarations_only.ll (+7)
  • (added) llvm/test/Transforms/PGOProfile/global_variables_only.ll (+5)
  • (added) llvm/test/Transforms/PGOProfile/no_global_variables.ll (+15)
  • (modified) llvm/unittests/Transforms/CMakeLists.txt (+1)
  • (added) llvm/unittests/Transforms/Instrumentation/CMakeLists.txt (+16)
  • (added) llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp (+207)
diff --git a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
index 2269c2e0fffae..ff94fc98d0117 100644
--- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
+++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
@@ -405,9 +405,14 @@ static GlobalVariable *createIRLevelProfileFlagVar(Module &M, bool IsCS) {
     ProfileVersion |= VARIANT_MASK_BYTE_COVERAGE;
   if (PGOTemporalInstrumentation)
     ProfileVersion |= VARIANT_MASK_TEMPORAL_PROF;
-  auto IRLevelVersionVariable = new GlobalVariable(
+  assert(!M.global_empty() &&
+         "If a module was instrumented, there must be defined global variables "
+         "at least for the counters.");
+  auto *InsertionPoint = &*M.global_begin();
+  auto *IRLevelVersionVariable = new GlobalVariable(
       M, IntTy64, true, GlobalValue::WeakAnyLinkage,
-      Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName);
+      Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName,
+      InsertionPoint);
   IRLevelVersionVariable->setVisibility(GlobalValue::HiddenVisibility);
   Triple TT(M.getTargetTriple());
   if (TT.supportsCOMDAT()) {
@@ -1829,11 +1834,6 @@ static bool InstrumentAllFunctions(
     Module &M, function_ref<TargetLibraryInfo &(Function &)> LookupTLI,
     function_ref<BranchProbabilityInfo *(Function &)> LookupBPI,
     function_ref<BlockFrequencyInfo *(Function &)> LookupBFI, bool IsCS) {
-  // For the context-sensitve instrumentation, we should have a separated pass
-  // (before LTO/ThinLTO linking) to create these variables.
-  if (!IsCS && !PGOCtxProfLoweringPass::isContextualIRPGOEnabled())
-    createIRLevelProfileFlagVar(M, /*IsCS=*/false);
-
   Triple TT(M.getTargetTriple());
   LLVMContext &Ctx = M.getContext();
   if (!TT.isOSBinFormatELF() && EnableVTableValueProfiling)
@@ -1845,6 +1845,7 @@ static bool InstrumentAllFunctions(
   std::unordered_multimap<Comdat *, GlobalValue *> ComdatMembers;
   collectComdatMembers(M, ComdatMembers);
 
+  bool AnythingInstrumented = false;
   for (auto &F : M) {
     if (skipPGOGen(F))
       continue;
@@ -1852,7 +1853,17 @@ static bool InstrumentAllFunctions(
     auto *BPI = LookupBPI(F);
     auto *BFI = LookupBFI(F);
     instrumentOneFunc(F, &M, TLI, BPI, BFI, ComdatMembers, IsCS);
+    AnythingInstrumented = true;
   }
+
+  if (!AnythingInstrumented)
+    return false;
+
+  // For the context-sensitve instrumentation, we should have a separated pass
+  // (before LTO/ThinLTO linking) to create these variables.
+  if (!IsCS && !PGOCtxProfLoweringPass::isContextualIRPGOEnabled())
+    createIRLevelProfileFlagVar(M, /*IsCS=*/false);
+
   return true;
 }
 
diff --git a/llvm/test/Transforms/PGOProfile/declarations_only.ll b/llvm/test/Transforms/PGOProfile/declarations_only.ll
new file mode 100644
index 0000000000000..6d9c86cb8f7a7
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/declarations_only.ll
@@ -0,0 +1,7 @@
+; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --implicit-check-not='__llvm_profile_raw_version'
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare i32 @test_1(i32 %i);
+
+declare i32 @test_2(i32 %i);
diff --git a/llvm/test/Transforms/PGOProfile/global_variables_only.ll b/llvm/test/Transforms/PGOProfile/global_variables_only.ll
new file mode 100644
index 0000000000000..c4b298a27bce5
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/global_variables_only.ll
@@ -0,0 +1,5 @@
+; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --implicit-check-not='__llvm_profile_raw_version'
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@var = internal unnamed_addr global [35 x ptr] zeroinitializer, align 16
diff --git a/llvm/test/Transforms/PGOProfile/no_global_variables.ll b/llvm/test/Transforms/PGOProfile/no_global_variables.ll
new file mode 100644
index 0000000000000..0faa2f35274c9
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/no_global_variables.ll
@@ -0,0 +1,15 @@
+; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN-COMDAT
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+; GEN-COMDAT: $__llvm_profile_raw_version = comdat any
+; GEN-COMDAT: @__llvm_profile_raw_version = hidden constant i64 {{[0-9]+}}, comdat
+
+define i32 @test_1(i32 %i) {
+entry:
+  ret i32 0
+}
+
+define i32 @test_2(i32 %i) {
+entry:
+  ret i32 1
+}
diff --git a/llvm/unittests/Transforms/CMakeLists.txt b/llvm/unittests/Transforms/CMakeLists.txt
index 98c821acde3a5..320cdf5674149 100644
--- a/llvm/unittests/Transforms/CMakeLists.txt
+++ b/llvm/unittests/Transforms/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_subdirectory(Coroutines)
+add_subdirectory(Instrumentation)
 add_subdirectory(IPO)
 add_subdirectory(Scalar)
 add_subdirectory(Utils)
diff --git a/llvm/unittests/Transforms/Instrumentation/CMakeLists.txt b/llvm/unittests/Transforms/Instrumentation/CMakeLists.txt
new file mode 100644
index 0000000000000..1f249b0049d06
--- /dev/null
+++ b/llvm/unittests/Transforms/Instrumentation/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Analysis
+  AsmParser
+  Core
+  Instrumentation
+  Passes
+  Support
+)
+
+add_llvm_unittest(InstrumentationTests
+  PGOInstrumentationTest.cpp
+  )
+
+target_link_libraries(InstrumentationTests PRIVATE LLVMTestingSupport)
+
+set_property(TARGET InstrumentationTests PROPERTY FOLDER "Tests/UnitTests/TransformTests")
diff --git a/llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp b/llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp
new file mode 100644
index 0000000000000..670a869ea716b
--- /dev/null
+++ b/llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp
@@ -0,0 +1,207 @@
+//===- PGOInstrumentationTest.cpp - Instrumentation unit 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/Instrumentation/PGOInstrumentation.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/ProfileData/InstrProf.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace llvm;
+
+using ::testing::DoDefault;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::IsNull;
+using ::testing::Ref;
+using ::testing::Return;
+using ::testing::Sequence;
+using ::testing::Test;
+using testing::_;
+
+template <typename Derived> class MockAnalysisHandleBase {
+public:
+  class Analysis : public AnalysisInfoMixin<Analysis> {
+  public:
+    class Result {
+    public:
+      // Forward invalidation events to the mock handle.
+      bool invalidate(Module &M, const PreservedAnalyses &PA,
+                      ModuleAnalysisManager::Invalidator &Inv) {
+        return Handle->invalidate(M, PA, Inv);
+      }
+    private:
+      explicit Result(Derived *Handle) : Handle(Handle) {}
+
+      friend MockAnalysisHandleBase;
+      Derived *Handle;
+    };
+
+    Result run(Module &M, ModuleAnalysisManager &AM) {
+      return Handle->run(M, AM);
+    }
+
+  private:
+    friend AnalysisInfoMixin<Analysis>;
+    friend MockAnalysisHandleBase;
+    static inline AnalysisKey Key;
+
+    Derived *Handle;
+
+    explicit Analysis(Derived *Handle) : Handle(Handle) {}
+  };
+
+  Analysis getAnalysis() {
+    return Analysis(static_cast<Derived *>(this));
+  }
+
+  typename Analysis::Result getResult() {
+    return typename Analysis::Result(static_cast<Derived *>(this));
+  }
+
+protected:
+  void setDefaults() {
+    ON_CALL(static_cast<Derived &>(*this), run(_, _))
+        .WillByDefault(Return(this->getResult()));
+    ON_CALL(static_cast<Derived &>(*this), invalidate(_, _, _))
+        .WillByDefault(Invoke([](Module &M, const PreservedAnalyses &PA,
+                                 ModuleAnalysisManager::Invalidator &) {
+          auto PAC = PA.template getChecker<Analysis>();
+          return !PAC.preserved() &&
+                 !PAC.template preservedSet<AllAnalysesOn<Module>>();
+        }));
+  }
+
+private:
+  friend Derived;
+  MockAnalysisHandleBase() = default;
+};
+
+class MockModuleAnalysisHandle
+    : public MockAnalysisHandleBase<MockModuleAnalysisHandle> {
+public:
+  MockModuleAnalysisHandle() { setDefaults(); }
+
+  MOCK_METHOD(typename Analysis::Result, run,
+              (Module &, ModuleAnalysisManager &));
+
+  MOCK_METHOD(bool, invalidate,
+              (Module &, const PreservedAnalyses &,
+               ModuleAnalysisManager::Invalidator &));
+};
+
+struct PGOInstrumentationGenTest : public Test {
+  LLVMContext Ctx;
+  ModulePassManager MPM;
+  PassBuilder PB;
+  MockModuleAnalysisHandle MMAHandle;
+  LoopAnalysisManager LAM;
+  FunctionAnalysisManager FAM;
+  CGSCCAnalysisManager CGAM;
+  ModuleAnalysisManager MAM;
+  LLVMContext Context;
+  std::unique_ptr<Module> M;
+
+  PGOInstrumentationGenTest() {
+    MAM.registerPass([&] { return MMAHandle.getAnalysis(); });
+    PB.registerModuleAnalyses(MAM);
+    PB.registerCGSCCAnalyses(CGAM);
+    PB.registerFunctionAnalyses(FAM);
+    PB.registerLoopAnalyses(LAM);
+    PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+    MPM.addPass(
+        RequireAnalysisPass<MockModuleAnalysisHandle::Analysis, Module>());
+    MPM.addPass(PGOInstrumentationGen());
+  }
+
+  void parseAssembly(const StringRef IR) {
+    SMDiagnostic Error;
+    M = parseAssemblyString(IR, Error, Context);
+    std::string ErrMsg;
+    raw_string_ostream OS(ErrMsg);
+    Error.print("", OS);
+
+    // A failure here means that the test itself is buggy.
+    if (!M)
+      report_fatal_error(OS.str().c_str());
+  }
+};
+
+TEST_F(PGOInstrumentationGenTest, NotInstrumentedDeclarationsOnly) {
+  const StringRef NotInstrumentableCode = R"(
+    declare i32 @f(i32);
+  )";
+
+  parseAssembly(NotInstrumentableCode);
+
+  ASSERT_THAT(M, NotNull());
+
+  EXPECT_CALL(MMAHandle, run(Ref(*M), _)).WillOnce(DoDefault());
+  EXPECT_CALL(MMAHandle, invalidate(Ref(*M), _, _)).Times(0);
+
+  MPM.run(*M, MAM);
+
+  const auto *IRInstrVar =
+      M->getNamedGlobal(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
+  EXPECT_THAT(IRInstrVar, IsNull());
+}
+
+TEST_F(PGOInstrumentationGenTest, NotInstrumentedGlobals) {
+  const StringRef NotInstrumentableCode = R"(
+    @foo.table = internal unnamed_addr constant [1 x ptr] [ptr @f]
+    declare i32 @f(i32);
+  )";
+
+  parseAssembly(NotInstrumentableCode);
+
+  ASSERT_THAT(M, NotNull());
+
+  EXPECT_CALL(MMAHandle, run(Ref(*M), _)).WillOnce(DoDefault());
+  EXPECT_CALL(MMAHandle, invalidate(Ref(*M), _, _)).Times(0);
+
+  MPM.run(*M, MAM);
+
+  const auto *IRInstrVar =
+      M->getNamedGlobal(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
+  EXPECT_THAT(IRInstrVar, IsNull());
+}
+
+TEST_F(PGOInstrumentationGenTest, Instrumented) {
+  const StringRef InstrumentableCode = R"(
+    define i32 @f(i32 %n) {
+    entry:
+      ret i32 0
+    }
+  )";
+
+  parseAssembly(InstrumentableCode);
+
+  ASSERT_THAT(M, NotNull());
+
+  Sequence PassSequence;
+  EXPECT_CALL(MMAHandle, run(Ref(*M), _))
+      .InSequence(PassSequence)
+      .WillOnce(DoDefault());
+  EXPECT_CALL(MMAHandle, invalidate(Ref(*M), _, _))
+      .InSequence(PassSequence)
+      .WillOnce(DoDefault());
+
+  MPM.run(*M, MAM);
+
+  const auto *IRInstrVar =
+      M->getNamedGlobal(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
+  EXPECT_THAT(IRInstrVar, NotNull());
+  EXPECT_FALSE(IRInstrVar->isDeclaration());
+}
+
+} // end anonymous namespace

Copy link

github-actions bot commented May 26, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@samolisov samolisov force-pushed the pgo-instrument-when-at-least-one-definition branch from 4d46fc3 to 7bf25a1 Compare May 26, 2024 18:00
M, IntTy64, true, GlobalValue::WeakAnyLinkage,
Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName);
Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName,
InsertionPoint);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the explicit insertion point needed now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the COMDATs are generated in an LLVM IR module at the beginning while globals are generated one after another. Because now the __llvm_profile_raw_version variable is generated after the counters, it is decoupled from the corresponding comdat directive by default (so, without the insertion point):

$__llvm_profile_raw_version = comdat any
@__profn_test_br_1 = private constant [9 x i8] c"test_br_1"
@__llvm_profile_raw_version = hidden constant i64 {00000}, comdat

what looks ugly and may make the debugging process less smooth. This is why I added the explicit insertion point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a separate change that should be split out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aeubanks do you mean the __llvm_profile_raw_version variable generation? If to separate only insertion point adding, some lit test must be modified temporarily and then returned back. I think this work isn't necessary, better I'll split the patch to the compile time optimization and the variable not generation. Thank you for the suggestion.

@@ -0,0 +1,15 @@
; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN-COMDAT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this test behave different than the current status quo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of the test is to check that the assert on line https://github.com/llvm/llvm-project/pull/93421/files#diff-618e0bf5dc7c44fae29843f3be69506a1b0a1409c7f018c61b0acf593f1c4605R408 is never triggered. A module with no global variables (other than the counters inserted during the instrumentation) was prepared to demonstrate that the version variable __llvm_profile_raw_version will be generated and it will be inserted in the module just after the corresponding comdat. If you think this is a redundant module, I'm going to remove it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is just for compile time optimization, then adding these tests isn't necessary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah the commit title is a bit misleading since there is a functional change in that we don't insert __llvm_profile_raw_version if there are no globals. so this test is fine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aeubanks Yes, unfortunately I have no idea how to reflect both changes in the title what is the sign there should be two commits.

@samolisov
Copy link
Contributor Author

@teresajohnson Thank you very much for the feedback. I've answered inline and mark the PR as WIP because some rework is required.

@samolisov samolisov marked this pull request as draft May 29, 2024 02:24
@samolisov samolisov changed the title [PGO] Instrument modules with at least a single function definition [PGO] Preserve analysis when nothing was instrumented May 30, 2024
@samolisov samolisov changed the title [PGO] Preserve analysis when nothing was instrumented [PGO] Preserve analysis results when nothing was instrumented May 30, 2024
@samolisov
Copy link
Contributor Author

I'm going to rebase my changes onto updated main because an important part of the work: unit tests and the baseline for LIT tests are already there. I know, there is some troubles with rebased commits in GitHub UI, but I see no alternative. Maybe merging main into my branch but I afraid of a lot of intermediate commits will be displayed as changes in the PR and make the review impossible.

@samolisov samolisov force-pushed the pgo-instrument-when-at-least-one-definition branch from 7bf25a1 to 37019cf Compare May 30, 2024 20:19
@samolisov samolisov marked this pull request as ready for review May 30, 2024 20:20
@samolisov
Copy link
Contributor Author

samolisov commented May 31, 2024

The PR has been modified to contain NFC changes only to optimize the compilation time. All the changes related to __llvm_profile_raw_version will be submitted as a separated PR.

P.S. It seems, github doesn't allow to modify the source branch for a PR, so I cannot convert this PR into a stacked one. I've pushed exactly the same commits into the https://github.com/llvm/llvm-project/tree/users/psamolysov/pgo-instrument-when-at-least-one-definition branch and opened the second PR into that branch. There are some conflicts but I believe they will be solved after landing the changes from the current PR into main. This commit is reviewable: 5d5ead1

@samolisov
Copy link
Contributor Author

Gentle ping.

@samolisov
Copy link
Contributor Author

@teresajohnson @aeubanks Could you have a look at the PR again? I believe, all the comments and suggestions were addressed. Even though the next parts of this "stacked" PR won't pass the review, this part may reduce the compilation time a little bit, at least.

@samolisov
Copy link
Contributor Author

Gentle ping.

Copy link
Contributor

@teresajohnson teresajohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm sorry I completely missed the updates on this one

@samolisov samolisov force-pushed the pgo-instrument-when-at-least-one-definition branch from 944d5f5 to 8cb224b Compare October 11, 2024 16:55
@samolisov
Copy link
Contributor Author

@teresajohnson This is my bad, I should have used the re-request review button.

Anyway, thank you very much for review. I'm going to merge this after CI finishing.

The 'PGOInstrumentationGen' pass should preserve all analysis results
when nothing was actually instrumented. Currently, only modules that
contain at least a single function definition are instrumented. When a
module contains only function declarations and, optionally, global
variable definitions (a module for the regular-LTO phase for thin-LTO
when LTOUnit splitting is enabled, for example), such module is not
instrumented (yet?) and there is no reason to introduce the
'__llvm_profile_raw_version' variable into the module.
@samolisov samolisov force-pushed the pgo-instrument-when-at-least-one-definition branch from 8cb224b to 9c08c66 Compare October 11, 2024 18:43
@samolisov samolisov merged commit 23c64be into llvm:main Oct 12, 2024
8 checks passed
@samolisov samolisov deleted the pgo-instrument-when-at-least-one-definition branch October 12, 2024 03:30
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 12, 2024

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-expensive-checks-debian running on gribozavr4 while building llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/16/builds/7037

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: Transforms/PGOProfile/critical-edge-threshold.ll' FAILED ********************
Exit Code: 2

Command Output (stderr):
--
RUN: at line 2: /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt < /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/Transforms/PGOProfile/critical-edge-threshold.ll -passes=pgo-instr-gen -pgo-critical-edge-threshold=1 -pgo-instrument-entry=true -S | /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/Transforms/PGOProfile/critical-edge-threshold.ll
+ /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt -passes=pgo-instr-gen -pgo-critical-edge-threshold=1 -pgo-instrument-entry=true -S
+ /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/Transforms/PGOProfile/critical-edge-threshold.ll
LLVM ERROR: Module changed by PGOInstrumentationGen without invalidating analyses
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt -passes=pgo-instr-gen -pgo-critical-edge-threshold=1 -pgo-instrument-entry=true -S
1.	Running pass "ctx-instr-gen" on module "<stdin>"
 #0 0x00000000045a3337 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x45a3337)
 #1 0x00000000045a0dee llvm::sys::RunSignalHandlers() (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x45a0dee)
 #2 0x00000000045a39ef SignalHandler(int) Signals.cpp:0:0
 #3 0x00007f3e96aa6140 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x13140)
 #4 0x00007f3e965bad51 raise (/lib/x86_64-linux-gnu/libc.so.6+0x38d51)
 #5 0x00007f3e965a4537 abort (/lib/x86_64-linux-gnu/libc.so.6+0x22537)
 #6 0x0000000004504c4a llvm::report_fatal_error(llvm::Twine const&, bool) (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x4504c4a)
 #7 0x000000000283148a void llvm::detail::UniqueFunctionBase<void, llvm::StringRef, llvm::Any, llvm::PreservedAnalyses const&>::CallImpl<llvm::PreservedCFGCheckerInstrumentation::registerCallbacks(llvm::PassInstrumentationCallbacks&, llvm::AnalysisManager<llvm::Module>&)::$_18>(void*, llvm::StringRef, llvm::Any&, llvm::PreservedAnalyses const&) StandardInstrumentations.cpp:0:0
 #8 0x00000000043d1fbe llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x43d1fbe)
 #9 0x0000000000859840 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x859840)
#10 0x000000000084ef25 optMain (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x84ef25)
#11 0x00007f3e965a5d7a __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x23d7a)
#12 0x000000000084b28a _start (/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/opt+0x84b28a)
FileCheck error: '<stdin>' is empty.
FileCheck command line:  /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/Transforms/PGOProfile/critical-edge-threshold.ll

--

********************


@mikaelholmen
Copy link
Collaborator

Hi @samolisov

Did you see that a buildbot failed with this patch? I see the same if I compile with EXPENSIVE_CHECKS set and run lit tests.
The following tests start failing with this patch:

Failed Tests (4):
  Clang :: CodeGen/pgo-instrumentation.c
  LLVM :: Transforms/PGOProfile/critical-edge-threshold.ll
  LLVM :: Transforms/PGOProfile/declarations_only.ll
  LLVM :: Transforms/PGOProfile/global_variables_only.ll

They all hit the error

Module changed by PGOInstrumentationGen without invalidating analyses

@samolisov
Copy link
Contributor Author

Hi @samolisov

Did you see that a buildbot failed with this patch? I see the same if I compile with EXPENSIVE_CHECKS set and run lit tests. The following tests start failing with this patch:

Failed Tests (4):
  Clang :: CodeGen/pgo-instrumentation.c
  LLVM :: Transforms/PGOProfile/critical-edge-threshold.ll
  LLVM :: Transforms/PGOProfile/declarations_only.ll
  LLVM :: Transforms/PGOProfile/global_variables_only.ll

They all hit the error

Module changed by PGOInstrumentationGen without invalidating analyses

Hello @mikaelholmen I saw this errors but unfortunately I cannot reproduce them locally with neither release nor debug builds. I have no idea what this expensive checks do and what is the difference with usual tests.

Only thing what I see, the pass now may add a global variable without invalidating any analysis but I'm not sure is this a problem. If so, the better idea would be to revert the patch.

@mikaelholmen
Copy link
Collaborator

Hello @mikaelholmen I saw this errors but unfortunately I cannot reproduce them locally with neither release nor debug builds. I have no idea what this expensive checks do and what is the difference with usual tests.

Add

-DLLVM_ENABLE_EXPENSIVE_CHECKS=ON

to your cmake command and recompile and run lit tests. Then you should be able to see the failures.

samolisov pushed a commit that referenced this pull request Oct 16, 2024
@samolisov
Copy link
Contributor Author

@mikaelholmen Thank you very much! I've added the flag and now can reproduce the issue.

@teresajohnson @aeubanks Could you help? I opened the second PR to create the $__llvm_profile_raw_version and @__llvm_profile_raw_version variables only when there were actually generated instructions to instrument the module. But currently I saw a "chicken-egg" problem: even though the module was not instrumented, the preamble:

$__llvm_profile_raw_version = comdat any

@__llvm_profile_raw_version = hidden constant i64 72057594037927946, comdat

is still generated and this changes the hash of the module what leads to error emission in the PassManager:

...
#11 0x0000556e634bf56e void llvm::PassInstrumentation::runAfterPass<llvm::Module, llvm::detail::PassConcept<llvm::Module, llvm::AnalysisManager<llvm::Module>>>(llvm::detail::PassConcept<llvm::Module, llvm::AnalysisManager<llvm::Module>> const&, llvm::Module const&, llvm::PreservedAnalyses const&) const
#12 0x0000556e634bc555 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&)
#13 0x0000556e5f196d9d llvm::runPassPipeline
...

I see the following two ways: either to combine both patches into one or just to revert the current changes (what has already been done) to not brake the pipelines. So, I think we may discuss the changed focusing on whether the global variable __llvm_profile_raw_version should be generated in any way or only when the module has actually been modified?

bricknerb pushed a commit to bricknerb/llvm-project that referenced this pull request Oct 16, 2024
DanielCChen pushed a commit to DanielCChen/llvm-project that referenced this pull request Oct 16, 2024
…3421)

The `PGOInstrumentationGen` pass should preserve all analysis results
when nothing was actually instrumented. Currently, only modules that
contain at least a single function definition are instrumented. When a
module contains only function declarations and, optionally, global
variable definitions (a module for the regular-LTO phase for thin-LTO
when LTOUnit splitting is enabled, for example), such module is not
instrumented (yet?) and there is no reason to invalidate any analysis
results.

NFC.
bricknerb pushed a commit to bricknerb/llvm-project that referenced this pull request Oct 17, 2024
…3421)

The `PGOInstrumentationGen` pass should preserve all analysis results
when nothing was actually instrumented. Currently, only modules that
contain at least a single function definition are instrumented. When a
module contains only function declarations and, optionally, global
variable definitions (a module for the regular-LTO phase for thin-LTO
when LTOUnit splitting is enabled, for example), such module is not
instrumented (yet?) and there is no reason to invalidate any analysis
results.

NFC.
bricknerb pushed a commit to bricknerb/llvm-project that referenced this pull request Oct 17, 2024
EricWF pushed a commit to efcs/llvm-project that referenced this pull request Oct 22, 2024
…3421)

The `PGOInstrumentationGen` pass should preserve all analysis results
when nothing was actually instrumented. Currently, only modules that
contain at least a single function definition are instrumented. When a
module contains only function declarations and, optionally, global
variable definitions (a module for the regular-LTO phase for thin-LTO
when LTOUnit splitting is enabled, for example), such module is not
instrumented (yet?) and there is no reason to invalidate any analysis
results.

NFC.
EricWF pushed a commit to efcs/llvm-project that referenced this pull request Oct 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:transforms PGO Profile Guided Optimizations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants