Skip to content

Commit 24860c1

Browse files
committed
[Coro] Prebuild a global debug info set and share it between all coroutine clones
Summary: CoroCloner, by calling into CloneFunctionInto, does a lot of repeated work priming DIFinder and building a list of global debug info metadata. For programs compiled with full debug info this gets very expensive. This diff builds the data once and shares it between all clones. Anecdata for a sample cpp source file compiled with full debug info: | | Baseline | IdentityMD set | Prebuilt GlobalDI (cur.) | |-----------------+----------+----------------+--------------------------| | CoroSplitPass | 306ms | 221ms | 68ms | | CoroCloner | 101ms | 72ms | 0.5ms | | CollectGlobalDI | - | - | 63ms | |-----------------+----------+----------------+--------------------------| | Speed up | 1x | 1.4x | 4.5x | Note that CollectGlobalDI happens once *per coroutine* rather than per clone. Test Plan: ninja check-llvm-unit ninja check-llvm Compiled a sample internal source file, checked time trace output for scope timings. stack-info: PR: #118628, branch: users/artempyanykh/fast-coro-upstream/9
1 parent e80a2a7 commit 24860c1

File tree

2 files changed

+62
-18
lines changed

2 files changed

+62
-18
lines changed

llvm/lib/Transforms/Coroutines/CoroCloner.h

+18-11
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class BaseCloner {
4848
CloneKind FKind;
4949
IRBuilder<> Builder;
5050
TargetTransformInfo &TTI;
51+
const MetadataSetTy &GlobalDebugInfo;
5152

5253
ValueToValueMapTy VMap;
5354
Function *NewF = nullptr;
@@ -60,12 +61,12 @@ class BaseCloner {
6061
/// Create a cloner for a continuation lowering.
6162
BaseCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
6263
Function *NewF, AnyCoroSuspendInst *ActiveSuspend,
63-
TargetTransformInfo &TTI)
64+
TargetTransformInfo &TTI, const MetadataSetTy &GlobalDebugInfo)
6465
: OrigF(OrigF), Suffix(Suffix), Shape(Shape),
6566
FKind(Shape.ABI == ABI::Async ? CloneKind::Async
6667
: CloneKind::Continuation),
67-
Builder(OrigF.getContext()), TTI(TTI), NewF(NewF),
68-
ActiveSuspend(ActiveSuspend) {
68+
Builder(OrigF.getContext()), TTI(TTI), GlobalDebugInfo(GlobalDebugInfo),
69+
NewF(NewF), ActiveSuspend(ActiveSuspend) {
6970
assert(Shape.ABI == ABI::Retcon || Shape.ABI == ABI::RetconOnce ||
7071
Shape.ABI == ABI::Async);
7172
assert(NewF && "need existing function for continuation");
@@ -74,22 +75,26 @@ class BaseCloner {
7475

7576
public:
7677
BaseCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
77-
CloneKind FKind, TargetTransformInfo &TTI)
78+
CloneKind FKind, TargetTransformInfo &TTI,
79+
const MetadataSetTy &GlobalDebugInfo)
7880
: OrigF(OrigF), Suffix(Suffix), Shape(Shape), FKind(FKind),
79-
Builder(OrigF.getContext()), TTI(TTI) {}
81+
Builder(OrigF.getContext()), TTI(TTI),
82+
GlobalDebugInfo(GlobalDebugInfo) {}
8083

8184
virtual ~BaseCloner() {}
8285

8386
/// Create a clone for a continuation lowering.
8487
static Function *createClone(Function &OrigF, const Twine &Suffix,
8588
coro::Shape &Shape, Function *NewF,
8689
AnyCoroSuspendInst *ActiveSuspend,
87-
TargetTransformInfo &TTI) {
90+
TargetTransformInfo &TTI,
91+
const MetadataSetTy &GlobalDebugInfo) {
8892
assert(Shape.ABI == ABI::Retcon || Shape.ABI == ABI::RetconOnce ||
8993
Shape.ABI == ABI::Async);
9094
TimeTraceScope FunctionScope("BaseCloner");
9195

92-
BaseCloner Cloner(OrigF, Suffix, Shape, NewF, ActiveSuspend, TTI);
96+
BaseCloner Cloner(OrigF, Suffix, Shape, NewF, ActiveSuspend, TTI,
97+
GlobalDebugInfo);
9398
Cloner.create();
9499
return Cloner.getFunction();
95100
}
@@ -129,20 +134,22 @@ class SwitchCloner : public BaseCloner {
129134
protected:
130135
/// Create a cloner for a switch lowering.
131136
SwitchCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
132-
CloneKind FKind, TargetTransformInfo &TTI)
133-
: BaseCloner(OrigF, Suffix, Shape, FKind, TTI) {}
137+
CloneKind FKind, TargetTransformInfo &TTI,
138+
const MetadataSetTy &GlobalDebugInfo)
139+
: BaseCloner(OrigF, Suffix, Shape, FKind, TTI, GlobalDebugInfo) {}
134140

