Skip to content

Commit 15d1560

Browse files
authored
[Clang] Improve EmitClangAttrSpellingListIndex (#114899)
`EmitClangAttrSpellingListIndex()` performs a lot of unnecessary string comparisons which is wasteful in time and stack space. This commit attempts to refactor this method to be more performant.
1 parent 09fb01a commit 15d1560

File tree

3 files changed

+80
-11
lines changed

3 files changed

+80
-11
lines changed

clang/include/clang/Basic/AttributeCommonInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class AttributeCommonInfo {
6767
IgnoredAttribute,
6868
UnknownAttribute,
6969
};
70+
enum class Scope { NONE, CLANG, GNU, MSVC, OMP, HLSL, GSL, RISCV };
7071

7172
private:
7273
const IdentifierInfo *AttrName = nullptr;

clang/lib/Basic/Attributes.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "clang/Basic/ParsedAttrInfo.h"
1818
#include "clang/Basic/TargetInfo.h"
1919

20+
#include "llvm/ADT/StringMap.h"
21+
2022
using namespace clang;
2123

2224
static int hasAttributeImpl(AttributeCommonInfo::Syntax Syntax, StringRef Name,
@@ -153,12 +155,37 @@ std::string AttributeCommonInfo::getNormalizedFullName() const {
153155
normalizeName(getAttrName(), getScopeName(), getSyntax()));
154156
}
155157

158+
// Sorted list of attribute scope names
159+
static constexpr std::pair<StringRef, AttributeCommonInfo::Scope> ScopeList[] =
160+
{{"", AttributeCommonInfo::Scope::NONE},
161+
{"clang", AttributeCommonInfo::Scope::CLANG},
162+
{"gnu", AttributeCommonInfo::Scope::GNU},
163+
{"gsl", AttributeCommonInfo::Scope::GSL},
164+
{"hlsl", AttributeCommonInfo::Scope::HLSL},
165+
{"msvc", AttributeCommonInfo::Scope::MSVC},
166+
{"omp", AttributeCommonInfo::Scope::OMP},
167+
{"riscv", AttributeCommonInfo::Scope::RISCV}};
168+
169+
AttributeCommonInfo::Scope
170+
getScopeFromNormalizedScopeName(StringRef ScopeName) {
171+
auto It = std::lower_bound(
172+
std::begin(ScopeList), std::end(ScopeList), ScopeName,
173+
[](const std::pair<StringRef, AttributeCommonInfo::Scope> &Element,
174+
StringRef Value) { return Element.first < Value; });
175+
assert(It != std::end(ScopeList) && It->first == ScopeName);
176+
177+
return It->second;
178+
}
179+
156180
unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
157181
// Both variables will be used in tablegen generated
158182
// attribute spell list index matching code.
159183
auto Syntax = static_cast<AttributeCommonInfo::Syntax>(getSyntax());
160-
StringRef Scope = normalizeAttrScopeName(getScopeName(), Syntax);
161-
StringRef Name = normalizeAttrName(getAttrName(), Scope, Syntax);
184+
StringRef ScopeName = normalizeAttrScopeName(getScopeName(), Syntax);
185+
StringRef Name = normalizeAttrName(getAttrName(), ScopeName, Syntax);
186+
187+
AttributeCommonInfo::Scope ComputedScope =
188+
getScopeFromNormalizedScopeName(ScopeName);
162189

163190
#include "clang/Sema/AttrSpellingListIndex.inc"
164191
}

clang/utils/TableGen/ClangAttrEmitter.cpp

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ADT/STLExtras.h"
2121
#include "llvm/ADT/SmallString.h"
2222
#include "llvm/ADT/StringExtras.h"
23+
#include "llvm/ADT/StringMap.h"
2324
#include "llvm/ADT/StringRef.h"
2425
#include "llvm/ADT/StringSet.h"
2526
#include "llvm/ADT/StringSwitch.h"
@@ -3843,19 +3844,59 @@ void EmitClangAttrSpellingListIndex(const RecordKeeper &Records,
38433844
const Record &R = *I.second;
38443845
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
38453846
OS << " case AT_" << I.first << ": {\n";
3846-
for (unsigned I = 0; I < Spellings.size(); ++ I) {
3847-
OS << " if (Name == \"" << Spellings[I].name() << "\" && "
3848-
<< "getSyntax() == AttributeCommonInfo::AS_" << Spellings[I].variety()
3849-
<< " && Scope == \"" << Spellings[I].nameSpace() << "\")\n"
3850-
<< " return " << I << ";\n";
3847+
3848+
// If there are none or one spelling to check, resort to the default
3849+
// behavior of returning index as 0.
3850+
if (Spellings.size() <= 1) {
3851+
OS << " return 0;\n"
3852+
<< " break;\n"
3853+
<< " }\n";
3854+
continue;
38513855
}
38523856

3853-
OS << " break;\n";
3854-
OS << " }\n";
3857+
std::vector<StringRef> Names;
3858+
llvm::transform(Spellings, std::back_inserter(Names),
3859+
[](const FlattenedSpelling &FS) { return FS.name(); });
3860+
llvm::sort(Names);
3861+
Names.erase(llvm::unique(Names), Names.end());
3862+
3863+
for (const auto &[Idx, FS] : enumerate(Spellings)) {
3864+
OS << " if (";
3865+
if (Names.size() > 1) {
3866+
SmallVector<StringRef, 6> SameLenNames;
3867+
llvm::copy_if(
3868+
Names, std::back_inserter(SameLenNames),
3869+
[&](StringRef N) { return N.size() == FS.name().size(); });
3870+
3871+
if (SameLenNames.size() == 1) {
3872+
OS << "Name.size() == " << FS.name().size() << " && ";
3873+
} else {
3874+
// FIXME: We currently fall back to comparing entire strings if there
3875+
// are 2 or more spelling names with the same length. This can be
3876+
// optimized to check only for the the first differing character
3877+
// between them instead.
3878+
OS << "Name == \"" << FS.name() << "\""
3879+
<< " && ";
3880+
}
3881+
}
3882+
3883+
OS << "getSyntax() == AttributeCommonInfo::AS_" << FS.variety()
3884+
<< " && ComputedScope == ";
3885+
if (FS.nameSpace() == "")
3886+
OS << "AttributeCommonInfo::Scope::NONE";
3887+
else
3888+
OS << "AttributeCommonInfo::Scope::" + FS.nameSpace().upper();
3889+
3890+
OS << ")\n"
3891+
<< " return " << Idx << ";\n";
3892+
}
3893+
3894+
OS << " break;\n"
3895+
<< " }\n";
38553896
}
38563897

3857-
OS << " }\n";
3858-
OS << " return 0;\n";
3898+
OS << " }\n"
3899+
<< " return 0;\n";
38593900
}
38603901

38613902
// Emits code used by RecursiveASTVisitor to visit attributes

0 commit comments

Comments
 (0)