We shouldn't access coro frame after returning from await_suspend() and before llvm.coro.suspend().
Make sure we always hoist conditional cleanup markers when inside the await.suspend block.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Thanks for working on this Wei!
Can you please add a testcase?
clang/lib/CodeGen/CodeGenFunction.h | ||
---|---|---|
336 | Should this live inside CGCoroData instead? |
The test should be required here.
clang/lib/CodeGen/CGExpr.cpp | ||
---|---|---|
544–547 | We should add comment to explain why we add a forward path for coroutines. |
BTW, what is the conclusion for the concern about sanitizers? Would change make sanitizers to perform false positive diagnostics?
This change is limited to the await.suspend block, which only does codegen for awaiter.await_suspend(coroutine_handle<p>::from_promise(p)). In this case, I think the only asan check removed by the change is the conditional marker for cleanup the temporary coroutine_handler used as the parameter of await_suspend, so my understanding is that it shouldn't affect asan diagnostic.
To show the difference it makes to the source from issue #59181
Before:
%ref.tmp25 = alloca %"struct.std::__1::coroutine_handle.6", align 8 ... // asan check inserted here bool cond_cleanup_marker = false; if (cond) { // get awaiter ... if (!awaiter.await_ready()) { call void @llvm.lifetime.start.p0(i64 8, ptr %ref.tmp25) // asan check inserted here cond_cleanup_marker = true; // store handler to %ref.tmp25 ... awaiter.await_suspend(); // asan check inserted here if (cond_cleanup_marker) call void @llvm.lifetime.end.p0(i64 8, ptr %ref.tmp25) call i8 @llvm.coro.suspend(...) ... } ... } else { ... } ... lpad: ... // asan check inserted here if (cond_cleanup_marker) call void @llvm.lifetime.end.p0(i64 8, ptr %ref.tmp25)
The issue is only reproduced in -O0, because `cond_cleanup_marker is optimized away in -O1 or up opt level.
After:
%ref.tmp25 = alloca %"struct.std::__1::coroutine_handle.6", align 8 ... call void @llvm.lifetime.start.p0(i64 8, ptr %ref.tmp25) if (cond) { ... if (!awaiter.await_ready()) { // store handler to %ref.tmp25 ... awaiter.await_suspend(); call void @llvm.lifetime.end.p0(i64 8, ptr %ref.tmp25) call i8 @llvm.coro.suspend(...) ... } ... } else { ... } ... lpad: call void @llvm.lifetime.end.p0(i64 8, ptr %ref.tmp25)
This also matches with the IR without asan.
clang/lib/CodeGen/CGExpr.cpp | ||
---|---|---|
544–547 | Will do | |
clang/lib/CodeGen/CodeGenFunction.h | ||
336 | CGCoroData is forward declared here, so it does not know what's inside. |
We should add comment to explain why we add a forward path for coroutines.