[clang] [llvm] [Darwin][Clang][PGO] Pass CSPGO related flags to ld when using libLTO.dylib (PR #195020)

Henry Jiang via cfe-commits cfe-commits at lists.llvm.org
Fri May 1 11:05:15 PDT 2026


https://github.com/mustartt updated https://github.com/llvm/llvm-project/pull/195020

>From 8e62ab256d7101c68a8f22b60dac16ec11c2d7f6 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Wed, 29 Apr 2026 23:36:56 -0700
Subject: [PATCH 1/3] [Darwin] Fix clang driver's invocation for cspgo

---
 clang/lib/Driver/ToolChains/Darwin.cpp        | 18 ++++++++++++++
 clang/test/Driver/cspgo-lto.c                 | 14 +++++++++++
 llvm/lib/LTO/LTOCodeGenerator.cpp             |  7 ++++--
 llvm/lib/LTO/ThinLTOCodeGenerator.cpp         | 12 ++++++++++
 .../llvm-lto/Inputs/cspgo-noncs.proftext      |  1 +
 llvm/test/tools/llvm-lto/cspgo-generate.ll    | 20 ++++++++++++++++
 .../tools/llvm-lto/cspgo-thinlto-generate.ll  | 24 +++++++++++++++++++
 7 files changed, 94 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/tools/llvm-lto/Inputs/cspgo-noncs.proftext
 create mode 100644 llvm/test/tools/llvm-lto/cspgo-generate.ll
 create mode 100644 llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll

diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
index 61b779c60b90f..5045d21c4e262 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -493,6 +493,24 @@ void darwin::Linker::AddLinkArgs(Compilation &C, const ArgList &Args,
       CmdArgs.push_back(
           Args.MakeArgString(Twine("--codegen-data-generate-path=") +
                              CodeGenDataGenArg->getValue()));
+  } else {
+    if (auto *CSPGOGenerateArg = getLastCSProfileGenerateArg(Args)) {
+      SmallString<128> Path(CSPGOGenerateArg->getNumValues() == 0
+                                ? ""
+                                : CSPGOGenerateArg->getValue());
+      llvm::sys::path::append(Path, "default_%m.profraw");
+      CmdArgs.push_back("-mllvm");
+      CmdArgs.push_back("-cs-profile-generate");
+      CmdArgs.push_back("-mllvm");
+      CmdArgs.push_back(Args.MakeArgString(Twine("-cs-profile-path=") + Path));
+    } else if (auto *ProfileUseArg = getLastProfileUseArg(Args)) {
+      SmallString<128> Path(
+          ProfileUseArg->getNumValues() == 0 ? "" : ProfileUseArg->getValue());
+      if (Path.empty() || llvm::sys::fs::is_directory(Path))
+        llvm::sys::path::append(Path, "default.profdata");
+      CmdArgs.push_back("-mllvm");
+      CmdArgs.push_back(Args.MakeArgString(Twine("-cs-profile-path=") + Path));
+    }
   }
 }
 
diff --git a/clang/test/Driver/cspgo-lto.c b/clang/test/Driver/cspgo-lto.c
index 232d21bbf948a..4129298e652e4 100644
--- a/clang/test/Driver/cspgo-lto.c
+++ b/clang/test/Driver/cspgo-lto.c
@@ -18,3 +18,17 @@
 // DARWIN-GEN1-SAME: "--cs-profile-path=default_%m.profraw"
 // DARWIN-GEN2: "--cs-profile-generate"
 // DARWIN-GEN2-SAME: "--cs-profile-path=directory{{(/|\\\\)}}default_%m.profraw"
+
+// RUN: %clang --target=arm64-apple-macos -### %t.o -flto=thin -fcs-profile-generate 2>&1 | FileCheck %s --check-prefix=DARWIN-LD-GEN1
+// RUN: %clang --target=arm64-apple-macos -### %t.o -flto=thin -fcs-profile-generate=directory 2>&1 | FileCheck %s --check-prefix=DARWIN-LD-GEN2
+
+// DARWIN-LD-GEN1: "-mllvm" "-cs-profile-generate"
+// DARWIN-LD-GEN1-SAME: "-mllvm" "-cs-profile-path=default_%m.profraw"
+// DARWIN-LD-GEN2: "-mllvm" "-cs-profile-generate"
+// DARWIN-LD-GEN2-SAME: "-mllvm" "-cs-profile-path=directory{{(/|\\\\)}}default_%m.profraw"
+
+// RUN: %clang --target=arm64-apple-macos -### %t.o -flto=thin -fprofile-use 2>&1 | FileCheck %s --check-prefix=DARWIN-LD-USE1
+// RUN: %clang --target=arm64-apple-macos -### %t.o -flto=thin -fprofile-use=a.profdata 2>&1 | FileCheck %s --check-prefix=DARWIN-LD-USE2
+
+// DARWIN-LD-USE1: "-mllvm" "-cs-profile-path=default.profdata"
+// DARWIN-LD-USE2: "-mllvm" "-cs-profile-path=a.profdata"
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index 46be71da5a092..321826618bc69 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -111,11 +111,11 @@ static cl::opt<std::string> AIXSystemAssemblerPath(
     cl::desc("Path to a system assembler, picked up on AIX only"),
     cl::value_desc("path"));
 
-static cl::opt<bool>
+cl::opt<bool>
     LTORunCSIRInstr("cs-profile-generate",
                     cl::desc("Perform context sensitive PGO instrumentation"));
 
-static cl::opt<std::string>
+cl::opt<std::string>
     LTOCSIRProfile("cs-profile-path",
                    cl::desc("Context sensitive profile file path"));
 } // namespace llvm
@@ -557,6 +557,9 @@ bool LTOCodeGenerator::optimize() {
 
   // libLTO parses options late, so re-set them here.
   Context.setDiscardValueNames(LTODiscardValueNames);
+  Config.StatsFile = LTOStatsFile;
+  Config.RunCSIRInstr = LTORunCSIRInstr;
+  Config.CSIRProfile = LTOCSIRProfile;
 
   auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks(
       Context, RemarksFilename, RemarksPasses, RemarksFormat,
diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
index 93b672ae7840c..41d83a591a37b 100644
--- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -79,6 +79,8 @@ extern cl::opt<bool> RemarksWithHotness;
 extern cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser>
     RemarksHotnessThreshold;
 extern cl::opt<std::string> RemarksFormat;
+extern cl::opt<bool> LTORunCSIRInstr;
+extern cl::opt<std::string> LTOCSIRProfile;
 }
 
 // Default to using all available threads in the system, but using only one
@@ -235,6 +237,16 @@ static void optimizeModule(Module &TheModule, TargetMachine &TM,
                            unsigned OptLevel, bool Freestanding,
                            bool DebugPassManager, ModuleSummaryIndex *Index) {
   std::optional<PGOOptions> PGOOpt;
+  if (LTORunCSIRInstr) {
+    PGOOpt =
+        PGOOptions("", LTOCSIRProfile, "",
+                   /*MemoryProfile=*/"", PGOOptions::IRUse,
+                   PGOOptions::CSIRInstr, PGOOptions::ColdFuncOpt::Default);
+  } else if (!LTOCSIRProfile.empty()) {
+    PGOOpt = PGOOptions(LTOCSIRProfile, "", "",
+                        /*MemoryProfile=*/"", PGOOptions::IRUse,
+                        PGOOptions::CSIRUse, PGOOptions::ColdFuncOpt::Default);
+  }
   LoopAnalysisManager LAM;
   FunctionAnalysisManager FAM;
   CGSCCAnalysisManager CGAM;
diff --git a/llvm/test/tools/llvm-lto/Inputs/cspgo-noncs.proftext b/llvm/test/tools/llvm-lto/Inputs/cspgo-noncs.proftext
new file mode 100644
index 0000000000000..04a7c1c1a35aa
--- /dev/null
+++ b/llvm/test/tools/llvm-lto/Inputs/cspgo-noncs.proftext
@@ -0,0 +1 @@
+:ir
diff --git a/llvm/test/tools/llvm-lto/cspgo-generate.ll b/llvm/test/tools/llvm-lto/cspgo-generate.ll
new file mode 100644
index 0000000000000..d667cd3fde544
--- /dev/null
+++ b/llvm/test/tools/llvm-lto/cspgo-generate.ll
@@ -0,0 +1,20 @@
+; Test that CS-PGO instrumentation works through the old LTO C API codepath.
+; This validates the fix for a bug where LTOCodeGenerator::optimize() did not
+; re-read cl::opt values (cs-profile-generate, cs-profile-path) after
+; parseCodeGenDebugOptions(), causing Config.RunCSIRInstr to be stale.
+;
+; RUN: llvm-profdata merge %S/Inputs/cspgo-noncs.proftext -o %t.profdata
+; RUN: opt -module-summary %s -o %t.o
+; RUN: llvm-lto -cs-profile-generate -cs-profile-path=%t.profdata \
+; RUN:   -exported-symbol=_main -o %t.lto.o %t.o
+; RUN: llvm-nm %t.lto.o | FileCheck %s
+;
+; CHECK: __profc_main
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "arm64-apple-macosx14.0.0"
+
+define i32 @main() {
+entry:
+  ret i32 0
+}
diff --git a/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll b/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll
new file mode 100644
index 0000000000000..670b5e82cfe4d
--- /dev/null
+++ b/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll
@@ -0,0 +1,24 @@
+; Test that CS-PGO instrumentation works through ThinLTOCodeGenerator::optimizeModule().
+; This validates the fix for a bug where optimizeModule() created an empty
+; PGOOptions and never populated it from cl::opt flags, so the PassBuilder
+; never saw a CS instrumentation request.
+;
+; RUN: llvm-profdata merge %S/Inputs/cspgo-noncs.proftext -o %t.profdata
+; RUN: opt -module-summary %s -o %t.o
+; RUN: llvm-lto -thinlto-action=optimize \
+; RUN:   -cs-profile-generate -cs-profile-path=%t.profdata \
+; RUN:   -o %t.opt.bc %t.o
+; RUN: llvm-dis %t.opt.bc -o - | FileCheck %s
+;
+; CHECK: @__profc_main = private global
+; CHECK-LABEL: @main()
+; CHECK:   %pgocount = load i64, ptr @__profc_main
+; CHECK:   store i64 %{{.*}}, ptr @__profc_main
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "arm64-apple-macosx14.0.0"
+
+define i32 @main() {
+entry:
+  ret i32 0
+}

>From 653af2336279618979fd6b803711c574048a64b2 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Wed, 29 Apr 2026 23:57:47 -0700
Subject: [PATCH 2/3] add aarch64 registerd target for test

---
 llvm/test/tools/llvm-lto/cspgo-generate.ll         | 5 +----
 llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll | 5 +----
 2 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/llvm/test/tools/llvm-lto/cspgo-generate.ll b/llvm/test/tools/llvm-lto/cspgo-generate.ll
index d667cd3fde544..02d8c283fdf0c 100644
--- a/llvm/test/tools/llvm-lto/cspgo-generate.ll
+++ b/llvm/test/tools/llvm-lto/cspgo-generate.ll
@@ -1,7 +1,4 @@
-; Test that CS-PGO instrumentation works through the old LTO C API codepath.
-; This validates the fix for a bug where LTOCodeGenerator::optimize() did not
-; re-read cl::opt values (cs-profile-generate, cs-profile-path) after
-; parseCodeGenDebugOptions(), causing Config.RunCSIRInstr to be stale.
+; REQUIRES: aarch64-registered-target
 ;
 ; RUN: llvm-profdata merge %S/Inputs/cspgo-noncs.proftext -o %t.profdata
 ; RUN: opt -module-summary %s -o %t.o
diff --git a/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll b/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll
index 670b5e82cfe4d..c4647443698a9 100644
--- a/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll
+++ b/llvm/test/tools/llvm-lto/cspgo-thinlto-generate.ll
@@ -1,7 +1,4 @@
-; Test that CS-PGO instrumentation works through ThinLTOCodeGenerator::optimizeModule().
-; This validates the fix for a bug where optimizeModule() created an empty
-; PGOOptions and never populated it from cl::opt flags, so the PassBuilder
-; never saw a CS instrumentation request.
+; REQUIRES: aarch64-registered-target
 ;
 ; RUN: llvm-profdata merge %S/Inputs/cspgo-noncs.proftext -o %t.profdata
 ; RUN: opt -module-summary %s -o %t.o

>From 1a192be233654d2740ccfd18c024ca69ce52cc10 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Fri, 1 May 2026 11:04:12 -0700
Subject: [PATCH 3/3] address review

---
 clang/test/Driver/cspgo-lto.c                 |  2 ++
 .../tools/llvm-lto/Inputs/cspgo-cs.proftext   |  5 +++++
 llvm/test/tools/llvm-lto/cspgo-generate.ll    |  2 +-
 llvm/test/tools/llvm-lto/cspgo-thinlto-use.ll | 20 +++++++++++++++++++
 llvm/test/tools/llvm-lto/cspgo-use.ll         | 18 +++++++++++++++++
 5 files changed, 46 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/tools/llvm-lto/Inputs/cspgo-cs.proftext
 create mode 100644 llvm/test/tools/llvm-lto/cspgo-thinlto-use.ll
 create mode 100644 llvm/test/tools/llvm-lto/cspgo-use.ll

diff --git a/clang/test/Driver/cspgo-lto.c b/clang/test/Driver/cspgo-lto.c
index 4129298e652e4..e9420d7972b14 100644
--- a/clang/test/Driver/cspgo-lto.c
+++ b/clang/test/Driver/cspgo-lto.c
@@ -30,5 +30,7 @@
 // RUN: %clang --target=arm64-apple-macos -### %t.o -flto=thin -fprofile-use 2>&1 | FileCheck %s --check-prefix=DARWIN-LD-USE1
 // RUN: %clang --target=arm64-apple-macos -### %t.o -flto=thin -fprofile-use=a.profdata 2>&1 | FileCheck %s --check-prefix=DARWIN-LD-USE2
 
+// DARWIN-LD-USE1-NOT: "-cs-profile-generate"
 // DARWIN-LD-USE1: "-mllvm" "-cs-profile-path=default.profdata"
+// DARWIN-LD-USE2-NOT: "-cs-profile-generate"
 // DARWIN-LD-USE2: "-mllvm" "-cs-profile-path=a.profdata"
diff --git a/llvm/test/tools/llvm-lto/Inputs/cspgo-cs.proftext b/llvm/test/tools/llvm-lto/Inputs/cspgo-cs.proftext
new file mode 100644
index 0000000000000..ce700d863cb2e
--- /dev/null
+++ b/llvm/test/tools/llvm-lto/Inputs/cspgo-cs.proftext
@@ -0,0 +1,5 @@
+:csir
+main
+1895182923573755903
+1
+100000
diff --git a/llvm/test/tools/llvm-lto/cspgo-generate.ll b/llvm/test/tools/llvm-lto/cspgo-generate.ll
index 02d8c283fdf0c..a5d8718d75380 100644
--- a/llvm/test/tools/llvm-lto/cspgo-generate.ll
+++ b/llvm/test/tools/llvm-lto/cspgo-generate.ll
@@ -1,7 +1,7 @@
 ; REQUIRES: aarch64-registered-target
 ;
 ; RUN: llvm-profdata merge %S/Inputs/cspgo-noncs.proftext -o %t.profdata
-; RUN: opt -module-summary %s -o %t.o
+; RUN: llvm-as %s -o %t.o
 ; RUN: llvm-lto -cs-profile-generate -cs-profile-path=%t.profdata \
 ; RUN:   -exported-symbol=_main -o %t.lto.o %t.o
 ; RUN: llvm-nm %t.lto.o | FileCheck %s
diff --git a/llvm/test/tools/llvm-lto/cspgo-thinlto-use.ll b/llvm/test/tools/llvm-lto/cspgo-thinlto-use.ll
new file mode 100644
index 0000000000000..37d4b5290b3de
--- /dev/null
+++ b/llvm/test/tools/llvm-lto/cspgo-thinlto-use.ll
@@ -0,0 +1,20 @@
+; REQUIRES: aarch64-registered-target
+;
+; RUN: llvm-profdata merge %S/Inputs/cspgo-cs.proftext -o %t.profdata
+; RUN: opt -module-summary %s -o %t.o
+; RUN: llvm-lto -thinlto-action=optimize \
+; RUN:   -cs-profile-path=%t.profdata \
+; RUN:   -o %t.opt.bc %t.o
+; RUN: llvm-dis %t.opt.bc -o - | FileCheck %s
+;
+; CHECK: @main(){{.*}} !prof [[PROF:![0-9]+]]
+; CHECK: CSProfileSummary
+; CHECK: [[PROF]] = !{!"function_entry_count", i64 100000}
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "arm64-apple-macosx14.0.0"
+
+define i32 @main() {
+entry:
+  ret i32 0
+}
diff --git a/llvm/test/tools/llvm-lto/cspgo-use.ll b/llvm/test/tools/llvm-lto/cspgo-use.ll
new file mode 100644
index 0000000000000..acecb05beeedb
--- /dev/null
+++ b/llvm/test/tools/llvm-lto/cspgo-use.ll
@@ -0,0 +1,18 @@
+; REQUIRES: aarch64-registered-target
+;
+; RUN: llvm-profdata merge %S/Inputs/cspgo-cs.proftext -o %t.profdata
+; RUN: llvm-as %s -o %t.o
+; RUN: llvm-lto -cs-profile-path=%t.profdata \
+; RUN:   -exported-symbol=_main -o %t.lto.o %t.o
+; RUN: llvm-nm %t.lto.o | FileCheck %s
+;
+; CHECK-NOT: __profc_
+; CHECK: _main
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "arm64-apple-macosx14.0.0"
+
+define i32 @main() {
+entry:
+  ret i32 0
+}



More information about the cfe-commits mailing list