Skip to content

Commit c613ae3

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 d9e79cc commit c613ae3

File tree

2 files changed

+63
-18
lines changed

2 files changed

+63
-18
lines changed

llvm/lib/Transforms/Coroutines/CoroCloner.h

+20-11
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

+43-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,27 @@ 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+
MetadataSetTy CommonDebugInfo;
91+
DebugInfoFinder DIFinder;
92+
DISubprogram *SPClonedWithinModule = CollectDebugInfoForCloning(
93+
F, CloneFunctionChangeType::LocalChangesOnly, DIFinder);
94+
95+
FindDebugInfoToIdentityMap(CommonDebugInfo,
96+
CloneFunctionChangeType::LocalChangesOnly,
97+
DIFinder, SPClonedWithinModule);
98+
return CommonDebugInfo;
99+
}
100+
} // end anonymous namespace
101+
80102
// FIXME:
81103
// Lower the intrinisc in CoroEarly phase if coroutine frame doesn't escape
82104
// and it is known that other transformations, for example, sanitizers
@@ -891,8 +913,11 @@ void coro::BaseCloner::create() {
891913
auto savedLinkage = NewF->getLinkage();
892914
NewF->setLinkage(llvm::GlobalValue::ExternalLinkage);
893915

894-
CloneFunctionInto(NewF, &OrigF, VMap,
895-
CloneFunctionChangeType::LocalChangesOnly, Returns);
916+
CloneFunctionAttributesInto(NewF, &OrigF, VMap, false);
917+
CloneFunctionMetadataInto(*NewF, OrigF, VMap, RF_None, nullptr, nullptr,
918+
&CommonDebugInfo);
919+
CloneFunctionBodyInto(*NewF, OrigF, VMap, RF_None, Returns, "", nullptr,
920+
nullptr, nullptr, &CommonDebugInfo);
896921

897922
auto &Context = NewF->getContext();
898923

@@ -1374,16 +1399,21 @@ struct SwitchCoroutineSplitter {
13741399
TargetTransformInfo &TTI) {
13751400
assert(Shape.ABI == coro::ABI::Switch);
13761401

1402+
MetadataSetTy CommonDebugInfo{collectCommonDebugInfo(F)};
1403+
13771404
// Create a resume clone by cloning the body of the original function,
13781405
// setting new entry block and replacing coro.suspend an appropriate value
13791406
// to force resume or cleanup pass for every suspend point.
13801407
createResumeEntryBlock(F, Shape);
13811408
auto *ResumeClone = coro::SwitchCloner::createClone(
1382-
F, ".resume", Shape, coro::CloneKind::SwitchResume, TTI);
1409+
F, ".resume", Shape, coro::CloneKind::SwitchResume, TTI,
1410+
CommonDebugInfo);
13831411
auto *DestroyClone = coro::SwitchCloner::createClone(
1384-
F, ".destroy", Shape, coro::CloneKind::SwitchUnwind, TTI);
1412+
F, ".destroy", Shape, coro::CloneKind::SwitchUnwind, TTI,
1413+
CommonDebugInfo);
13851414
auto *CleanupClone = coro::SwitchCloner::createClone(
1386-
F, ".cleanup", Shape, coro::CloneKind::SwitchCleanup, TTI);
1415+
F, ".cleanup", Shape, coro::CloneKind::SwitchCleanup, TTI,
1416+
CommonDebugInfo);
13871417

13881418
postSplitCleanup(*ResumeClone);
13891419
postSplitCleanup(*DestroyClone);
@@ -1768,12 +1798,15 @@ void coro::AsyncABI::splitCoroutine(Function &F, coro::Shape &Shape,
17681798
}
17691799

17701800
assert(Clones.size() == Shape.CoroSuspends.size());
1801+
1802+
MetadataSetTy CommonDebugInfo{collectCommonDebugInfo(F)};
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, CommonDebugInfo);
17771810
}
17781811
}
17791812

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

19011934
assert(Clones.size() == Shape.CoroSuspends.size());
1935+
1936+
MetadataSetTy CommonDebugInfo{collectCommonDebugInfo(F)};
1937+
19021938
for (auto [Idx, CS] : llvm::enumerate(Shape.CoroSuspends)) {
19031939
auto Suspend = CS;
19041940
auto Clone = Clones[Idx];
19051941

19061942
coro::BaseCloner::createClone(F, "resume." + Twine(Idx), Shape, Clone,
1907-
Suspend, TTI);
1943+
Suspend, TTI, CommonDebugInfo);
19081944
}
19091945
}
19101946

0 commit comments

Comments
 (0)