[fatlto] Add coroutine passes when using FatLTO with ThinLTO (#134434)

When coroutines are used w/ both -ffat-lto-objects and -flto=thin,
the coroutine passes are not added to the optimization pipelines.
Ensure they are added before ModuleOptimization to generate a
working ELF object.

Fixes #134409.
This commit is contained in:
Paul Kirth 2025-04-07 08:41:49 -07:00 committed by GitHub
parent b09daa4b23
commit 268c065eab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 0 deletions

View File

@ -0,0 +1,43 @@
// An end-to-end test to make sure coroutine passes are added for thinlto.
// REQUIRES: x86-registered-target
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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: tail call void @_Z4corov()
// CHECK-NEXT: ret i32 0
// CHECK-NEXT: }
coro();
}

View File

@ -1692,6 +1692,19 @@ PassBuilder::buildFatLTODefaultPipeline(OptimizationLevel Level, bool ThinLTO,
if (ThinLTO && PGOOpt && PGOOpt->Action == PGOOptions::SampleUse)
MPM.addPass(buildThinLTODefaultPipeline(Level, /*ImportSummary=*/nullptr));
else {
// 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: replace w/ buildCoroWrapper() when it takes phase and level into
// consideration.
CGSCCPassManager CGPM;
CGPM.addPass(CoroSplitPass(Level != OptimizationLevel::O0));
CGPM.addPass(CoroAnnotationElidePass());
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));
MPM.addPass(CoroCleanupPass());
}
// otherwise, just use module optimization
MPM.addPass(
buildModuleOptimizationPipeline(Level, ThinOrFullLTOPhase::None));