135141
void create() override;
136142

137143
public:
138144
/// Create a clone for a switch lowering.
139145
static Function *createClone(Function &OrigF, const Twine &Suffix,
140146
coro::Shape &Shape, CloneKind FKind,
141-
TargetTransformInfo &TTI) {
147+
TargetTransformInfo &TTI,
148+
const MetadataSetTy &GlobalDebugInfo) {
142149
assert(Shape.ABI == ABI::Switch);
143150
TimeTraceScope FunctionScope("SwitchCloner");
144151

145-
SwitchCloner Cloner(OrigF, Suffix, Shape, FKind, TTI);
152+
SwitchCloner Cloner(OrigF, Suffix, Shape, FKind, TTI, GlobalDebugInfo);
146153
Cloner.create();
147154
return Cloner.getFunction();
148155
}

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

+44-7
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "llvm/IR/CallingConv.h"
4444
#include "llvm/IR/Constants.h"
4545
#include "llvm/IR/DataLayout.h"
46+
#include "llvm/IR/DebugInfo.h"
4647
#include "llvm/IR/DerivedTypes.h"
4748
#include "llvm/IR/Dominators.h"
4849
#include "llvm/IR/GlobalValue.h"
@@ -77,6 +78,25 @@ using namespace llvm;
7778

7879
#define DEBUG_TYPE "coro-split"
7980

