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