Skip to content

Commit 901a816

Browse files
[clang] Optimize castToDeclContext for 2% improvement in build times (#76825)
Optimize castToDeclContext for 2% improvement in build times castToDeclContext is a heavily used function, and as such, it needs to be kept as slim as feasible to preserve as much performance as possible. To this end, it was observed that the function was generating suboptimal assembly code, and putting the most common execution path in the longest sequence of instructions. This patch addresses this by guiding the compiler towards generating a lookup table of offsets, which can be used to perform an addition on the pointer. This results in a 1-2% improvement on debug builds (and a negligible improvement on release). To achieve this, the switch was simplified to flatten the if statements in the default branch. In order to make the values of the switch more compact, encouraging LLVM to generate a look-up table instead of a jump table, the AST TableGen generator was modified so it can take order priority based on class inheritance. This combination allowed for a more optimal generation of the function. Of note, 2 other functions with an equivalent structure also needed to be modified. Fixes #76824
1 parent e947b63 commit 901a816

File tree

5 files changed

+97
-77
lines changed

5 files changed

+97
-77
lines changed

clang/include/clang/AST/DeclCXX.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,6 +2044,14 @@ class RequiresExprBodyDecl : public Decl, public DeclContext {
20442044
// Implement isa/cast/dyncast/etc.
20452045
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
20462046
static bool classofKind(Kind K) { return K == RequiresExprBody; }
2047+
2048+
static DeclContext *castToDeclContext(const RequiresExprBodyDecl *D) {
2049+
return static_cast<DeclContext *>(const_cast<RequiresExprBodyDecl *>(D));
2050+
}
2051+
2052+
static RequiresExprBodyDecl *castFromDeclContext(const DeclContext *DC) {
2053+
return static_cast<RequiresExprBodyDecl *>(const_cast<DeclContext *>(DC));
2054+
}
20472055
};
20482056

20492057
/// Represents a static or instance method of a struct/union/class.

clang/lib/AST/DeclBase.cpp

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -930,39 +930,27 @@ const AttrVec &Decl::getAttrs() const {
930930

931931
Decl *Decl::castFromDeclContext (const DeclContext *D) {
932932
Decl::Kind DK = D->getDeclKind();
933-
switch(DK) {
934-
#define DECL(NAME, BASE)
935-
#define DECL_CONTEXT(NAME) \
936-
case Decl::NAME: \
937-
return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D));
938-
#define DECL_CONTEXT_BASE(NAME)
939-
#include "clang/AST/DeclNodes.inc"
940-
default:
933+
switch (DK) {
941934
#define DECL(NAME, BASE)
942-
#define DECL_CONTEXT_BASE(NAME) \
943-
if (DK >= first##NAME && DK <= last##NAME) \
944-
return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D));
935+
#define DECL_CONTEXT(NAME) \
936+
case Decl::NAME: \
937+
return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D));
945938
#include "clang/AST/DeclNodes.inc"
946-
llvm_unreachable("a decl that inherits DeclContext isn't handled");
939+
default:
940+
llvm_unreachable("a decl that inherits DeclContext isn't handled");
947941
}
948942
}
949943