81+
namespace {
82+
/// Collect (a known) subset of global debug info metadata potentially used by
83+
/// the function \p F.
84+
///
85+
/// This metadata set can be used to avoid cloning debug info not owned by \p F
86+
/// and is shared among all potential clones \p F.
87+
void collectGlobalDebugInfo(Function &F, MetadataSetTy &GlobalDebugInfo) {
88+
TimeTraceScope FunctionScope("CollectGlobalDebugInfo");
89+
90+
DebugInfoFinder DIFinder;
91+
DISubprogram *SPClonedWithinModule = CollectDebugInfoForCloning(
92+
F, CloneFunctionChangeType::LocalChangesOnly, DIFinder);
93+
94+
FindDebugInfoToIdentityMap(GlobalDebugInfo,
95+
CloneFunctionChangeType::LocalChangesOnly,
96+
DIFinder, SPClonedWithinModule);
97+
}
98+
} // end anonymous namespace
99+
80100
// FIXME:
81101
// Lower the intrinisc in CoroEarly phase if coroutine frame doesn't escape
82102
// and it is known that other transformations, for example, sanitizers
@@ -891,8 +911,11 @@ void coro::BaseCloner::create() {
891911
auto savedLinkage = NewF->getLinkage();
892912
NewF->setLinkage(llvm::GlobalValue::ExternalLinkage);
893913

894-
CloneFunctionInto(NewF, &OrigF, VMap,
895-
CloneFunctionChangeType::LocalChangesOnly, Returns);
914+
CloneFunctionAttributesInto(NewF, &OrigF, VMap, false);
915+
CloneFunctionMetadataInto(NewF, &OrigF, VMap, RF_None, nullptr, nullptr,
916+
&GlobalDebugInfo);
917+
CloneFunctionBodyInto(NewF, &OrigF, VMap, RF_None, Returns, "", nullptr,
918+
nullptr, nullptr, &GlobalDebugInfo);
896919

897920
auto &Context = NewF->getContext();
898921

@@ -1374,16 +1397,22 @@ struct SwitchCoroutineSplitter {
13741397
TargetTransformInfo &TTI) {
13751398
assert(Shape.ABI == coro::ABI::Switch);
13761399

1400+
MetadataSetTy GlobalDebugInfo;
1401+
collectGlobalDebugInfo(F, GlobalDebugInfo);
1402+
13771403
// Create a resume clone by cloning the body of the original function,
13781404
// setting new entry block and replacing coro.suspend an appropriate value
13791405
// to force resume or cleanup pass for every suspend point.
13801406
createResumeEntryBlock(F, Shape);
13811407
auto *ResumeClone = coro::SwitchCloner::createClone(
1382-
F, ".resume", Shape, coro::CloneKind::SwitchResume, TTI);
1408+
F, ".resume", Shape, coro::CloneKind::SwitchResume, TTI,
1409+
GlobalDebugInfo);
13831410
auto *DestroyClone = coro::SwitchCloner::createClone(
1384-
F, ".destroy", Shape, coro::CloneKind::SwitchUnwind, TTI);
1411+
F, ".destroy", Shape, coro::CloneKind::SwitchUnwind, TTI,
1412+
GlobalDebugInfo);
13851413
auto *CleanupClone = coro::SwitchCloner::createClone(
1386-
F, ".cleanup", Shape, coro::CloneKind::SwitchCleanup, TTI);
1414+
F, ".cleanup", Shape, coro::CloneKind::SwitchCleanup, TTI,
1415+
GlobalDebugInfo);
13871416

13881417
postSplitCleanup(*ResumeClone);
13891418
postSplitCleanup(*DestroyClone);
@@ -1768,12 +1797,16 @@ void coro::AsyncABI::splitCoroutine(Function &F, coro::Shape &Shape,
17681797
}
17691798

17701799
assert(Clones.size() == Shape.CoroSuspends.size());
1800+
1801+
MetadataSetTy GlobalDebugInfo;
1802+
collectGlobalDebugInfo(F, GlobalDebugInfo);
1803+
17711804
for (auto [Idx, CS] : llvm::enumerate(Shape.CoroSuspends)) {
17721805
auto *Suspend = CS;
17731806
auto *Clone = Clones[Idx];
17741807

17751808
coro::BaseCloner::createClone(F, "resume." + Twine(Idx), Shape, Clone,
1776-
Suspend, TTI);
1809+
Suspend, TTI, GlobalDebugInfo);
17771810
}
17781811
}
17791812

@@ -1899,12 +1932,16 @@ void coro::AnyRetconABI::splitCoroutine(Function &F, coro::Shape &Shape,
18991932
}
19001933

19011934
assert(Clones.size() == Shape.CoroSuspends.size());
1935+
1936+
MetadataSetTy GlobalDebugInfo;
1937+
collectGlobalDebugInfo(F, GlobalDebugInfo);
1938+
19021939
for (auto [Idx, CS] : llvm::enumerate(Shape.CoroSuspends)) {
19031940
auto Suspend = CS;
19041941
auto Clone = Clones[Idx];
19051942

19061943
coro::BaseCloner::createClone(F, "resume." + Twine(Idx), Shape, Clone,
1907-
Suspend, TTI);
1944+
Suspend, TTI, GlobalDebugInfo);
19081945
}
19091946
}
19101947

0 commit comments

Comments
 (0)