Skip to content

[Pipelines] Do not run CoroSplit and CoroCleanup in LTO pre-link pipeline #90310

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

Merged
merged 2 commits into from
Apr 29, 2024

Conversation

apolloww
Copy link
Contributor

@apolloww apolloww commented Apr 27, 2024

Skip CoroSplit and CoroCleanup in LTO pre-link pipeline so that CoroElide can happen after callee coroutine is imported into caller's module in ThinLTO.

@llvmbot llvmbot added clang Clang issues not falling into any other category coroutines C++20 coroutines labels Apr 27, 2024
@llvmbot
Copy link
Member

llvmbot commented Apr 27, 2024

@llvm/pr-subscribers-coroutines

@llvm/pr-subscribers-clang

Author: Wei Wang (apolloww)

Changes

Skip CoroSplit and CoroCleanup in pre-lto pipeline so that CoroElide can happen after callee coroutine is imported into caller's module in ThinLTO.


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

6 Files Affected:

  • (added) clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp (+77)
  • (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+8-4)
  • (modified) llvm/test/Other/new-pm-defaults.ll (+4-2)
  • (modified) llvm/test/Other/new-pm-thinlto-prelink-defaults.ll (-2)
  • (modified) llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll (-2)
  • (modified) llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll (-2)
diff --git a/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp b/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp
new file mode 100644
index 00000000000000..293aef6781677f
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp
@@ -0,0 +1,77 @@
+// This tests that the coroutine elide optimization could happen succesfully with ThinLTO.
+// This test is adapted from coro-elide.cpp and splits functions into two files.
+//
+// RUN: split-file %s %t
+// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-callee.cpp -o coro-elide-callee.o
+// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-caller.cpp -o coro-elide-caller.o
+// RUN: llvm-lto -thinlto coro-elide-callee.o coro-elide-caller.o -o summary
+// RUN: %clang_cc1 -O2 -x ir coro-elide-caller.o -fthinlto-index=summary.thinlto.bc -emit-llvm -o - | FileCheck %s
+
+//--- coro-elide-task.h
+#pragma once
+#include "Inputs/coroutine.h"
+
+struct Task {
+  struct promise_type {
+    struct FinalAwaiter {
+      bool await_ready() const noexcept { return false; }
+      template <typename PromiseType>
+      std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
+        if (!h)
+          return std::noop_coroutine();
+        return h.promise().continuation;
+      }
+      void await_resume() noexcept {}
+    };
+    Task get_return_object() noexcept {
+      return std::coroutine_handle<promise_type>::from_promise(*this);
+    }
+    std::suspend_always initial_suspend() noexcept { return {}; }
+    FinalAwaiter final_suspend() noexcept { return {}; }
+    void unhandled_exception() noexcept {}
+    void return_value(int x) noexcept {
+      _value = x;
+    }
+    std::coroutine_handle<> continuation;
+    int _value;
+  };
+
+  Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
+  ~Task() {
+    if (handle)
+      handle.destroy();
+  }
+
+  struct Awaiter {
+    bool await_ready() const noexcept { return false; }
+    void await_suspend(std::coroutine_handle<void> continuation) noexcept {}
+    int await_resume() noexcept {
+      return 43;
+    }
+  };
+
+  auto operator co_await() {
+    return Awaiter{};
+  }
+
+private:
+  std::coroutine_handle<promise_type> handle;
+};
+
+//--- coro-elide-callee.cpp
+#include "coro-elide-task.h"
+Task task0() {
+  co_return 43;
+}
+
+//--- coro-elide-caller.cpp
+#include "coro-elide-task.h"
+
+Task task0();
+
+Task task1() {
+  co_return co_await task0();
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z5task1v.resume
+// CHECK-NOT: {{.*}}_Znwm
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 90ba3b541553e2..4ca8aff479b0ff 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -963,7 +963,8 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
   MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(
       RequireAnalysisPass<ShouldNotRunFunctionPassesAnalysis, Function>()));
 
-  MainCGPipeline.addPass(CoroSplitPass(Level != OptimizationLevel::O0));
+  if (!isLTOPreLink(Phase))
+    MainCGPipeline.addPass(CoroSplitPass(Level != OptimizationLevel::O0));
 
   // Make sure we don't affect potential future NoRerun CGSCC adaptors.
   MIWP.addLateModulePass(createModuleToFunctionPassAdaptor(
@@ -1005,8 +1006,10 @@ PassBuilder::buildModuleInlinerPipeline(OptimizationLevel Level,
       buildFunctionSimplificationPipeline(Level, Phase),
       PTO.EagerlyInvalidateAnalyses));
 
-  MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(
-      CoroSplitPass(Level != OptimizationLevel::O0)));
+
+  if (!isLTOPreLink(Phase))
+    MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(
+        CoroSplitPass(Level != OptimizationLevel::O0)));
 
   return MPM;
 }
