Skip to content

Commit 5bbda30

Browse files
committed
[Coro] Prebuild a module-level 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 common module-level debug info metadata. For programs compiled with full debug info this can get 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 CommonDI (cur.) | |-----------------|----------|----------------|--------------------------| | CoroSplitPass | 306ms | 221ms | 68ms | | CoroCloner | 101ms | 72ms | 0.5ms | | CollectGlobalDI | - | - | 63ms | | Speed up | 1x | 1.4x | 4.5x | Note that CollectCommonDebugInfo 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 196f7c2 commit 5bbda30

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

llvm/lib/Transforms/Coroutines/CoroCloner.h

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ class BaseCloner {
4848
CloneKind FKind;
4949
IRBuilder<> Builder;
5050
TargetTransformInfo &TTI;
51+
// Common module-level metadata that's shared between all coroutine clones and
52+
// doesn't need to be cloned itself.
53+
const MetadataSetTy &CommonDebugInfo;
5154

5255
ValueToValueMapTy VMap;
5356
Function *NewF = nullptr;
@@ -60,12 +63,12 @@ class BaseCloner {
6063
/// Create a cloner for a continuation lowering.
6164
BaseCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
6265
Function *NewF, AnyCoroSuspendInst *ActiveSuspend,
63-
TargetTransformInfo &TTI)
66+
TargetTransformInfo &TTI, const MetadataSetTy &CommonDebugInfo)
6467
: OrigF(OrigF), Suffix(Suffix), Shape(Shape),
6568
FKind(Shape.ABI == ABI::Async ? CloneKind::Async
6669
: CloneKind::Continuation),
67-
Builder(OrigF.getContext()), TTI(TTI), NewF(NewF),
68-
ActiveSuspend(ActiveSuspend) {
70+
Builder(OrigF.getContext()), TTI(TTI), CommonDebugInfo(CommonDebugInfo),
71+
NewF(NewF), ActiveSuspend(ActiveSuspend) {
6972
assert(Shape.ABI == ABI::Retcon || Shape.ABI == ABI::RetconOnce ||
7073
Shape.ABI == ABI::Async);
7174
assert(NewF && "need existing function for continuation");
@@ -74,22 +77,26 @@ class BaseCloner {
7477

7578
public:
7679
BaseCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
77-
CloneKind FKind, TargetTransformInfo &TTI)
80+
CloneKind FKind, TargetTransformInfo &TTI,
81+
const MetadataSetTy &CommonDebugInfo)
7882
: OrigF(OrigF), Suffix(Suffix), Shape(Shape), FKind(FKind),
79-
Builder(OrigF.getContext()), TTI(TTI) {}
83+
Builder(OrigF.getContext()), TTI(TTI),
84+
CommonDebugInfo(CommonDebugInfo) {}
8085

8186
virtual ~BaseCloner() {}
8287

8388
/// Create a clone for a continuation lowering.
8489
static Function *createClone(Function &OrigF, const Twine &Suffix,
8590
coro::Shape &Shape, Function *NewF,
8691
AnyCoroSuspendInst *ActiveSuspend,
87-
TargetTransformInfo &TTI) {
92+
TargetTransformInfo &TTI,
93+
const MetadataSetTy &CommonDebugInfo) {
8894
assert(Shape.ABI == ABI::Retcon || Shape.ABI == ABI::RetconOnce ||
8995
Shape.ABI == ABI::Async);
9096
TimeTraceScope FunctionScope("BaseCloner");
9197

92-
BaseCloner Cloner(OrigF, Suffix, Shape, NewF, ActiveSuspend, TTI);
98+
BaseCloner Cloner(OrigF, Suffix, Shape, NewF, ActiveSuspend, TTI,
99+
CommonDebugInfo);
93100
Cloner.create();
94101
return Cloner.getFunction();
95102
}
@@ -129,20 +136,22 @@ class SwitchCloner : public BaseCloner {
129136
protected:
130137
/// Create a cloner for a switch lowering.
131138
SwitchCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
132-
CloneKind FKind, TargetTransformInfo &TTI)
133-
: BaseCloner(OrigF, Suffix, Shape, FKind, TTI) {}
139+
CloneKind FKind, TargetTransformInfo &TTI,
140+
const MetadataSetTy &CommonDebugInfo)
141+
: BaseCloner(OrigF, Suffix, Shape, FKind, TTI, CommonDebugInfo) {}
134142

135143
void create() override;
136144

