Skip to content

Commit 1be64e5

Browse files
[clang][Sema] Add instant event when template instantiation is deferred. (#111524)
While profiling a clang invocation using `-ftime-trace`, now we add instant events when template instantiation is deferred. These events include the fully qualified name of the function template being deferred and therefore could be very verbose. This is therefore only added in verbose mode (when `TimeTraceVerbose` is enabled). The point of time when a particular instantiation is deferred can be used to identify the parent TimeTrace scope (usually another function instantiation), which is responsible for deferring this instantiation. This relationship can be used to attribute the cost of a deferred template instantiation to the function deferring this particular instantiation.
1 parent 068d76b commit 1be64e5

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

clang/lib/Sema/SemaExpr.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/ASTLambda.h"
1919
#include "clang/AST/ASTMutationListener.h"
2020
#include "clang/AST/CXXInheritance.h"
21+
#include "clang/AST/Decl.h"
2122
#include "clang/AST/DeclObjC.h"
2223
#include "clang/AST/DeclTemplate.h"
2324
#include "clang/AST/EvaluatedExprVisitor.h"
@@ -65,6 +66,7 @@
6566
#include "llvm/Support/Casting.h"
6667
#include "llvm/Support/ConvertUTF.h"
6768
#include "llvm/Support/SaveAndRestore.h"
69+
#include "llvm/Support/TimeProfiler.h"
6870
#include "llvm/Support/TypeSize.h"
6971
#include <optional>
7072

@@ -18144,6 +18146,15 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
1814418146
Func->setInstantiationIsPending(true);
1814518147
PendingInstantiations.push_back(
1814618148
std::make_pair(Func, PointOfInstantiation));
18149+
if (llvm::isTimeTraceVerbose()) {
18150+
llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] {
18151+
std::string Name;
18152+
llvm::raw_string_ostream OS(Name);
18153+
Func->getNameForDiagnostic(OS, getPrintingPolicy(),
18154+
/*Qualified=*/true);
18155+
return Name;
18156+
});
18157+
}
1814718158
// Notify the consumer that a function was implicitly instantiated.
1814818159
Consumer.HandleCXXImplicitFunctionInstantiation(Func);
1814918160
}

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5015,6 +5015,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
50155015
Function->setInstantiationIsPending(true);
50165016
PendingInstantiations.push_back(
50175017
std::make_pair(Function, PointOfInstantiation));
5018+
5019+
if (llvm::isTimeTraceVerbose()) {
5020+
llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] {
5021+
std::string Name;
5022+
llvm::raw_string_ostream OS(Name);
5023+
Function->getNameForDiagnostic(OS, getPrintingPolicy(),
5024+
/*Qualified=*/true);
5025+
return Name;
5026+
});
5027+
}
50185028
} else if (TSK == TSK_ImplicitInstantiation) {
50195029
if (AtEndOfTU && !getDiagnostics().hasErrorOccurred() &&
50205030
!getSourceManager().isInSystemHeader(PatternDecl->getBeginLoc())) {

clang/unittests/Support/TimeProfilerTest.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,55 @@ Frontend (test.cc)
238238
buildTraceGraph(Json));
239239
}
240240

241+
TEST(TimeProfilerTest, ClassTemplateInstantiations) {
242+
std::string Code = R"(
243+
template<class T>
244+
struct S
245+
{
246+
void foo() {}
247+
void bar();
248+
};
249+
250+
template struct S<double>; // explicit instantiation of S<double>
251+
252+
void user() {
253+
S<int> a; // implicit instantiation of S<int>
254+
S<float>* b;
255+
b->foo(); // implicit instatiation of S<float> and S<float>::foo()
256+
}
257+
)";
258+
259+
setupProfiler();
260+
ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
261+
std::string Json = teardownProfiler();
262+
ASSERT_EQ(R"(
263+
Frontend (test.cc)
264+
| ParseClass (S)
265+
| InstantiateClass (S<double>, test.cc:9)
266+
| InstantiateFunction (S<double>::foo, test.cc:5)
267+
| ParseDeclarationOrFunctionDefinition (test.cc:11:5)
268+
| | ParseFunctionDefinition (user)
269+
| | | InstantiateClass (S<int>, test.cc:3)
270+
| | | InstantiateClass (S<float>, test.cc:3)
271+
| | | DeferInstantiation (S<float>::foo)
272+
| PerformPendingInstantiations
273+
| | InstantiateFunction (S<float>::foo, test.cc:5)
274+
)",
275+
buildTraceGraph(Json));
276+
}
277+
241278
TEST(TimeProfilerTest, TemplateInstantiations) {
242279
std::string B_H = R"(
243280
template <typename T>
244-
T fooB(T t) {
281+
T fooC(T t) {
245282
return T();
246283
}
247284
285+
template <typename T>
286+
constexpr T fooB(T t) {
287+
return fooC(t);
288+
}
289+
248290
#define MacroTemp(x) template <typename T> void foo##x(T) { T(); }
249291
)";
250292

@@ -267,14 +309,19 @@ TEST(TimeProfilerTest, TemplateInstantiations) {
267309
std::string Json = teardownProfiler();
268310
ASSERT_EQ(R"(
269311
Frontend (test.cc)
312+
| ParseFunctionDefinition (fooC)
270313
| ParseFunctionDefinition (fooB)
271314
| ParseFunctionDefinition (fooMTA)
272315
| ParseFunctionDefinition (fooA)
273316
| ParseDeclarationOrFunctionDefinition (test.cc:3:5)
274317
| | ParseFunctionDefinition (user)
318+
| | | DeferInstantiation (fooA<int>)
275319
| PerformPendingInstantiations
276320
| | InstantiateFunction (fooA<int>, a.h:7)
277-
| | | InstantiateFunction (fooB<int>, b.h:3)
321+
| | | InstantiateFunction (fooB<int>, b.h:8)
322+
| | | | DeferInstantiation (fooC<int>)
323+
| | | DeferInstantiation (fooMTA<int>)
324+
| | | InstantiateFunction (fooC<int>, b.h:3)
278325
| | | InstantiateFunction (fooMTA<int>, a.h:4)
279326
)",
280327
buildTraceGraph(Json));

0 commit comments

Comments
 (0)