@@ -1183,7 +1186,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   // and argument promotion.
   MPM.addPass(DeadArgumentEliminationPass());
 
-  MPM.addPass(CoroCleanupPass());
+  if (!isLTOPreLink(Phase))
+    MPM.addPass(CoroCleanupPass());
 
   // Optimize globals now that functions are fully simplified.
   MPM.addPass(GlobalOptPass());
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index 51fb93daa4dfa6..ebfed7b687e2ce 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -224,12 +224,14 @@
 ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis
-; CHECK-O-NEXT: Running pass: CoroSplitPass
+; CHECK-DEFAULT-NEXT: Running pass: CoroSplitPass
+; CHECK-LTO-NOT: Running pass: CoroSplitPass
 ; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
 ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
-; CHECK-O-NEXT: Running pass: CoroCleanupPass
+; CHECK-DEFAULT-NEXT: Running pass: CoroCleanupPass
+; CHECK-LTO-NOT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
 ; CHECK-DEFAULT-NEXT: Running pass: EliminateAvailableExternallyPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index 6486639e07b49c..e2fd74306f80c3 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -183,12 +183,10 @@
 ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis
-; CHECK-O-NEXT: Running pass: CoroSplitPass
 ; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
 ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
-; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
 ; CHECK-EXT: Running pass: {{.*}}::Bye
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
index 09f9f0f48baddb..13a63bbe4d9cad 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -182,12 +182,10 @@
 ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis
-; CHECK-O-NEXT: Running pass: CoroSplitPass
 ; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
 ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
-; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on bar
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
index 47bdbfd2d357d4..3130da86fa9908 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -147,12 +147,10 @@
 ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis
-; CHECK-O-NEXT: Running pass: CoroSplitPass
 ; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
 ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
 ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
-; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo

@apolloww apolloww changed the title [Pipelines] Do not run CoroSplit and CoroCleanup in pre-lto pipeline [Pipelines] Do not run CoroSplit and CoroCleanup in LTO pre-link pipeline Apr 27, 2024
@apolloww apolloww requested a review from ChuanqiXu9 April 27, 2024 03:00
Copy link

github-actions bot commented Apr 27, 2024

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

Copy link
Member

@ChuanqiXu9 ChuanqiXu9 left a comment

Choose a reason for hiding this comment

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

Feel not bad.

@apolloww apolloww merged commit cd68d7b into llvm:main Apr 29, 2024
@sbc100
Copy link
Collaborator

sbc100 commented Apr 30, 2024

This change seems to causing the auto-roller for emscripten to fail due to a crash in LTO. See https://ci.chromium.org/ui/p/emscripten-releases/builders/luci.emscripten-releases.ci/linux-test-suites

PromoteIntegerOperand Op #3: t22: i32,ch = llvm.coro.subfn.addr t26, TargetConstant:i32<54>, t9, Constant:i8<0>

LLVM ERROR: Do not know how to promote this operator's operand!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /usr/local/google/home/sbc/dev/wasm/llvm-build/bin/wasm-ld -o test_val.wasm /tmp/emscripten_temp_54ryesgk/test_val_0.o -lembind-rtti -L/usr/local/google/home/sbc/dev/wasm/emscripten/cache/sysroot/lib/wasm32-emscripten/lto -lGL-getprocaddr -lal -lhtml5 -lstubs -lnoexit -lc -ldlmalloc -lcompiler_rt -lc++-noexcept -lc++abi-noexcept -lsockets -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmpgksompwplibemscripten_js_symbols.so --strip-debug -u__cxa_atexit --export=_emscripten_stack_alloc --export=__getTypeName --export=__get_temp_ret --export=__set_temp_ret --export=__wasm_call_ctors --export=emscripten_stack_get_current --export=_emscripten_stack_restore --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-table -z stack-size=65536 --no-growable-memory --initial-heap=16777216 --no-entry --table-base=1 --global-base=1024
1.	Running pass 'Function Pass Manager' on module 'ld-temp.o'.
2.	Running pass 'WebAssembly Instruction Selection' on function '@_emval_coro_resume'
 #0 0x00007f187817e048 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.19.0git+0x17e048)
 #1 0x00007f187817bcbe llvm::sys::RunSignalHandlers() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.19.0git+0x17bcbe)
 #2 0x00007f187817e70d SignalHandler(int) Signals.cpp:0:0
 #3 0x00007f1877d7b510 (/lib/x86_64-linux-gnu/libc.so.6+0x3c510)
 #4 0x00007f1877dc916c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007f1877d7b472 raise ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x00007f1877d654b2 abort ./stdlib/abort.c:81:7
 #7 0x00007f18780d497a llvm::report_fatal_error(llvm::Twine const&, bool) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.19.0git+0xd497a)
 #8 0x00007f18780d47c6 (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.19.0git+0xd47c6)
 #9 0x00007f18749713e3 (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/../lib/libLLVMSelectionDAG.so.19.0git+0x1713e3)