137145
public:
138146
/// Create a clone for a switch lowering.
139147
static Function *createClone(Function &OrigF, const Twine &Suffix,
140148
coro::Shape &Shape, CloneKind FKind,
141-
TargetTransformInfo &TTI) {
149+
TargetTransformInfo &TTI,
150+
const MetadataSetTy &CommonDebugInfo) {
142151
assert(Shape.ABI == ABI::Switch);
143152
TimeTraceScope FunctionScope("SwitchCloner");
144153

145-
SwitchCloner Cloner(OrigF, Suffix, Shape, FKind, TTI);
154+
SwitchCloner Cloner(OrigF, Suffix, Shape, FKind, TTI, CommonDebugInfo);
146155
Cloner.create();
147156
return Cloner.getFunction();
148157
}

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 40 additions & 7 deletions
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,24 @@ 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+
MetadataSetTy collectCommonDebugInfo(Function &F) {
88+
TimeTraceScope FunctionScope("CollectCommonDebugInfo");
89+
90+
DebugInfoFinder DIFinder;
91+
DISubprogram *SPClonedWithinModule = CollectDebugInfoForCloning(
92+
F, CloneFunctionChangeType::LocalChangesOnly, DIFinder);
93+
94+
return FindDebugInfoToIdentityMap(CloneFunctionChangeType::LocalChangesOnly,
95+
DIFinder, SPClonedWithinModule);
96+
}
97+
} // end anonymous namespace
98+
8099
// FIXME:
81100
// Lower the intrinisc in CoroEarly phase if coroutine frame doesn't escape
82101
// and it is known that other transformations, for example, sanitizers
@@ -891,8 +910,11 @@ void coro::BaseCloner::create() {
891910
auto savedLinkage = NewF->getLinkage();
892911
NewF->setLinkage(llvm::GlobalValue::ExternalLinkage);
893912

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

897919
auto &Context = NewF->getContext();
898920

@@ -1374,16 +1396,21 @@ struct SwitchCoroutineSplitter {
13741396
TargetTransformInfo &TTI) {
13751397
assert(Shape.ABI == coro::ABI::Switch);
13761398

1399+
MetadataSetTy CommonDebugInfo{collectCommonDebugInfo(F)};
1400+
13771401
// Create a resume clone by cloning the body of the original function,
13781402
// setting new entry block and replacing coro.suspend an appropriate value
13791403
// to force resume or cleanup pass for every suspend point.
13801404
createResumeEntryBlock(F, Shape);
13811405
auto *ResumeClone = coro::SwitchCloner::createClone(
1382-
F, ".resume", Shape, coro::CloneKind::SwitchResume, TTI);
1406+
F, ".resume", Shape, coro::CloneKind::SwitchResume, TTI,
1407+
CommonDebugInfo);
13831408
auto *DestroyClone = coro::SwitchCloner::createClone(
1384-
F, ".destroy", Shape, coro::CloneKind::SwitchUnwind, TTI);
1409+
F, ".destroy", Shape, coro::CloneKind::SwitchUnwind, TTI,
1410+
CommonDebugInfo);
13851411
auto *CleanupClone = coro::SwitchCloner::createClone(
1386-
F, ".cleanup", Shape, coro::CloneKind::SwitchCleanup, TTI);
1412+
F, ".cleanup", Shape, coro::CloneKind::SwitchCleanup, TTI,
1413+
CommonDebugInfo);
13871414

13881415
postSplitCleanup(*ResumeClone);
13891416
postSplitCleanup(*DestroyClone);
@@ -1768,12 +1795,15 @@ void coro::AsyncABI::splitCoroutine(Function &F, coro::Shape &Shape,
17681795
}
17691796

17701797
assert(Clones.size() == Shape.CoroSuspends.size());
1798+
1799+
MetadataSetTy CommonDebugInfo{collectCommonDebugInfo(F)};
1800+
17711801
for (auto [Idx, CS] : llvm::enumerate(Shape.CoroSuspends)) {
17721802
auto *Suspend = CS;
17731803
auto *Clone = Clones[Idx];
17741804

17751805
coro::BaseCloner::createClone(F, "resume." + Twine(Idx), Shape, Clone,
1776-
Suspend, TTI);
1806+
Suspend, TTI, CommonDebugInfo);
17771807
}
17781808
}
17791809

@@ -1899,12 +1929,15 @@ void coro::AnyRetconABI::splitCoroutine(Function &F, coro::Shape &Shape,
18991929
}
19001930

19011931
assert(Clones.size() == Shape.CoroSuspends.size());
1932+
1933+
MetadataSetTy CommonDebugInfo{collectCommonDebugInfo(F)};
1934+
19021935
for (auto [Idx, CS] : llvm::enumerate(Shape.CoroSuspends)) {
19031936
auto Suspend = CS;
19041937
auto Clone = Clones[Idx];
19051938

19061939
coro::BaseCloner::createClone(F, "resume." + Twine(Idx), Shape, Clone,
1907-
Suspend, TTI);
1940+
Suspend, TTI, CommonDebugInfo);
19081941
}
19091942
}
19101943

0 commit comments

Comments
 (0)