950944
DeclContext *Decl::castToDeclContext(const Decl *D) {
951945
Decl::Kind DK = D->getKind();
952946
switch(DK) {
953947
#define DECL(NAME, BASE)
954-
#define DECL_CONTEXT(NAME) \
955-
case Decl::NAME: \
956-
return static_cast<NAME##Decl *>(const_cast<Decl *>(D));
957-
#define DECL_CONTEXT_BASE(NAME)
948+
#define DECL_CONTEXT(NAME) \
949+
case Decl::NAME: \
950+
return static_cast<NAME##Decl *>(const_cast<Decl *>(D));
958951
#include "clang/AST/DeclNodes.inc"
959-
default:
960-
#define DECL(NAME, BASE)
961-
#define DECL_CONTEXT_BASE(NAME) \
962-
if (DK >= first##NAME && DK <= last##NAME) \
963-
return static_cast<NAME##Decl *>(const_cast<Decl *>(D));
964-
#include "clang/AST/DeclNodes.inc"
965-
llvm_unreachable("a decl that inherits DeclContext isn't handled");
952+
default:
953+
llvm_unreachable("a decl that inherits DeclContext isn't handled");
966954
}
967955
}
968956

@@ -1129,20 +1117,14 @@ DeclContext::DeclContext(Decl::Kind K) {
11291117
}
11301118

11311119
bool DeclContext::classof(const Decl *D) {
1132-
switch (D->getKind()) {
1120+
Decl::Kind DK = D->getKind();
1121+
switch (DK) {
11331122
#define DECL(NAME, BASE)
11341123
#define DECL_CONTEXT(NAME) case Decl::NAME:
1135-
#define DECL_CONTEXT_BASE(NAME)
11361124
#include "clang/AST/DeclNodes.inc"
1137-
return true;
1138-
default:
1139-
#define DECL(NAME, BASE)
1140-
#define DECL_CONTEXT_BASE(NAME) \
1141-
if (D->getKind() >= Decl::first##NAME && \
1142-
D->getKind() <= Decl::last##NAME) \
1143-
return true;
1144-
#include "clang/AST/DeclNodes.inc"
1145-
return false;
1125+
return true;
1126+
default:
1127+
return false;
11461128
}
11471129
}
11481130

clang/utils/TableGen/ClangASTNodesEmitter.cpp

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class ClangASTNodesEmitter {
3333
typedef std::multimap<ASTNode, ASTNode> ChildMap;
3434
typedef ChildMap::const_iterator ChildIterator;
3535

36+
std::set<ASTNode> PrioritizedClasses;
3637
RecordKeeper &Records;
3738
ASTNode Root;
3839
const std::string &NodeClassName;
@@ -70,8 +71,16 @@ class ClangASTNodesEmitter {
7071
std::pair<ASTNode, ASTNode> EmitNode(raw_ostream& OS, ASTNode Base);
7172
public:
7273
explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N,
73-
const std::string &S)
74-
: Records(R), NodeClassName(N), BaseSuffix(S) {}
74+
const std::string &S,
75+
std::string_view PriorizeIfSubclassOf)
76+
: Records(R), NodeClassName(N), BaseSuffix(S) {
77+
auto vecPrioritized =
78+
PriorizeIfSubclassOf.empty()
79+
? std::vector<Record *>{}
80+
: R.getAllDerivedDefinitions(PriorizeIfSubclassOf);
81+
PrioritizedClasses =
82+
std::set<ASTNode>(vecPrioritized.begin(), vecPrioritized.end());
83+
}
7584

7685
// run - Output the .inc file contents
7786
void run(raw_ostream &OS);
@@ -95,8 +104,23 @@ std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
95104
if (!Base.isAbstract())
96105
First = Last = Base;
97106

107+
auto comp = [this](ASTNode LHS, ASTNode RHS) {
108+
auto LHSPrioritized = PrioritizedClasses.count(LHS) > 0;
109+
auto RHSPrioritized = PrioritizedClasses.count(RHS) > 0;
110+
if (LHSPrioritized && !RHSPrioritized)
111+
return true;
112+
if (!LHSPrioritized && RHSPrioritized)
113+
return false;
114+
115+
return LHS.getName() > RHS.getName();
116+
};
117+
auto SortedChildren = std::set<ASTNode, decltype(comp)>(comp);
118+
98119
for (; i != e; ++i) {
99-
ASTNode Child = i->second;
120+
SortedChildren.insert(i->second);
121+
}
122+
123+
for (const auto &Child : SortedChildren) {
100124
bool Abstract = Child.isAbstract();
101125
std::string NodeName = macroName(std::string(Child.getName()));
102126

@@ -148,9 +172,7 @@ void ClangASTNodesEmitter::deriveChildTree() {
148172
const std::vector<Record*> Stmts
149173
= Records.getAllDerivedDefinitions(NodeClassName);
150174

151-
for (unsigned i = 0, e = Stmts.size(); i != e; ++i) {
152-
Record *R = Stmts[i];
153-
175+
for (auto *R : Stmts) {
154176
if (auto B = R->getValueAsOptionalDef(BaseFieldName))
155177
Tree.insert(std::make_pair(B, R));
156178
else if (Root)
@@ -182,9 +204,9 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) {
182204
OS << "#endif\n\n";
183205

184206
OS << "#ifndef LAST_" << macroHierarchyName() << "_RANGE\n";
185-
OS << "# define LAST_"
186-
<< macroHierarchyName() << "_RANGE(Base, First, Last) "
187-
<< macroHierarchyName() << "_RANGE(Base, First, Last)\n";
207+
OS << "# define LAST_" << macroHierarchyName()
208+
<< "_RANGE(Base, First, Last) " << macroHierarchyName()
209+
<< "_RANGE(Base, First, Last)\n";
188210
OS << "#endif\n\n";
189211

190212
EmitNode(OS, Root);
@@ -196,8 +218,20 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) {
196218
}
197219

198220
void clang::EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS,
199-
const std::string &N, const std::string &S) {
200-
ClangASTNodesEmitter(RK, N, S).run(OS);
221+
const std::string &N, const std::string &S,
222+
std::string_view PriorizeIfSubclassOf) {
223+
ClangASTNodesEmitter(RK, N, S, PriorizeIfSubclassOf).run(OS);
224+
}
225+
226+
void printDeclContext(const std::multimap<Record *, Record *> &Tree,
227+
Record *DeclContext, raw_ostream &OS) {
228+
if (!DeclContext->getValueAsBit(AbstractFieldName))
229+
OS << "DECL_CONTEXT(" << DeclContext->getName() << ")\n";
230+
auto i = Tree.lower_bound(DeclContext);
231+
auto end = Tree.upper_bound(DeclContext);
232+
for (; i != end; ++i) {
233+
printDeclContext(Tree, i->second, OS);
234+
}
201235
}
202236

203237
// Emits and addendum to a .inc file to enumerate the clang declaration
@@ -210,38 +244,25 @@ void clang::EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) {
210244
OS << "#ifndef DECL_CONTEXT\n";
211245
OS << "# define DECL_CONTEXT(DECL)\n";
212246
OS << "#endif\n";
213-
214-
OS << "#ifndef DECL_CONTEXT_BASE\n";
215-
OS << "# define DECL_CONTEXT_BASE(DECL) DECL_CONTEXT(DECL)\n";
216-
OS << "#endif\n";
217-
218-
typedef std::set<Record*> RecordSet;
219-
typedef std::vector<Record*> RecordVector;
220-
221-
RecordVector DeclContextsVector
222-
= Records.getAllDerivedDefinitions(DeclContextNodeClassName);
223-
RecordVector Decls = Records.getAllDerivedDefinitions(DeclNodeClassName);
224-
RecordSet DeclContexts (DeclContextsVector.begin(), DeclContextsVector.end());
225-
226-
for (RecordVector::iterator i = Decls.begin(), e = Decls.end(); i != e; ++i) {
227-
Record *R = *i;
228-
229-
if (Record *B = R->getValueAsOptionalDef(BaseFieldName)) {
230-
if (DeclContexts.find(B) != DeclContexts.end()) {
231-
OS << "DECL_CONTEXT_BASE(" << B->getName() << ")\n";
232-
DeclContexts.erase(B);
233-
}
234-
}
247+
248+
std::vector<Record *> DeclContextsVector =
249+
Records.getAllDerivedDefinitions(DeclContextNodeClassName);
250+
std::vector<Record *> Decls =
251+
Records.getAllDerivedDefinitions(DeclNodeClassName);
252+
253+
std::multimap<Record *, Record *> Tree;
254+
255+
const std::vector<Record *> Stmts =
256+
Records.getAllDerivedDefinitions(DeclNodeClassName);
257+
258+
for (auto *R : Stmts) {
259+
if (auto *B = R->getValueAsOptionalDef(BaseFieldName))
260+
Tree.insert(std::make_pair(B, R));
235261
}
236262

237-
// To keep identical order, RecordVector may be used
238-
// instead of RecordSet.
239-
for (RecordVector::iterator
240-
i = DeclContextsVector.begin(), e = DeclContextsVector.end();
241-
i != e; ++i)
242-
if (DeclContexts.find(*i) != DeclContexts.end())
243-
OS << "DECL_CONTEXT(" << (*i)->getName() << ")\n";
263+
for (auto *DeclContext : DeclContextsVector) {
264+
printDeclContext(Tree, DeclContext, OS);
265+
}
244266

245267
OS << "#undef DECL_CONTEXT\n";
246-
OS << "#undef DECL_CONTEXT_BASE\n";
247268
}

clang/utils/TableGen/TableGen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
398398
EmitClangASTNodes(Records, OS, CommentNodeClassName, "");
399399
break;
400400
case GenClangDeclNodes:
401-
EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl");
401+
EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl",
402+
DeclContextNodeClassName);
402403
EmitClangDeclContext(Records, OS);
403404
break;
404405
case GenClangStmtNodes:

clang/utils/TableGen/TableGenBackends.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,16 @@ class RecordKeeper;
2525
namespace clang {
2626

2727
void EmitClangDeclContext(llvm::RecordKeeper &RK, llvm::raw_ostream &OS);
28+
/**
29+
@param PriorizeIfSubclassOf These classes should be prioritized in the output.
30+
This is useful to force enum generation/jump tables/lookup tables to be more
31+
compact in both size and surrounding code in hot functions. An example use is
32+
in Decl for classes that inherit from DeclContext, for functions like
33+
castFromDeclContext.
34+
*/
2835
void EmitClangASTNodes(llvm::RecordKeeper &RK, llvm::raw_ostream &OS,
29-
const std::string &N, const std::string &S);
36+
const std::string &N, const std::string &S,
37+
std::string_view PriorizeIfSubclassOf = "");
3038
void EmitClangBasicReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
3139
void EmitClangBasicWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
3240
void EmitClangTypeNodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);

0 commit comments

Comments
 (0)