#10 0x00007f187498ff79 llvm::DAGTypeLegalizer::run() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/../lib/libLLVMSelectionDAG.so.19.0git+0x18ff79)
#11 0x00007f18749953f7 llvm::SelectionDAG::LegalizeTypes() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/../lib/libLLVMSelectionDAG.so.19.0git+0x1953f7)
#12 0x00007f1874acba4f llvm::SelectionDAGISel::CodeGenAndEmitDAG() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/../lib/libLLVMSelectionDAG.so.19.0git+0x2cba4f)
#13 0x00007f1874aca101 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/../lib/libLLVMSelectionDAG.so.19.0git+0x2ca101)
#14 0x00007f1874ac6a63 llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/../lib/libLLVMSelectionDAG.so.19.0git+0x2c6a63)
#15 0x00007f1875359b94 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMCodeGen.so.19.0git+0x359b94)
#16 0x00007f1875e97a1a llvm::FPPassManager::runOnFunction(llvm::Function&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMCore.so.19.0git+0x297a1a)
#17 0x00007f1875e9fe22 llvm::FPPassManager::runOnModule(llvm::Module&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMCore.so.19.0git+0x29fe22)
#18 0x00007f1875e9850f llvm::legacy::PassManagerImpl::run(llvm::Module&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMCore.so.19.0git+0x29850f)
#19 0x00007f1877c24c62 codegen(llvm::lto::Config const&, llvm::TargetMachine*, std::__2::function<llvm::Expected<std::__2::unique_ptr<llvm::CachedFileStream, std::__2::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, unsigned int, llvm::Module&, llvm::ModuleSummaryIndex const&) LTOBackend.cpp:0:0
#20 0x00007f1877c23daf llvm::lto::backend(llvm::lto::Config const&, std::__2::function<llvm::Expected<std::__2::unique_ptr<llvm::CachedFileStream, std::__2::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, unsigned int, llvm::Module&, llvm::ModuleSummaryIndex&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMLTO.so.19.0git+0x35daf)
#21 0x00007f1877c1180c llvm::lto::LTO::runRegularLTO(std::__2::function<llvm::Expected<std::__2::unique_ptr<llvm::CachedFileStream, std::__2::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMLTO.so.19.0git+0x2380c)
#22 0x00007f1877c10c49 llvm::lto::LTO::run(std::__2::function<llvm::Expected<std::__2::unique_ptr<llvm::CachedFileStream, std::__2::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, std::__2::function<llvm::Expected<std::__2::function<llvm::Expected<std::__2::unique_ptr<llvm::CachedFileStream, std::__2::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>> (unsigned int, llvm::StringRef, llvm::Twine const&)>) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libLLVMLTO.so.19.0git+0x22c49)
#23 0x00007f1878598575 lld::wasm::BitcodeCompiler::compile() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/liblldWasm.so.19.0git+0x36575)
#24 0x00007f18785a286a lld::wasm::SymbolTable::compileBitcodeFiles() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/liblldWasm.so.19.0git+0x4086a)
#25 0x00007f187857dd0c lld::wasm::(anonymous namespace)::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) Driver.cpp:0:0
#26 0x00007f187857ae03 lld::wasm::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/liblldWasm.so.19.0git+0x18e03)
#27 0x00007f1878918a22 lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/liblldCommon.so.19.0git+0xca22)
#28 0x000055d414e4e6c3 lld_main(int, char**, llvm::ToolContext const&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/wasm-ld+0x26c3)
#29 0x000055d414e4ead7 main (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/wasm-ld+0x2ad7)
#30 0x00007f1877d666ca __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#31 0x00007f1877d66785 call_init ./csu/../csu/libc-start.c:128:20
#32 0x00007f1877d66785 __libc_start_main ./csu/../csu/libc-start.c:347:5
#33 0x000055d414e4e29a _start (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/wasm-ld+0x229a)

