25
25
#include " llvm/ADT/PriorityWorklist.h"
26
26
#include " llvm/ADT/SmallPtrSet.h"
27
27
#include " llvm/ADT/SmallVector.h"
28
+ #include " llvm/ADT/StringExtras.h"
28
29
#include " llvm/ADT/StringRef.h"
29
30
#include " llvm/ADT/Twine.h"
30
31
#include " llvm/Analysis/CFG.h"
@@ -1179,6 +1180,14 @@ static void updateAsyncFuncPointerContextSize(coro::Shape &Shape) {
1179
1180
Shape.AsyncLowering .AsyncFuncPointer ->setInitializer (NewFuncPtrStruct);
1180
1181
}
1181
1182
1183
+ static TypeSize getFrameSizeForShape (coro::Shape &Shape) {
1184
+ // In the same function all coro.sizes should have the same result type.
1185
+ auto *SizeIntrin = Shape.CoroSizes .back ();
1186
+ Module *M = SizeIntrin->getModule ();
1187
+ const DataLayout &DL = M->getDataLayout ();
1188
+ return DL.getTypeAllocSize (Shape.FrameTy );
1189
+ }
1190
+
1182
1191
static void replaceFrameSizeAndAlignment (coro::Shape &Shape) {
1183
1192
if (Shape.ABI == coro::ABI::Async)
1184
1193
updateAsyncFuncPointerContextSize (Shape);
@@ -1194,10 +1203,8 @@ static void replaceFrameSizeAndAlignment(coro::Shape &Shape) {
1194
1203
1195
1204
// In the same function all coro.sizes should have the same result type.
1196
1205
auto *SizeIntrin = Shape.CoroSizes .back ();
1197
- Module *M = SizeIntrin->getModule ();
1198
- const DataLayout &DL = M->getDataLayout ();
1199
- auto Size = DL.getTypeAllocSize (Shape.FrameTy );
1200
- auto *SizeConstant = ConstantInt::get (SizeIntrin->getType (), Size );
1206
+ auto *SizeConstant =
1207
+ ConstantInt::get (SizeIntrin->getType (), getFrameSizeForShape (Shape));
1201
1208
1202
1209
for (CoroSizeInst *CS : Shape.CoroSizes ) {
1203
1210
CS->replaceAllUsesWith (SizeConstant);
@@ -1455,6 +1462,74 @@ struct SwitchCoroutineSplitter {
1455
1462
setCoroInfo (F, Shape, Clones);
1456
1463
}
1457
1464
1465
+ // Create a variant of ramp function that does not perform heap allocation
1466
+ // for a switch ABI coroutine.
1467
+ //
1468
+ // The newly split `.noalloc` ramp function has the following differences:
1469
+ // - Has one additional frame pointer parameter in lieu of dynamic
1470
+ // allocation.
1471
+ // - Suppressed allocations by replacing coro.alloc and coro.free.
1472
+ static Function *createNoAllocVariant (Function &F, coro::Shape &Shape,
1473
+ SmallVectorImpl<Function *> &Clones) {
1474
+ auto *OrigFnTy = F.getFunctionType ();
1475
+ auto OldParams = OrigFnTy->params ();
1476
+
1477
+ SmallVector<Type *> NewParams;
1478
+ NewParams.reserve (OldParams.size () + 1 );
1479
+ NewParams.append (OldParams.begin (), OldParams.end ());
1480
+ NewParams.push_back (PointerType::getUnqual (Shape.FrameTy ));
1481
+
1482
+ auto *NewFnTy = FunctionType::get (OrigFnTy->getReturnType (), NewParams,
1483
+ OrigFnTy->isVarArg ());
1484
+ Function *NoAllocF =
1485
+ Function::Create (NewFnTy, F.getLinkage (), F.getName () + " .noalloc" );
1486
+
1487
+ ValueToValueMapTy VMap;
1488
+ unsigned int Idx = 0 ;
1489
+ for (const auto &I : F.args ()) {
1490
+ VMap[&I] = NoAllocF->getArg (Idx++);
1491
+ }
1492
+ SmallVector<ReturnInst *, 4 > Returns;
1493
+ CloneFunctionInto (NoAllocF, &F, VMap,
1494
+ CloneFunctionChangeType::LocalChangesOnly, Returns);
1495
+
1496
+ if (Shape.CoroBegin ) {
1497
+ auto *NewCoroBegin =
1498
+ cast_if_present<CoroBeginInst>(VMap[Shape.CoroBegin ]);
1499
+ auto *NewCoroId = cast<CoroIdInst>(NewCoroBegin->getId ());
1500
+ coro::replaceCoroFree (NewCoroId, /* Elide=*/ true );
1501
+ coro::suppressCoroAllocs (NewCoroId);
1502
+ NewCoroBegin->replaceAllUsesWith (NoAllocF->getArg (Idx));
1503
+ NewCoroBegin->eraseFromParent ();
1504
+ }
1505
+
1506
+ Module *M = F.getParent ();
1507
+ M->getFunctionList ().insert (M->end (), NoAllocF);
1508
+
1509
+ removeUnreachableBlocks (*NoAllocF);
1510
+ auto NewAttrs = NoAllocF->getAttributes ();
1511
+ // We just appended the frame pointer as the last argument of the new
1512
+ // function.
1513
+ auto FrameIdx = NoAllocF->arg_size () - 1 ;
1514
+ // When we elide allocation, we read these attributes to determine the
1515
+ // frame size and alignment.
1516
+ addFramePointerAttrs (NewAttrs, NoAllocF->getContext (), FrameIdx,
1517
+ Shape.FrameSize , Shape.FrameAlign ,
1518
+ /* NoAlias=*/ false );
1519
+
1520
+ NoAllocF->setAttributes (NewAttrs);
1521
+
1522
+ Clones.push_back (NoAllocF);
1523
+ // Reset the original function's coro info, make the new noalloc variant
1524
+ // connected to the original ramp function.
1525
+ setCoroInfo (F, Shape, Clones);
1526
+ // After copying, set the linkage to internal linkage. Original function
1527
+ // may have different linkage, but optimization dependent on this function
1528
+ // generally relies on LTO.
1529
+ NoAllocF->setLinkage (llvm::GlobalValue::InternalLinkage);
1530
+ return NoAllocF;
1531
+ }
1532
+
1458
1533
private:
1459
1534
// Create a resume clone by cloning the body of the original function, setting
1460
1535
// new entry block and replacing coro.suspend an appropriate value to force
@@ -1913,6 +1988,21 @@ class PrettyStackTraceFunction : public PrettyStackTraceEntry {
1913
1988
};
1914
1989
} // namespace
1915
1990
1991
+ // / Remove calls to llvm.coro.end in the original function.
1992
+ static void removeCoroEndsFromRampFunction (const coro::Shape &Shape) {
1993
+ if (Shape.ABI != coro::ABI::Switch) {
1994
+ for (auto *End : Shape.CoroEnds ) {
1995
+ replaceCoroEnd (End, Shape, Shape.FramePtr , /* in resume*/ false , nullptr );
1996
+ }
1997
+ } else {
1998
+ for (llvm::AnyCoroEndInst *End : Shape.CoroEnds ) {
1999
+ auto &Context = End->getContext ();
2000
+ End->replaceAllUsesWith (ConstantInt::getFalse (Context));
2001
+ End->eraseFromParent ();
2002
+ }
2003
+ }
2004
+ }
2005
+
1916
2006
static coro::Shape
1917
2007
splitCoroutine (Function &F, SmallVectorImpl<Function *> &Clones,
1918
2008
TargetTransformInfo &TTI, bool OptimizeFrame,
@@ -1932,10 +2022,19 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
1932
2022
simplifySuspendPoints (Shape);
1933
2023
buildCoroutineFrame (F, Shape, TTI, MaterializableCallback);
1934
2024
replaceFrameSizeAndAlignment (Shape);
2025
+ bool isNoSuspendCoroutine = Shape.CoroSuspends .empty ();
2026
+
2027
+ bool shouldCreateNoAllocVariant =
2028
+ !isNoSuspendCoroutine && Shape.ABI == coro::ABI::Switch &&
2029
+ F.hasFnAttribute (llvm::Attribute::CoroGenNoallocRamp);
1935
2030
2031
+ // We don't need this attribute any more. Delete it.
2032
+ if (F.hasFnAttribute (llvm::Attribute::CoroGenNoallocRamp)) {
2033
+ F.removeFnAttr (llvm::Attribute::CoroGenNoallocRamp);
2034
+ }
1936
2035
// If there are no suspend points, no split required, just remove
1937
2036
// the allocation and deallocation blocks, they are not needed.
1938
- if (Shape. CoroSuspends . empty () ) {
2037
+ if (isNoSuspendCoroutine ) {
1939
2038
handleNoSuspendCoroutine (Shape);
1940
2039
} else {
1941
2040
switch (Shape.ABI ) {
@@ -1967,22 +2066,13 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
1967
2066
for (DbgVariableRecord *DVR : DbgVariableRecords)
1968
2067
coro::salvageDebugInfo (ArgToAllocaMap, *DVR, Shape.OptimizeFrame ,
1969
2068
false /* UseEntryValue*/ );
1970
- return Shape;
1971
- }
1972
2069
1973
- // / Remove calls to llvm.coro.end in the original function.
1974
- static void removeCoroEndsFromRampFunction (const coro::Shape &Shape) {
1975
- if (Shape.ABI != coro::ABI::Switch) {
1976
- for (auto *End : Shape.CoroEnds ) {
1977
- replaceCoroEnd (End, Shape, Shape.FramePtr , /* in resume*/ false , nullptr );
1978
- }
1979
- } else {
1980
- for (llvm::AnyCoroEndInst *End : Shape.CoroEnds ) {
1981
- auto &Context = End->getContext ();
1982
- End->replaceAllUsesWith (ConstantInt::getFalse (Context));
1983
- End->eraseFromParent ();
1984
- }
1985
- }
2070
+ removeCoroEndsFromRampFunction (Shape);
2071
+
2072
+ if (shouldCreateNoAllocVariant)
2073
+ SwitchCoroutineSplitter::createNoAllocVariant (F, Shape, Clones);
2074
+
2075
+ return Shape;
1986
2076
}
1987
2077
1988
2078
static void updateCallGraphAfterCoroutineSplit (
@@ -2113,13 +2203,12 @@ PreservedAnalyses CoroSplitPass::run(LazyCallGraph::SCC &C,
2113
2203
F.setSplittedCoroutine ();
2114
2204
2115
2205
SmallVector<Function *, 4 > Clones;
2116
- auto &ORE = FAM.getResult <OptimizationRemarkEmitterAnalysis>(F);
2117
- const coro::Shape Shape =
2206
+ coro::Shape Shape =
2118
2207
splitCoroutine (F, Clones, FAM.getResult <TargetIRAnalysis>(F),
2119
2208
OptimizeFrame, MaterializableCallback);
2120
- removeCoroEndsFromRampFunction (Shape);
2121
2209
updateCallGraphAfterCoroutineSplit (*N, Shape, Clones, C, CG, AM, UR, FAM);
2122
2210
2211
+ auto &ORE = FAM.getResult <OptimizationRemarkEmitterAnalysis>(F);
2123
2212
ORE.emit ([&]() {
2124
2213
return OptimizationRemark (DEBUG_TYPE, " CoroSplit" , &F)
2125
2214
<< " Split '" << ore::NV (" function" , F.getName ())
@@ -2135,9 +2224,9 @@ PreservedAnalyses CoroSplitPass::run(LazyCallGraph::SCC &C,
2135
2224
}
2136
2225
}
2137
2226
2138
- for (auto *PrepareFn : PrepareFns) {
2139
- replaceAllPrepares (PrepareFn, CG, C);
2140
- }
2227
+ for (auto *PrepareFn : PrepareFns) {
2228
+ replaceAllPrepares (PrepareFn, CG, C);
2229
+ }
2141
2230
2142
2231
return PreservedAnalyses::none ();
2143
2232
}
0 commit comments