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] Generate __llvm_profile_raw_version only when instrumented #93917

Open
wants to merge 2 commits into
base: users/psamolysov/pgo-instrument-when-at-least-one-definition
Choose a base branch
from

Conversation

samolisov
Copy link
Contributor

Currently, only modules that contain at least a single function
definition are instrumented. When a module contains no function
definitions at all, the module is not instrumented (yet?) and there is
no reason to introduce the '__llvm_profile_raw_version' variable into
the module.

samolisov added 2 commits May 31, 2024 05:53
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.
Currently, only modules that contain at least a single function
definition are instrumented. When a module contains no function
definitions at all, the module is not instrumented (yet?) and there is
no reason to introduce the '__llvm_profile_raw_version' variable into
the module.
@llvmbot llvmbot added PGO Profile Guided Optimizations llvm:transforms labels May 31, 2024
@llvmbot
Copy link
Member

llvmbot commented May 31, 2024

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-pgo

Author: Pavel Samolysov (samolisov)

Changes

Currently, only modules that contain at least a single function
definition are instrumented. When a module contains no function
definitions at all, the module is not instrumented (yet?) and there is
no reason to introduce the '__llvm_profile_raw_version' variable into
the module.


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

5 Files Affected:

  • (modified) llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp (+18-7)
  • (modified) llvm/test/Transforms/PGOProfile/declarations_only.ll (+1-6)
  • (modified) llvm/test/Transforms/PGOProfile/global_variables_only.ll (+1-4)
  • (added) llvm/test/Transforms/PGOProfile/no_global_variables.ll (+15)
  • (modified) llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp (+50-19)
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
index e7208fc264c7c..7a975725410c9 100644
--- a/llvm/test/Transforms/PGOProfile/declarations_only.ll
+++ b/llvm/test/Transforms/PGOProfile/declarations_only.ll
@@ -1,13 +1,8 @@
-; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN --check-prefix=GEN-COMDAT
+; 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"
 
-; GEN-COMDAT: $__llvm_profile_raw_version = comdat any
-; GEN-COMDAT: @__llvm_profile_raw_version = hidden constant i64 {{[0-9]+}}, comdat
-; GEN-NOT: @__profn_test_1 = private constant [6 x i8] c"test_1"
-; GEN-NOT: @__profn_test_2 = private constant [6 x i8] c"test_2"
-
 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
index 3bfa29af5d34f..46628bfafe534 100644
--- a/llvm/test/Transforms/PGOProfile/global_variables_only.ll
+++ b/llvm/test/Transforms/PGOProfile/global_variables_only.ll
@@ -1,9 +1,6 @@
-; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN-COMDAT
+; 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"
 
-; GEN-COMDAT: $__llvm_profile_raw_version = comdat any
-; GEN-COMDAT: @__llvm_profile_raw_version = hidden constant i64 {{[0-9]+}}, comdat
-
 @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/Instrumentation/PGOInstrumentationTest.cpp b/llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp
index 02c2df2a138b0..01d17c5059a1a 100644
--- a/llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp
+++ b/llvm/unittests/Transforms/Instrumentation/PGOInstrumentationTest.cpp
@@ -104,9 +104,13 @@ class MockModuleAnalysisHandle
                ModuleAnalysisManager::Invalidator &));
 };
 
-struct PGOInstrumentationGenTest
-    : public Test,
-      WithParamInterface<std::tuple<StringRef, StringRef>> {
+template <typename ParamType> struct PGOTestName {
+  std::string operator()(const TestParamInfo<ParamType> &Info) const {
+    return std::get<1>(Info.param).str();
+  }
+};
+
+struct PGOInstrumentationGenTest : public Test {
   LLVMContext Ctx;
   ModulePassManager MPM;
   PassBuilder PB;
@@ -143,12 +147,47 @@ struct PGOInstrumentationGenTest
   }
 };
 
+struct PGOInstrumentationGenInstrumentTest
+    : PGOInstrumentationGenTest,
+      WithParamInterface<std::tuple<StringRef, StringRef>> {};
+
 static constexpr StringRef CodeWithFuncDefs = R"(
   define i32 @f(i32 %n) {
   entry:
     ret i32 0
   })";
 
+INSTANTIATE_TEST_SUITE_P(
+    PGOInstrumetationGenTestSuite, PGOInstrumentationGenInstrumentTest,
+    Values(std::make_tuple(CodeWithFuncDefs, "instrument_function_defs")),
+    PGOTestName<PGOInstrumentationGenInstrumentTest::ParamType>());
+
+TEST_P(PGOInstrumentationGenInstrumentTest, Instrumented) {
+  const StringRef Code = std::get<0>(GetParam());
+  parseAssembly(Code);
+
+  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());
+}
+
+struct PGOInstrumentationGenIgnoreTest
+    : PGOInstrumentationGenTest,
+      WithParamInterface<std::tuple<StringRef, StringRef>> {};
+
 static constexpr StringRef CodeWithFuncDecls = R"(
   declare i32 @f(i32);
 )";
@@ -159,34 +198,26 @@ static constexpr StringRef CodeWithGlobals = R"(
 )";
 
 INSTANTIATE_TEST_SUITE_P(
-    PGOInstrumetationGenTestSuite, PGOInstrumentationGenTest,
-    Values(std::make_tuple(CodeWithFuncDefs, "instrument_function_defs"),
-           std::make_tuple(CodeWithFuncDecls, "instrument_function_decls"),
+    PGOInstrumetationGenIgnoreTestSuite, PGOInstrumentationGenIgnoreTest,
+    Values(std::make_tuple(CodeWithFuncDecls, "instrument_function_decls"),
            std::make_tuple(CodeWithGlobals, "instrument_globals")),
-    [](const TestParamInfo<PGOInstrumentationGenTest::ParamType> &Info) {
-      return std::get<1>(Info.param).str();
-    });
+    PGOTestName<PGOInstrumentationGenIgnoreTest::ParamType>());
 
-TEST_P(PGOInstrumentationGenTest, Instrumented) {
+TEST_P(PGOInstrumentationGenIgnoreTest, NotInstrumented) {
   const StringRef Code = std::get<0>(GetParam());
+
   parseAssembly(Code);
 
   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());
+  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, NotNull());
-  EXPECT_FALSE(IRInstrVar->isDeclaration());
+  EXPECT_THAT(IRInstrVar, IsNull());
 }
 
 } // end anonymous namespace

@samolisov
Copy link
Contributor Author

It seems, github doesn't allow to modify the source branch for a PR, so I cannot convert an already opened PR #93421 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 this PR into that branch. There are some conflicts but I believe they will be solved after landing the changes from #93421 into main. This commit is reviewable: 5d5ead1

@aeubanks
Copy link
Contributor

What's the benefit to this change? From a compile time perspective, files containing no functions are essentially negligible. From a consistency perspective, not adding __llvm_profile_raw_version in some cases creates divergence between different source files (IIRC we do some checking of __llvm_profile_raw_version against the profile runtime library? which is good for checking that all your sources were compiled the same way)

@samolisov
Copy link
Contributor Author

@aeubanks my intent is to get no __llvm_profile_raw_version in modules where nothing was actually instrumented. Usually, in my cases, they are the modules for regular-LTO part in thin-LTO where split LTO Units are enabled. This is a good point about consistency and using the variable as a flag that a module had already been instrumented but when a split LTO unit contains the variable in both parts it makes problems for the linker: I know how to distinguish one module from another and change the linkage for the variable but I have no idea how to deal with two parts of the same module. May be this solution looks as a hack but I think it makes sense: nothing was instrumented and nothing makes a reason for the version variable to present. But I'm unfamiliar about details of the profile runtime library, if the absence of the version variable makes troubles it is a reason to preserve it. But actually I tried to link such modules, without the variable, and get some profile.

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.

3 participants