@sbc100
Copy link
Collaborator

sbc100 commented Apr 30, 2024

@apolloww
Copy link
Contributor Author

hmm, this looks like llvm.coro.subfn.addr is not lowered before codegen starts. And this is for wasm and MonoLTO. Let me check.

@apolloww
Copy link
Contributor Author

apolloww commented Apr 30, 2024

OK, so Mono LTO backend pipeline does not have any coro passes. We can add coroutine passes to buildLTODefaultPipeline, but I am not sure how big the use case it is (AFAIK, most builds use ThinLTO.). For now, I'll limit the tuning to ThinLTO only. Will send an update soon.

//
// RUN: split-file %s %t
// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-callee.cpp -o coro-elide-callee.o
// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-caller.cpp -o coro-elide-caller.o
Copy link
Collaborator

Choose a reason for hiding this comment

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

This test case requires an X86 backend. Those of us who have embraced the AArch64-only future are encountering test errors when running this test.

(maybe it's just me)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just landed #90672 to limit the test to x86 linux targets. Did that fix the test failure?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yep! Thank you for the quick fix.

apolloww added a commit that referenced this pull request May 1, 2024
…nk pipeline (#90690)

Follow up to #90310, limit the tune up only to ThinLTO pre-link as
coroutine passes are not in MonoLTO backend
@apolloww apolloww deleted the coro-thinlto branch May 1, 2024 16:41
rnk added a commit that referenced this pull request May 10, 2024
…ink pipeline (#90310)" and related patches

This change is incorrect when thinlto and asan are enabled, and this can
be observed by adding `-fsanitize=address` to the provided
coro-elide-thinlto.cpp test. It results in the error "Coroutines cannot
handle non static allocas yet", and ASan introduces a dynamic alloca.

In other words, we must preserve the invariant that CoroSplit runs
before ASan. If we move CoroSplit to the post post-link compile stage,
ASan has to be moved to the post-link compile stage first.  It would
also be correct to make CoroSplit handle dynamic allocas so the pass
ordering doesn't matter, but sanitizer instrumentation really ought to
be last, after coroutine splitting.

This reverts commit bafc5f4.
This reverts commit b1b1bfa.
This reverts commit 0232b77.
This reverts commit fb2d305.
This reverts commit 1cb3371.
This reverts commit cd68d7b.
@rnk
Copy link
Collaborator

rnk commented May 10, 2024

Hey, I went ahead and reverted this patch in aa0776d because it is incompatible with ASan ThinLTO builds. I think you have uncovered a latent issue in the ASan + thinlto configuration that is not really the fault of this change, but I didn't see a path to fixing forward in the short term, so I reverted this to get this configuration working again.

I think the next step is to follow up with @vitalybuka to figure out how to reorder the ASan instrumentation pass in the thinlto pipeline.

@apolloww
Copy link
Contributor Author

Hey, I went ahead and reverted this patch in aa0776d because it is incompatible with ASan ThinLTO builds. I think you have uncovered a latent issue in the ASan + thinlto configuration that is not really the fault of this change, but I didn't see a path to fixing forward in the short term, so I reverted this to get this configuration working again.

I think the next step is to follow up with @vitalybuka to figure out how to reorder the ASan instrumentation pass in the thinlto pipeline.

Oh, that's unfortunate. Could you explain more about the issue?

@vitalybuka
Copy link
Collaborator

Could you explain more about the issue?

  1. Without ThinLTO Asan runs after most optimizations including these coro passes.
  2. With ThinLTO Asan runs at pre-link, so coro passes follow the Asan.

There are other benefits to moving Asan to post-link ThinLTO stage, but it's likely will not trivial. Last time I tried, it caused other miscompiles which needed investigation.

I still hope to get back to that sometime later, but there is no particular timeline.

If this patch is important to land soon I see the following options:

  1. get reproducer and investigate what exactly is causing the crash. maybe something trivial.
  2. fallback to existing behavior if sanitizers are present

Depending on the nature of the issues, this may apply to other sanitizers.

@rnk
Copy link
Collaborator

rnk commented May 14, 2024

To be clear, you can repro the issue with the test case you have, just add -fsanitize=address to flags to repro.

@apolloww
Copy link
Contributor Author

Got some time to revisit this.

I tried the repro, and think there are couple issues here.

function import can't handle non-function aliasee

After adding -fsanitize=address to the pre-link compilations in the test case, it triggers an ICE during function import https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/IPO/FunctionImport.cpp#L1675. The code always expects the aliasee to be a function (I don't know how the assumption was made in the first place), but gets a variable instead. The global variable in this case is NoopCoro.Frame.Const, which is created during the CoroEarly pass from lowering the llvm.coro.noop intrinsic. AddressSanitizer pass then creates an alias for it.

In fact, this has nothing to do with coroutines. Asan would create alias for other GVs as well and trigger the same crash. For example, the following test case (mostly copied from llvm-project/llvm/test/Instrumentation/AddressSanitizer/local_alias.ll) also repro the crash:

; RUN: split-file %s %t
; RUN: opt -passes=asan,name-anon-globals -module-summary %t/main.ll -o main.bc
; RUN: opt -passes=asan,name-anon-globals -module-summary %t/alias.ll -o alias.bc
; RUN: llvm-lto --thinlto main.bc local_alias.bc -o summary
; RUN: %clang -O2 -x ir main.bc -fthinlto-index=summary.thinlto.bc

;--- main.ll
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

declare i32 @foo(i32)

define i32 @main() local_unnamed_addr {
    %1 = call i32 @foo(i32 1)
    ret i32 %1
}

;--- alias.ll
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@a = dso_local global [2 x i32] zeroinitializer, align 4
@b = private global [2 x i32] zeroinitializer, align 4
@c = internal global [2 x i32] zeroinitializer, align 4
@_ZL1d = unnamed_addr global [2 x i32] zeroinitializer, align 4

; Function Attrs: nounwind sanitize_address uwtable
define i32 @foo(i32 %M) {
entry:
  %M.addr = alloca i32, align 4
  store i32 %M, ptr %M.addr, align 4
  store volatile i32 6, ptr getelementptr inbounds ([2 x i32], ptr @a, i64 2, i64 0), align 4
  %0 = load i32, ptr %M.addr, align 4
  %idxprom = sext i32 %0 to i64
  %arrayidx = getelementptr inbounds [2 x i32], ptr @a, i64 0, i64 %idxprom
  %1 = load volatile i32, ptr %arrayidx, align 4
  ret i32 %1
}

In real use case though, I doubt we'd see this exact crash because the alias wouldn't be imported as it has private linkage (not 100% sure). why it shows up here is because after llvm-lto --thinlto ... that generates the combined index summary, it goes straight into function import without doing other thinlink steps (i.e. get rid of dead symbols) to trim down the import list. If we do all the steps properly, the alias wouldn't' be imported. So if we run the above example as, follow, it runs OK

; RUN: split-file %s %t
; RUN: opt -passes=asan,name-anon-globals -module-summary %t/main.ll -o main.bc
; RUN: opt -passes=asan,name-anon-globals -module-summary %t/alias.ll -o alias.bc
; RUN: llvm-lto --thinlto main.bc local_alias.bc -o summary
; RUN: llvm-lto --thinlto-action=import --thinlto-index=summary.thinlto.bc main.bc -o main.thinlto.imported.bc
; RUN: llvm-lto --thinlto-action=optimize main.thinlto.imported.bc

Either way, I think this issue can be resolve by handling non-function aliasee in function import.

AddressSanitizer breaks pre-split coroutine function structure

This is a more serious issue and I don't have an easy solution for it. And this may be the crash you observed.

Even if we resolves the first issue, there is a second crash inside CoroSplit pass

LLVM ERROR: Coroutines cannot handle non static allocas yet
...
   While splitting coroutine ...

This is because asan adds a bunch of instructions to update promise alloca (with poisoned values?) and this breaks the assumption that the second parameter of llvm.coro.id intrinsic is an alloc for the promise object on the stack. This comes down to the pipeline ordering that asan pass runs after most other passes including coro passes. When we shuffle some passes after asan, it can create problems. I think it makes sense to run asan after other passes to unblock them. For ThinLTO, the best place would be the end of post-link pipeline.

For now, I am going to make asan skip both pre-split coroutines and noop coroutine frame #99415. After this, I think the pipeline change can work.

apolloww added a commit that referenced this pull request Jul 30, 2024
…line (#100205)

This is re-land of #90310 after making asan skip pre-split coroutines in
#99415.

Skip CoroSplit and CoroCleanup in LTO pre-link pipeline so that
CoroElide can happen after callee coroutine is imported into caller's
module in ThinLTO.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category coroutines C++20 coroutines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants