[clang] [llvm] [CoroSplit] Directly use live SSA values instead of reloading from frame (PR #218309)
Weibo He via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 21:35:07 PDT 2026
https://github.com/NewSigma updated https://github.com/llvm/llvm-project/pull/218309
>From ad992a806d847343379fa20fa7a0c16d691b9df3 Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Fri, 21 Aug 2026 20:45:42 +0800
Subject: [PATCH 1/2] [Coroutines] Directly use live SSA values instead of
reloading from frame
---
.../coro-suspend-cleanups.cpp | 5 +-
llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 65 ++++++++++----
llvm/test/Transforms/Coroutines/coro-async.ll | 8 +-
.../Coroutines/coro-catchswitch-cleanuppad.ll | 12 +--
.../Transforms/Coroutines/coro-catchswitch.ll | 3 +-
llvm/test/Transforms/Coroutines/coro-frame.ll | 4 +-
.../Coroutines/coro-retcon-once-value.ll | 8 +-
.../Coroutines/coro-retcon-once-value2.ll | 8 +-
.../Coroutines/coro-retcon-resume-values.ll | 10 +--
.../Coroutines/coro-retcon-resume-values2.ll | 10 +--
.../test/Transforms/Coroutines/coro-retcon.ll | 10 +--
.../Transforms/Coroutines/coro-spill-ramp.ll | 84 +++++++++++++++++++
12 files changed, 172 insertions(+), 55 deletions(-)
create mode 100644 llvm/test/Transforms/Coroutines/coro-spill-ramp.ll
diff --git a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
index 6c05d02d0642f..fa5d74e743b3f 100644
--- a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
+++ b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
@@ -59,8 +59,9 @@ coroutine ArrayInitCoro() {
// CHECK: br label %cleanup{{.*}}
// CHECK: await.ready:
- // CHECK-NEXT: %arrayinit.element.reload.addr = getelementptr inbounds i8, ptr %0, i64 48
- // CHECK-NEXT: %arrayinit.element.reload = load ptr, ptr %arrayinit.element.reload.addr, align 8
+ // CHECK-NEXT: br label %await.ready.after.spill
+
+ // CHECK: await.ready.after.spill:
// CHECK-NEXT: call void @_ZN7Awaiter12await_resumeEv
// CHECK-NEXT: store i1 false, ptr %cleanup.isactive.reload.addr, align 1
// CHECK-NEXT: br label %cleanup{{.*}}.from.await.ready
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index c59e65f270a23..2b5fec3ffaad3 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -1086,20 +1086,48 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
Builder.SetInsertPoint(coro::getSpillInsertionPt(Shape, Def, DT));
createStoreIntoFrame(Builder, Def, ByValTy, Shape, FrameData);
-
- BasicBlock *CurrentBlock = nullptr;
+ // Before insertSpills():
+ // before.spill:
+ // ; use %def
+ //
+ // After insertSpills():
+ // before.spill:
+ // (phis)
+ // %InRamp = call i1 @llvm.coro.is_in_ramp()
+ // br i1 %InRamp, label %after.spill, label %ssa.spill
+ //
+ // ssa.spill:
+ // ; gep and load from frame
+ // br label %after.spill
+ //
+ // after.spill:
+ // %MaybeReload = phi ptr [%def, %before.spill], [%reload, %ssa.spill]
+ // ; use %MaybeReload
+ BasicBlock *BeforeSpillBB = nullptr;
+ BasicBlock *SpillBB = nullptr;
+ BasicBlock *AfterSpillBB = nullptr;
Value *CurrentReload = nullptr;
for (auto *U : E.second) {
// If we have not seen the use block, create a load instruction to reload
// the spilled value from the coroutine frame. Populates the Value pointer
// reference provided with the frame GEP.
- if (CurrentBlock != U->getParent()) {
- CurrentBlock = U->getParent();
- Builder.SetInsertPoint(CurrentBlock,
- CurrentBlock->getFirstInsertionPt());
-
- auto *GEP = createGEPToFramePointer(FrameData, Builder, Shape, E.first);
- GEP->setName(E.first->getName() + Twine(".reload.addr"));
+ if (BeforeSpillBB != U->getParent()) {
+ BeforeSpillBB = U->getParent();
+ AfterSpillBB = BeforeSpillBB->splitBasicBlock(
+ BeforeSpillBB->getFirstInsertionPt(),
+ BeforeSpillBB->getName() + Twine(".after.spill"));
+ SpillBB = BasicBlock::Create(
+ C, BeforeSpillBB->getName() + Twine(".spill"), F, AfterSpillBB);
+
+ BeforeSpillBB->getTerminator()->eraseFromParent();
+ Builder.SetInsertPoint(BeforeSpillBB);
+ auto *InRamp = Builder.CreateIntrinsic(Intrinsic::coro_is_in_ramp, {});
+ Builder.CreateCondBr(InRamp, AfterSpillBB, SpillBB);
+ Shape.CoroIsInRampInsts.push_back(cast<CoroIsInRampInst>(InRamp));
+
+ Builder.SetInsertPoint(SpillBB);
+ auto *GEP = createGEPToFramePointer(FrameData, Builder, Shape, Def);
+ GEP->setName(Def->getName() + Twine(".reload.addr"));
if (ByValTy) {
CurrentReload = GEP;
} else {
@@ -1111,6 +1139,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
LI->setMetadata(LLVMContext::MD_tbaa, TBAATag);
CurrentReload = LI;
}
+ Builder.CreateBr(AfterSpillBB);
TinyPtrVector<DbgVariableRecord *> DVRs = findDbgRecordsThroughLoads<
DbgVariableRecord::LocationType::Declare>(*F, Def);
@@ -1123,8 +1152,8 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
ValueAsMetadata::get(CurrentReload), DDI->getVariable(),
DDI->getExpression(), DDI->getDebugLoc(),
DbgVariableRecord::LocationType::Declare);
- Builder.GetInsertPoint()->getParent()->insertDbgRecordBefore(
- NewDVR, Builder.GetInsertPoint());
+ BeforeSpillBB->insertDbgRecordBefore(
+ NewDVR, BeforeSpillBB->getFirstInsertionPt());
// This dbg.declare is for the main function entry point. It
// will be deleted in all coro-split functions.
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
@@ -1150,14 +1179,20 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
DDI->getExpression(), DDI->getDebugLoc(),
Ty->isPointerTy() ? DbgVariableRecord::LocationType::Declare
: DbgVariableRecord::LocationType::Value);
- Builder.GetInsertPoint()->getParent()->insertDbgRecordBefore(
- NewDVR, Builder.GetInsertPoint());
+ BeforeSpillBB->insertDbgRecordBefore(
+ NewDVR, BeforeSpillBB->getFirstInsertionPt());
// This dbg.declare_value is for the main function entry point. It
// will be deleted in all coro-split functions.
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
};
for_each(DVRDeclareValues, SalvageOneCoro);
+ Builder.SetInsertPoint(AfterSpillBB->getFirstInsertionPt());
+ // No need to reload if the original SSA value is available
+ auto *MaybeReload = Builder.CreatePHI(Def->getType(), 2);
+ MaybeReload->addIncoming(CurrentReload, SpillBB);
+ MaybeReload->addIncoming(Def, BeforeSpillBB);
+
// If we have a single edge PHINode, remove it and replace it with a
// reload from the coroutine frame. (We already took care of multi edge
// PHINodes by normalizing them in the rewritePHIs function).
@@ -1165,14 +1200,14 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
assert(PN->getNumIncomingValues() == 1 &&
"unexpected number of incoming "
"values in the PHINode");
- PN->replaceAllUsesWith(CurrentReload);
+ PN->replaceAllUsesWith(MaybeReload);
PN->eraseFromParent();
continue;
}
// Replace all uses of CurrentValue in the current instruction with
// reload.
- U->replaceUsesOfWith(Def, CurrentReload);
+ U->replaceUsesOfWith(Def, MaybeReload);
// Instructions are added to Def's user list if the attached
// debug records use Def. Update those now.
for (DbgVariableRecord &DVR : filterDbgVars(U->getDbgRecordRange()))
diff --git a/llvm/test/Transforms/Coroutines/coro-async.ll b/llvm/test/Transforms/Coroutines/coro-async.ll
index 3454737820b7a..fd433a225143f 100644
--- a/llvm/test/Transforms/Coroutines/coro-async.ll
+++ b/llvm/test/Transforms/Coroutines/coro-async.ll
@@ -151,12 +151,12 @@ define void @my_async_function_pa(ptr %ctxt, ptr %task, ptr %actor) {
; CHECK: [[FRAME_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 128
; CHECK-O0: [[VECTOR_SPILL_ADDR:%.*]] = getelementptr inbounds i8, ptr {{.*}}, i64 32
; CHECK-O0: load <4 x double>, ptr [[VECTOR_SPILL_ADDR]], align 16
-; CHECK: [[CALLEE_CTXT_SPILL_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 160
-; CHECK: [[CALLEE_CTXT_RELOAD:%.*]] = load ptr, ptr [[CALLEE_CTXT_SPILL_ADDR]]
-; CHECK: [[ACTOR_RELOAD_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 152
-; CHECK: [[ACTOR_RELOAD:%.*]] = load ptr, ptr [[ACTOR_RELOAD_ADDR]]
; CHECK: [[ADDR1:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 144
; CHECK: [[ASYNC_CTXT_RELOAD:%.*]] = load ptr, ptr [[ADDR1]]
+; CHECK: [[ACTOR_RELOAD_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 152
+; CHECK: [[ACTOR_RELOAD:%.*]] = load ptr, ptr [[ACTOR_RELOAD_ADDR]]
+; CHECK: [[CALLEE_CTXT_SPILL_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 160
+; CHECK: [[CALLEE_CTXT_RELOAD:%.*]] = load ptr, ptr [[CALLEE_CTXT_SPILL_ADDR]]
; CHECK: [[ALLOCA_PRJ2:%.*]] = getelementptr inbounds nuw i8, ptr [[CALLER_CONTEXT]], i64 136
; CHECK: tail call void @llvm.coro.async.context.dealloc(ptr nonnull [[CALLEE_CTXT_RELOAD]])
; CHECK: [[VAL1:%.*]] = load i64, ptr [[FRAME_PTR]]
diff --git a/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll b/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll
index 945b364cbad70..c7890620fced7 100644
--- a/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll
+++ b/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll
@@ -81,18 +81,18 @@ cleanup2:
; CHECK: %1 = phi i8 [ 0, %handler2 ], [ 1, %catch.dispatch.2 ]
; CHECK: %2 = cleanuppad within %h1 []
; CHECK: %3 = icmp eq i8 %1, 0
-; CHECK: br i1 %3, label %cleanup2.from.handler2, label %cleanup2.from.catch.dispatch.2, !prof [[PROF1:![0-9]+]]
+; CHECK: br i1 %3, label %[[FROM_HANDLER:.+]], label %[[FROM_DISPATCH:.+]], !prof [[PROF1:![0-9]+]]
-; CHECK: cleanup2.from.handler2:
-; CHECK: %valueB.reload = load i32, ptr %valueB.spill.addr, align 4
+; CHECK: [[FROM_HANDLER]]:
+; CHECK: %valueB.reload = load i32, ptr %valueB.reload.addr, align 4
; CHECK: br label %cleanup2
-; CHECK: cleanup2.from.catch.dispatch.2:
-; CHECK: %valueA.reload = load i32, ptr %valueA.spill.addr, align 4
+; CHECK: [[FROM_DISPATCH]]:
+; CHECK: %valueA.reload = load i32, ptr %valueA.reload.addr, align 4
; CHECK: br label %cleanup2
; CHECK: cleanup2:
-; CHECK: %cleanupval2 = phi i32 [ %valueA.reload, %cleanup2.from.catch.dispatch.2 ], [ %valueB.reload, %cleanup2.from.handler2 ]
+; CHECK: %cleanupval2 = phi i32 [ %valueA.reload, %[[FROM_DISPATCH]] ], [ %valueB.reload, %[[FROM_HANDLER]] ]
; CHECK: call void @print(i32 %cleanupval2)
; CHECK: br label %cleanup
}
diff --git a/llvm/test/Transforms/Coroutines/coro-catchswitch.ll b/llvm/test/Transforms/Coroutines/coro-catchswitch.ll
index 776d2bfac2339..f09bb609e09da 100644
--- a/llvm/test/Transforms/Coroutines/coro-catchswitch.ll
+++ b/llvm/test/Transforms/Coroutines/coro-catchswitch.ll
@@ -38,8 +38,7 @@ define void @f(i1 %cond) presplitcoroutine personality i32 0 {
; CHECK-NEXT: store i1 false, ptr [[INDEX_ADDR3]], align 1
; CHECK-NEXT: br i1 false, label %[[RESUME:.*]], label %[[AFTERCOROEND]]
; CHECK: [[RESUME]]:
-; CHECK-NEXT: [[VAL_RELOAD:%.*]] = load i32, ptr [[VAL_SPILL_ADDR]], align 4
-; CHECK-NEXT: call void @print(i32 [[VAL_RELOAD]])
+; CHECK-NEXT: call void @print(i32 [[VAL]])
; CHECK-NEXT: br label %[[AFTERCOROEND]]
; CHECK: [[AFTERCOROEND]]:
; CHECK-NEXT: ret void
diff --git a/llvm/test/Transforms/Coroutines/coro-frame.ll b/llvm/test/Transforms/Coroutines/coro-frame.ll
index 2af186e405d54..c7f6c4774de2e 100644
--- a/llvm/test/Transforms/Coroutines/coro-frame.ll
+++ b/llvm/test/Transforms/Coroutines/coro-frame.ll
@@ -75,10 +75,10 @@ declare void @free(ptr)
; CHECK-LABEL: define internal void @f.resume(
; CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(40) [[HDL:%.*]]) personality i32 0 {
; CHECK-NEXT: [[ENTRY_RESUME:.*:]]
-; CHECK-NEXT: [[R_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[HDL]], i64 16
-; CHECK-NEXT: [[R_RELOAD:%.*]] = load double, ptr [[R_RELOAD_ADDR]], align 8
; CHECK-NEXT: [[THIS1_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[HDL]], i64 24
; CHECK-NEXT: [[THIS1_RELOAD:%.*]] = load i64, ptr [[THIS1_RELOAD_ADDR]], align 4
+; CHECK-NEXT: [[R_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[HDL]], i64 16
+; CHECK-NEXT: [[R_RELOAD:%.*]] = load double, ptr [[R_RELOAD_ADDR]], align 8
; CHECK-NEXT: [[TMP0:%.*]] = call double @print(double [[R_RELOAD]])
; CHECK-NEXT: call void @print2(i64 [[THIS1_RELOAD]])
; CHECK-NEXT: [[MEM:%.*]] = call ptr @llvm.coro.free(token poison, ptr [[HDL]])
diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll
index 74a3f8d449d0c..e83d8f0e13690 100644
--- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll
+++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll
@@ -123,7 +123,7 @@ declare void @print(i32)
; CHECK-LABEL: @f.resume.0(
; CHECK-NEXT: entryresume.0:
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[COROEND:%.*]], label [[CLEANUP_SINK_SPLIT:%.*]]
-; CHECK: cleanup.sink.split:
+; CHECK: cleanup.sink.split.after.spill:
; CHECK-NEXT: [[ARRAY_RELOAD:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: store i32 0, ptr [[ARRAY_RELOAD]], align 4
; CHECK-NEXT: br label [[COROEND]]
@@ -134,7 +134,7 @@ declare void @print(i32)
; CHECK-LABEL: @f.resume.1(
; CHECK-NEXT: entryresume.1:
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[COROEND:%.*]], label [[CLEANUP_SINK_SPLIT:%.*]]
-; CHECK: cleanup.sink.split:
+; CHECK: cleanup.sink.split.after.spill:
; CHECK-NEXT: [[ARRAY_RELOAD:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: store i32 10, ptr [[ARRAY_RELOAD]], align 4
; CHECK-NEXT: br label [[COROEND]]
@@ -175,7 +175,7 @@ declare void @print(i32)
; CHECK-NEXT: entryresume.0:
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[COROEND:%.*]], label [[CLEANUP_SINK_SPLIT:%.*]]
-; CHECK: cleanup.sink.split:
+; CHECK: cleanup.sink.split.after.spill:
; CHECK-NEXT: [[ARRAY_RELOAD:%.*]] = load ptr, ptr [[TMP2]], align 8
; CHECK-NEXT: store i32 0, ptr [[ARRAY_RELOAD]], align 4
; CHECK-NEXT: br label [[COROEND]]
@@ -193,7 +193,7 @@ declare void @print(i32)
; CHECK-NEXT: entryresume.1:
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[COROEND:%.*]], label [[CLEANUP_SINK_SPLIT:%.*]]
-; CHECK: cleanup.sink.split:
+; CHECK: cleanup.sink.split.after.spill:
; CHECK-NEXT: [[ARRAY_RELOAD:%.*]] = load ptr, ptr [[TMP2]], align 8
; CHECK-NEXT: store i32 10, ptr [[ARRAY_RELOAD]], align 4
; CHECK-NEXT: br label [[COROEND]]
diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll
index bf95b2a74e6de..b9f271bbb5635 100644
--- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll
+++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll
@@ -99,7 +99,7 @@ declare void @print(i32)
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: [[TEMP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 8
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[COROEND:%.*]], label [[CONT:%.*]]
-; CHECK: cont:
+; CHECK: cont.spill:
; CHECK-NEXT: [[PTR_RELOAD:%.*]] = load ptr, ptr [[TMP2]], align 8
; CHECK-NEXT: [[NEWVALUE:%.*]] = load i32, ptr [[TEMP]], align 4
; CHECK-NEXT: store i32 [[NEWVALUE]], ptr [[PTR_RELOAD]], align 4
@@ -128,12 +128,12 @@ declare void @print(i32)
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: [[TEMP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 8
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[CLEANUP:%.*]], label [[CONT:%.*]]
-; CHECK: cont:
+; CHECK: cont.spill:
; CHECK-NEXT: [[PTR_RELOAD:%.*]] = load ptr, ptr [[TMP2]], align 8
; CHECK-NEXT: [[NEWVALUE:%.*]] = load i32, ptr [[TEMP]], align 4
; CHECK-NEXT: store i32 [[NEWVALUE]], ptr [[PTR_RELOAD]], align 4
; CHECK-NEXT: br label [[CLEANUP]]
-; CHECK: cleanup:
+; CHECK: cleanup.spill:
; CHECK-NEXT: [[VAL_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 12
; CHECK-NEXT: [[VAL_RELOAD:%.*]] = load i8, ptr [[VAL_RELOAD_ADDR]], align 1
; CHECK-NEXT: call fastcc void @deallocate(ptr [[TMP2]])
@@ -157,7 +157,7 @@ declare void @print(i32)
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: [[TEMP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 8
; CHECK-NEXT: br i1 [[TMP1:%.*]], label [[COROEND:%.*]], label [[CONT:%.*]]
-; CHECK: cont:
+; CHECK: cont.spill:
; CHECK-NEXT: [[PTR_RELOAD:%.*]] = load ptr, ptr [[TMP2]], align 8
; CHECK-NEXT: [[NEWVALUE:%.*]] = load i32, ptr [[TEMP]], align 4
; CHECK-NEXT: store i32 [[NEWVALUE]], ptr [[PTR_RELOAD]], align 4
diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll
index 2f04453d69c4b..489e9799a119f 100644
--- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll
+++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll
@@ -40,14 +40,14 @@ define i32 @main() {
; CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4
; CHECK-NEXT: [[N_VAL3_SPILL_ADDR_I:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 4
; CHECK-NEXT: store i32 1, ptr [[N_VAL3_SPILL_ADDR_I]], align 4, !noalias [[META0:![0-9]+]]
-; CHECK-NEXT: [[INPUT_SPILL_ADDR_I:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 8
-; CHECK-NEXT: store i32 2, ptr [[INPUT_SPILL_ADDR_I]], align 4, !noalias [[META0]]
; CHECK-NEXT: [[INPUT_RELOAD_ADDR13_I:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 8
+; CHECK-NEXT: store i32 2, ptr [[INPUT_RELOAD_ADDR13_I]], align 4, !noalias [[META0]]
; CHECK-NEXT: [[N_VAL3_RELOAD_ADDR11_I:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 4
+; CHECK-NEXT: [[INPUT_RELOAD_ADDR14_I:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 8
; CHECK-NEXT: store i32 3, ptr [[N_VAL3_RELOAD_ADDR11_I]], align 4, !noalias [[META3:![0-9]+]]
-; CHECK-NEXT: store i32 4, ptr [[INPUT_RELOAD_ADDR13_I]], align 4, !noalias [[META3]]
-; CHECK-NEXT: tail call void @print(i32 7), !noalias [[META6:![0-9]+]]
-; CHECK-NEXT: tail call void @deallocate(ptr nonnull [[TMP0]]), !noalias [[META6]]
+; CHECK-NEXT: store i32 4, ptr [[INPUT_RELOAD_ADDR14_I]], align 4, !noalias [[META3]]
+; CHECK-NEXT: tail call void @print(i32 7), !noalias [[META6:![0-9]+]], !inline_history [[META9:![0-9]+]]
+; CHECK-NEXT: tail call void @deallocate(ptr nonnull [[TMP0]]), !noalias [[META6]], !inline_history [[META9]]
; CHECK-NEXT: ret i32 0
;
entry:
diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll
index b1dfbd1b6d4f6..6441456c5ac28 100644
--- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll
+++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll
@@ -58,10 +58,10 @@ declare void @print(i32)
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
; CHECK-NEXT: [[VALUE1_SPILL_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 12
; CHECK-NEXT: store i32 [[TMP1:%.*]], ptr [[VALUE1_SPILL_ADDR]], align 4
-; CHECK-NEXT: [[SUM0_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 8
-; CHECK-NEXT: [[SUM0_RELOAD:%.*]] = load i32, ptr [[SUM0_RELOAD_ADDR]], align 4
; CHECK-NEXT: [[VALUE0_RELOAD_ADDR5:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 4
; CHECK-NEXT: [[VALUE0_RELOAD6:%.*]] = load i32, ptr [[VALUE0_RELOAD_ADDR5]], align 4
+; CHECK-NEXT: [[SUM0_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 8
+; CHECK-NEXT: [[SUM0_RELOAD:%.*]] = load i32, ptr [[SUM0_RELOAD_ADDR]], align 4
; CHECK-NEXT: [[SUM1:%.*]] = call i32 @add(i32 [[SUM0_RELOAD]], i32 [[VALUE0_RELOAD6]])
; CHECK-NEXT: [[SUM2:%.*]] = call i32 @add(i32 [[SUM1]], i32 [[TMP1]])
; CHECK-NEXT: [[SUM2_SPILL_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 16
@@ -72,13 +72,13 @@ declare void @print(i32)
; CHECK-LABEL: @f.resume.2(
; CHECK-NEXT: entryresume.2:
; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
-; CHECK-NEXT: [[SUM2_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 16
+; CHECK-NEXT: [[SUM2_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 4
; CHECK-NEXT: [[SUM2_RELOAD:%.*]] = load i32, ptr [[SUM2_RELOAD_ADDR]], align 4
; CHECK-NEXT: [[VALUE1_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 12
; CHECK-NEXT: [[VALUE1_RELOAD:%.*]] = load i32, ptr [[VALUE1_RELOAD_ADDR]], align 4
-; CHECK-NEXT: [[VALUE0_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 4
+; CHECK-NEXT: [[VALUE0_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 16
; CHECK-NEXT: [[VALUE0_RELOAD:%.*]] = load i32, ptr [[VALUE0_RELOAD_ADDR]], align 4
-; CHECK-NEXT: [[SUM3:%.*]] = call i32 @add(i32 [[SUM2_RELOAD]], i32 [[VALUE0_RELOAD]])
+; CHECK-NEXT: [[SUM3:%.*]] = call i32 @add(i32 [[VALUE0_RELOAD]], i32 [[SUM2_RELOAD]])
; CHECK-NEXT: [[SUM4:%.*]] = call i32 @add(i32 [[SUM3]], i32 [[VALUE1_RELOAD]])
; CHECK-NEXT: [[SUM5:%.*]] = call i32 @add(i32 [[SUM4]], i32 [[TMP1:%.*]])
; CHECK-NEXT: call void @print(i32 [[SUM5]])
diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll
index cd2488adc74be..99ec0d8986ed9 100644
--- a/llvm/test/Transforms/Coroutines/coro-retcon.ll
+++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll
@@ -43,9 +43,9 @@ define i32 @main() {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void @print(i32 4)
; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata [[META0:![0-9]+]])
-; CHECK-NEXT: tail call void @print(i32 5), !noalias [[META0]]
-; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata [[META3:![0-9]+]])
-; CHECK-NEXT: tail call void @print(i32 6), !noalias [[META3]]
+; CHECK-NEXT: tail call void @print(i32 5), !noalias [[META0]], !inline_history [[META3:![0-9]+]]
+; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl(metadata [[META4:![0-9]+]])
+; CHECK-NEXT: tail call void @print(i32 6), !noalias [[META4]], !inline_history [[META3]]
; CHECK-NEXT: ret i32 0
;
; CORO-LABEL: @main(
@@ -84,10 +84,8 @@ define hidden { ptr, ptr } @g(ptr %buffer, ptr %ptr) {
; CORO-NEXT: [[TMP0:%.*]] = call ptr @allocate(i32 8)
; CORO-NEXT: store ptr [[TMP0]], ptr [[BUFFER:%.*]], align 8
; CORO-NEXT: store ptr [[PTR:%.*]], ptr [[TMP0]], align 8
-; CORO-NEXT: [[PTR_RELOAD_ADDR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 0
-; CORO-NEXT: [[PTR_RELOAD:%.*]] = load ptr, ptr [[PTR_RELOAD_ADDR]], align 8
; CORO-NEXT: [[TMP1:%.*]] = insertvalue { ptr, ptr } poison, ptr @g.resume.0, 0
-; CORO-NEXT: [[TMP2:%.*]] = insertvalue { ptr, ptr } [[TMP1]], ptr [[PTR_RELOAD]], 1
+; CORO-NEXT: [[TMP2:%.*]] = insertvalue { ptr, ptr } [[TMP1]], ptr [[PTR]], 1
; CORO-NEXT: ret { ptr, ptr } [[TMP2]]
;
entry:
diff --git a/llvm/test/Transforms/Coroutines/coro-spill-ramp.ll b/llvm/test/Transforms/Coroutines/coro-spill-ramp.ll
new file mode 100644
index 0000000000000..087ed7568da22
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-spill-ramp.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes='cgscc(instcombine,coro-split),simplifycfg,early-cse' -S | FileCheck %s
+
+; Regression test for GH192351
+; Other passes are free to sink instructions that do not access memory.
+; Test that spills in the ramp function are not replaced with reloads from the coroutine frame, thereby preventing loads from a dead frame.
+define void @f(i1 noundef zeroext %b) presplitcoroutine {
+; CHECK-LABEL: define void @f(
+; CHECK-SAME: i1 noundef zeroext [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.coro.id(i32 16, ptr null, ptr nonnull @f, ptr nonnull @f.resumers)
+; CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.coro.alloc(token [[TMP0]])
+; CHECK-NEXT: br i1 [[TMP1]], label %[[CORO_ALLOC:.*]], label %[[CORO_INIT:.*]]
+; CHECK: [[CORO_ALLOC]]:
+; CHECK-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 24)
+; CHECK-NEXT: br label %[[CORO_INIT]]
+; CHECK: [[CORO_INIT]]:
+; CHECK-NEXT: [[TMP2:%.*]] = phi ptr [ @f.destroy, %[[CORO_ALLOC]] ], [ @f.cleanup, %[[ENTRY]] ]
+; CHECK-NEXT: [[TMP3:%.*]] = phi ptr [ [[CALL]], %[[CORO_ALLOC]] ], [ null, %[[ENTRY]] ]
+; CHECK-NEXT: [[TMP4:%.*]] = call noalias nonnull ptr @llvm.coro.begin(token [[TMP0]], ptr [[TMP3]])
+; CHECK-NEXT: store ptr @f.resume, ptr [[TMP4]], align 8
+; CHECK-NEXT: [[DESTROY_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP4]], i64 8
+; CHECK-NEXT: store ptr [[TMP2]], ptr [[DESTROY_ADDR]], align 8
+; CHECK-NEXT: [[B_SPILL_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP4]], i64 17
+; CHECK-NEXT: store i1 [[B]], ptr [[B_SPILL_ADDR]], align 1
+; CHECK-NEXT: br i1 [[B]], label %[[COROSAVE:.*]], label %[[CLEANUP17:.*]]
+; CHECK: [[COROSAVE]]:
+; CHECK-NEXT: [[INDEX_ADDR1:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP4]], i64 16
+; CHECK-NEXT: store i1 false, ptr [[INDEX_ADDR1]], align 1
+; CHECK-NEXT: br label %[[AFTERCOROEND:.*]]
+; CHECK: [[CLEANUP17]]:
+; CHECK-NEXT: [[TMP5:%.*]] = call ptr @llvm.coro.free(token [[TMP0]], ptr nonnull [[TMP4]])
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[TMP5]], null
+; CHECK-NEXT: br i1 [[DOTNOT]], label %[[AFTERCOROEND]], label %[[CORO_FREE:.*]]
+; CHECK: [[CORO_FREE]]:
+; CHECK-NEXT: call void @free(ptr nonnull [[TMP5]], i64 24)
+; CHECK-NEXT: br label %[[AFTERCOROEND]]
+; CHECK: [[AFTERCOROEND]]:
+; CHECK-NEXT: [[B8:%.*]] = zext i1 [[B]] to i8
+; CHECK-NEXT: notail call void (...) @llvm.fake.use(i8 [[B8]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %b8 = zext i1 %b to i8 ; InstCombine sinks
+ %0 = call token @llvm.coro.id(i32 16, ptr null, ptr nonnull @f, ptr null)
+ %1 = call i1 @llvm.coro.alloc(token %0)
+ br i1 %1, label %coro.alloc, label %coro.init
+
+coro.alloc:
+ %2 = call i64 @llvm.coro.size.i64()
+ %call = call ptr @malloc(i64 %2)
+ br label %coro.init
+
+coro.init:
+ %3 = phi ptr [ null, %entry ], [ %call, %coro.alloc ]
+ %4 = call ptr @llvm.coro.begin(token %0, ptr %3)
+ br i1 %b, label %if.then, label %cleanup17
+
+if.then:
+ %5 = call token @llvm.coro.save(ptr %4)
+ %6 = call i8 @llvm.coro.suspend(token %5, i1 false)
+ switch i8 %6, label %coro.ret [
+ i8 0, label %cleanup17
+ i8 1, label %cleanup17
+ ]
+
+cleanup17:
+ %7 = call ptr @llvm.coro.free(token %0, ptr %4)
+ %.not = icmp eq ptr %7, null
+ br i1 %.not, label %coro.ret, label %coro.free
+
+coro.free:
+ %8 = call i64 @llvm.coro.size.i64()
+ call void @free(ptr %7, i64 %8)
+ br label %coro.ret
+
+coro.ret:
+ call void @llvm.coro.end(ptr null, i1 false, token none)
+ notail call void (...) @llvm.fake.use(i8 %b8)
+ ret void
+}
+
+declare ptr @malloc(i64)
+declare void @free(ptr, i64)
>From 18878bc3f075bf96813be1a4f720a39423db15c8 Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Mon, 24 Aug 2026 12:34:07 +0800
Subject: [PATCH 2/2] Update unittests
---
llvm/unittests/Transforms/Coroutines/ExtraRematTest.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/unittests/Transforms/Coroutines/ExtraRematTest.cpp b/llvm/unittests/Transforms/Coroutines/ExtraRematTest.cpp
index a34b630a34fb7..e2671d037d074 100644
--- a/llvm/unittests/Transforms/Coroutines/ExtraRematTest.cpp
+++ b/llvm/unittests/Transforms/Coroutines/ExtraRematTest.cpp
@@ -173,9 +173,9 @@ TEST_F(ExtraRematTest, TestCoroRematWithCallback) {
Function *F = M->getFunction("f.resume");
ASSERT_TRUE(F) << "could not find split function f.resume";
- BasicBlock *Resume1 = getBasicBlockByName(F, "resume1");
+ BasicBlock *Resume1 = getBasicBlockByName(F, "resume1.after.spill");
ASSERT_TRUE(Resume1)
- << "could not find expected BB resume1 in split function";
+ << "could not find expected BB in split function";
// With callback the extra rematerialization of the function should have
// happened
@@ -259,9 +259,9 @@ TEST_F(ExtraRematTest, TestCoroRematWithCustomABI) {
Function *F = M->getFunction("f.resume");
ASSERT_TRUE(F) << "could not find split function f.resume";
- BasicBlock *Resume1 = getBasicBlockByName(F, "resume1");
+ BasicBlock *Resume1 = getBasicBlockByName(F, "resume1.after.spill");
ASSERT_TRUE(Resume1)
- << "could not find expected BB resume1 in split function";
+ << "could not find expected BB in split function";
// With callback the extra rematerialization of the function should have
// happened
More information about the llvm-commits
mailing list