[clang] [llvm] [fatlto] Use the ThinLTO default pipeline for FatLTO (PR #134434)

Paul Kirth via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 4 11:55:14 PDT 2025


https://github.com/ilovepi created https://github.com/llvm/llvm-project/pull/134434

When coroutines are used w/ both -ffat-lto-objects and -flto=thin,
the coroutine passes are not added to the optimization pipelines.
Instead, just use the default ThinLTO pipeline to generate the ELF.

Fixes #134409.

>From 7bf068a99fe0ed84df2798f4808a949ecf5fdc0d Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Fri, 4 Apr 2025 11:49:02 -0700
Subject: [PATCH] [fatlto] Use the ThinLTO default pipeline for FatLTO

When coroutines are used w/ both -ffat-lto-objects and -flto=thin,
the coroutine passes are not added to the optimization pipelines.
Instead, just use the default ThinLTO pipeline to generate the ELF.

Fixes #134409.
---
 clang/test/CodeGenCoroutines/pr134409.cpp | 42 +++++++++++++++++++++++
 llvm/lib/Passes/PassBuilderPipelines.cpp  | 11 ++++--
 2 files changed, 50 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenCoroutines/pr134409.cpp

diff --git a/clang/test/CodeGenCoroutines/pr134409.cpp b/clang/test/CodeGenCoroutines/pr134409.cpp
new file mode 100644
index 0000000000000..3f3d95e191594
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/pr134409.cpp
@@ -0,0 +1,42 @@
+// An end-to-end test to make sure coroutine passes are added for thinlto.
+
+// RUN: %clang_cc1 -std=c++23 -ffat-lto-objects -flto=thin -emit-llvm %s -O3 -o - \
+// RUN:  | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+class BasicCoroutine {
+public:
+    struct Promise {
+        BasicCoroutine get_return_object() { return BasicCoroutine {}; }
+
+        void unhandled_exception() noexcept { }
+
+        void return_void() noexcept { }
+
+        std::suspend_never initial_suspend() noexcept { return {}; }
+        std::suspend_never final_suspend() noexcept { return {}; }
+    };
+    using promise_type = Promise;
+};
+
+// COM: match the embedded module, so we don't match something in it by accident.
+// CHECK: @llvm.embedded.object = {{.*}}
+// CHECK: @llvm.compiler.used = {{.*}}
+
+BasicCoroutine coro() {
+// CHECK: define {{.*}} void @_Z4corov() {{.*}} {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: ret void
+// CHECK-NEXT: }
+    co_return;
+}
+
+int main() {
+// CHECK: define {{.*}} i32 @main() {{.*}} {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: ret i32 0
+// CHECK-NEXT: }
+    coro();
+}
+
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index a18b36ba40754..4b15e0fb5c2a7 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -1688,10 +1688,15 @@ PassBuilder::buildFatLTODefaultPipeline(OptimizationLevel Level, bool ThinLTO,
   MPM.addPass(
       LowerTypeTestsPass(nullptr, nullptr, lowertypetests::DropTestKind::All));
 
-  // Use the ThinLTO post-link pipeline with sample profiling
-  if (ThinLTO && PGOOpt && PGOOpt->Action == PGOOptions::SampleUse)
+  // ModuleSimplification does not run the coroutine passes for ThinLTOPreLink,
+  // so we need the coroutine passes to run for ThinLTO builds, otherwise they
+  // will miscompile.
+  if (ThinLTO) {
+    // TODO: determine how to only run the ThinLTODefaultPipeline when using
+    // sample profiling. Ideally, we'd be able to still use the module
+    // optimization pipeline, with additional cleanups for coroutines.
     MPM.addPass(buildThinLTODefaultPipeline(Level, /*ImportSummary=*/nullptr));
-  else {
+  } else {
     // otherwise, just use module optimization
     MPM.addPass(
         buildModuleOptimizationPipeline(Level, ThinOrFullLTOPhase::None));



More information about the llvm-commits mailing list