Skip to content

Commit f0297ae

Browse files
authored
Switch the intrinsic names to a string table (llvm#118929)
This avoids the need to dynamically relocate each pointer in the table. To make this work, this PR also moves the binary search of intrinsic names to an internal function with an adjusted signature, and switches the unittesting to test against actual intrinsics.
1 parent 70c1764 commit f0297ae

File tree

4 files changed

+80
-48
lines changed

4 files changed

+80
-48
lines changed

llvm/include/llvm/IR/Intrinsics.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,6 @@ namespace Intrinsic {
112112
Function *getDeclarationIfExists(Module *M, ID id, ArrayRef<Type *> Tys,
113113
FunctionType *FT = nullptr);
114114

115-
/// Looks up Name in NameTable via binary search. NameTable must be sorted
116-
/// and all entries must start with "llvm.". If NameTable contains an exact
117-
/// match for Name or a prefix of Name followed by a dot, its index in
118-
/// NameTable is returned. Otherwise, -1 is returned.
119-
int lookupLLVMIntrinsicByName(ArrayRef<const char *> NameTable,
120-
StringRef Name, StringRef Target = "");
121-
122115
/// Map a Clang builtin name to an intrinsic ID.
123116
ID getIntrinsicForClangBuiltin(StringRef TargetPrefix, StringRef BuiltinName);
124117

llvm/lib/IR/Intrinsics.cpp

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,13 @@
3434
using namespace llvm;
3535

3636
/// Table of string intrinsic names indexed by enum value.
37-
static constexpr const char *const IntrinsicNameTable[] = {
38-
"not_intrinsic",
3937
#define GET_INTRINSIC_NAME_TABLE
4038
#include "llvm/IR/IntrinsicImpl.inc"
4139
#undef GET_INTRINSIC_NAME_TABLE
42-
};
4340

4441
StringRef Intrinsic::getBaseName(ID id) {
4542
assert(id < num_intrinsics && "Invalid intrinsic ID!");
46-
return IntrinsicNameTable[id];
43+
return IntrinsicNameTable + IntrinsicNameOffsetTable[id];
4744
}
4845

4946
StringRef Intrinsic::getName(ID id) {
@@ -621,9 +618,12 @@ bool Intrinsic::isTargetIntrinsic(Intrinsic::ID IID) {
621618
return IID > TargetInfos[0].Count;
622619
}
623620

624-
int llvm::Intrinsic::lookupLLVMIntrinsicByName(ArrayRef<const char *> NameTable,
625-
StringRef Name,
626-
StringRef Target) {
621+
/// Looks up Name in NameTable via binary search. NameTable must be sorted
622+
/// and all entries must start with "llvm.". If NameTable contains an exact
623+
/// match for Name or a prefix of Name followed by a dot, its index in
624+
/// NameTable is returned. Otherwise, -1 is returned.
625+
static int lookupLLVMIntrinsicByName(ArrayRef<unsigned> NameOffsetTable,
626+
StringRef Name, StringRef Target = "") {
627627
assert(Name.starts_with("llvm.") && "Unexpected intrinsic prefix");
628628
assert(Name.drop_front(5).starts_with(Target) && "Unexpected target");
629629

@@ -638,36 +638,53 @@ int llvm::Intrinsic::lookupLLVMIntrinsicByName(ArrayRef<const char *> NameTable,
638638
if (!Target.empty())
639639
CmpEnd += 1 + Target.size(); // skip the .target component.
640640

641-
const char *const *Low = NameTable.begin();
642-
const char *const *High = NameTable.end();
643-
const char *const *LastLow = Low;
641+
const unsigned *Low = NameOffsetTable.begin();
642+
const unsigned *High = NameOffsetTable.end();
643+
const unsigned *LastLow = Low;
644644
while (CmpEnd < Name.size() && High - Low > 0) {
645645
size_t CmpStart = CmpEnd;
646646
CmpEnd = Name.find('.', CmpStart + 1);
647647
CmpEnd = CmpEnd == StringRef::npos ? Name.size() : CmpEnd;
648-
auto Cmp = [CmpStart, CmpEnd](const char *LHS, const char *RHS) {
649-
return strncmp(LHS + CmpStart, RHS + CmpStart, CmpEnd - CmpStart) < 0;
648+
auto Cmp = [CmpStart, CmpEnd](auto LHS, auto RHS) {
649+
// `equal_range` requires the comparison to work with either side being an
650+
// offset or the value. Detect which kind each side is to set up the
651+
// compared strings.
652+
const char *LHSStr;
653+
if constexpr (std::is_integral_v<decltype(LHS)>) {
654+
LHSStr = &IntrinsicNameTable[LHS];
655+
} else {
656+
LHSStr = LHS;
657+
}
658+
const char *RHSStr;
659+
if constexpr (std::is_integral_v<decltype(RHS)>) {
660+
RHSStr = &IntrinsicNameTable[RHS];
661+
} else {
662+
RHSStr = RHS;
663+
}
664+
return strncmp(LHSStr + CmpStart, RHSStr + CmpStart, CmpEnd - CmpStart) <
665+
0;
650666
};
651667
LastLow = Low;
652668
std::tie(Low, High) = std::equal_range(Low, High, Name.data(), Cmp);
653669
}
654670
if (High - Low > 0)
655671
LastLow = Low;
656672

657-
if (LastLow == NameTable.end())
673+
if (LastLow == NameOffsetTable.end())
658674
return -1;
659-
StringRef NameFound = *LastLow;
675+
StringRef NameFound = &IntrinsicNameTable[*LastLow];
660676
if (Name == NameFound ||
661677
(Name.starts_with(NameFound) && Name[NameFound.size()] == '.'))
662-
return LastLow - NameTable.begin();
678+
return LastLow - NameOffsetTable.begin();
663679
return -1;
664680
}
665681

666-
/// Find the segment of \c IntrinsicNameTable for intrinsics with the same
682+
/// Find the segment of \c IntrinsicNameOffsetTable for intrinsics with the same
667683
/// target as \c Name, or the generic table if \c Name is not target specific.
668684
///
669-
/// Returns the relevant slice of \c IntrinsicNameTable and the target name.
670-
static std::pair<ArrayRef<const char *>, StringRef>
685+
/// Returns the relevant slice of \c IntrinsicNameOffsetTable and the target
686+
/// name.
687+
static std::pair<ArrayRef<unsigned>, StringRef>
671688
findTargetSubtable(StringRef Name) {
672689
assert(Name.starts_with("llvm."));
673690

@@ -680,25 +697,26 @@ findTargetSubtable(StringRef Name) {
680697
// We've either found the target or just fall back to the generic set, which
681698
// is always first.
682699
const auto &TI = It != Targets.end() && It->Name == Target ? *It : Targets[0];
683-
return {ArrayRef(&IntrinsicNameTable[1] + TI.Offset, TI.Count), TI.Name};
700+
return {ArrayRef(&IntrinsicNameOffsetTable[1] + TI.Offset, TI.Count),
701+
TI.Name};
684702
}
685703

686704
/// This does the actual lookup of an intrinsic ID which matches the given
687705
/// function name.
688706
Intrinsic::ID Intrinsic::lookupIntrinsicID(StringRef Name) {
689-
auto [NameTable, Target] = findTargetSubtable(Name);
690-
int Idx = Intrinsic::lookupLLVMIntrinsicByName(NameTable, Name, Target);
707+
auto [NameOffsetTable, Target] = findTargetSubtable(Name);
708+
int Idx = lookupLLVMIntrinsicByName(NameOffsetTable, Name, Target);
691709
if (Idx == -1)
692710
return Intrinsic::not_intrinsic;
693711

694712
// Intrinsic IDs correspond to the location in IntrinsicNameTable, but we have
695713
// an index into a sub-table.
696-
int Adjust = NameTable.data() - IntrinsicNameTable;
714+
int Adjust = NameOffsetTable.data() - IntrinsicNameOffsetTable;
697715
Intrinsic::ID ID = static_cast<Intrinsic::ID>(Idx + Adjust);
698716

699717
// If the intrinsic is not overloaded, require an exact match. If it is
700718
// overloaded, require either exact or prefix match.
701-
const auto MatchSize = strlen(NameTable[Idx]);
719+
const auto MatchSize = strlen(&IntrinsicNameTable[NameOffsetTable[Idx]]);
702720
assert(Name.size() >= MatchSize && "Expected either exact or prefix match");
703721
bool IsExactMatch = Name.size() == MatchSize;
704722
return IsExactMatch || Intrinsic::isOverloaded(ID) ? ID

llvm/unittests/IR/IntrinsicsTest.cpp

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,21 @@ class IntrinsicsTest : public ::testing::Test {
6363
};
6464

6565
TEST(IntrinsicNameLookup, Basic) {
66-
static constexpr const char *const NameTable[] = {
67-
"llvm.foo", "llvm.foo.a", "llvm.foo.b", "llvm.foo.b.a", "llvm.foo.c",
68-
};
66+
using namespace Intrinsic;
67+
EXPECT_EQ(Intrinsic::memcpy, lookupIntrinsicID("llvm.memcpy"));
6968

70-
static constexpr std::pair<const char *, int> Tests[] = {
71-
{"llvm.foo", 0}, {"llvm.foo.f64", 0}, {"llvm.foo.b", 2},
72-
{"llvm.foo.b.a", 3}, {"llvm.foo.c", 4}, {"llvm.foo.c.f64", 4},
73-
{"llvm.bar", -1},
74-
};
69+
// Partial, either between dots or not the last dot are not intrinsics.
70+
EXPECT_EQ(not_intrinsic, lookupIntrinsicID("llvm.mem"));
71+
EXPECT_EQ(not_intrinsic, lookupIntrinsicID("llvm.gc"));
7572

76-
for (const auto &[Name, ExpectedIdx] : Tests) {
77-
int Idx = Intrinsic::lookupLLVMIntrinsicByName(NameTable, Name);
78-
EXPECT_EQ(ExpectedIdx, Idx);
79-
if (!StringRef(Name).starts_with("llvm.foo"))
80-
continue;
81-
Idx = Intrinsic::lookupLLVMIntrinsicByName(NameTable, Name, "foo");
82-
EXPECT_EQ(ExpectedIdx, Idx);
83-
}
73+
// Look through intrinsic names with internal dots.
74+
EXPECT_EQ(memcpy_inline, lookupIntrinsicID("llvm.memcpy.inline"));
75+
76+
// Check that overloaded names are mapped to the underlying ID.
77+
EXPECT_EQ(memcpy_inline, lookupIntrinsicID("llvm.memcpy.inline.p0.p0.i8"));
78+
EXPECT_EQ(memcpy_inline, lookupIntrinsicID("llvm.memcpy.inline.p0.p0.i32"));
79+
EXPECT_EQ(memcpy_inline, lookupIntrinsicID("llvm.memcpy.inline.p0.p0.i64"));
80+
EXPECT_EQ(memcpy_inline, lookupIntrinsicID("llvm.memcpy.inline.p0.p0.i1024"));
8481
}
8582

8683
// Tests to verify getIntrinsicForClangBuiltin.

llvm/utils/TableGen/IntrinsicEmitter.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,37 @@ static constexpr IntrinsicTargetInfo TargetInfos[] = {
239239

240240
void IntrinsicEmitter::EmitIntrinsicToNameTable(
241241
const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
242+
// Built up a table of the intrinsic names.
243+
constexpr StringLiteral NotIntrinsic = "not_intrinsic";
244+
StringToOffsetTable Table;
245+
Table.GetOrAddStringOffset(NotIntrinsic);
246+
for (const auto &Int : Ints)
247+
Table.GetOrAddStringOffset(Int.Name);
248+
242249
OS << R"(// Intrinsic ID to name table.
243250
#ifdef GET_INTRINSIC_NAME_TABLE
244251
// Note that entry #0 is the invalid intrinsic!
252+
253+
)";
254+
255+
Table.EmitStringLiteralDef(OS, "static constexpr char IntrinsicNameTable[]",
256+
/*Indent=*/"");
257+
258+
OS << R"(
259+
static constexpr unsigned IntrinsicNameOffsetTable[] = {
245260
)";
261+
262+
OS << formatv(" {}, // {}\n", Table.GetStringOffset(NotIntrinsic),
263+
NotIntrinsic);
246264
for (const auto &Int : Ints)
247-
OS << " \"" << Int.Name << "\",\n";
248-
OS << "#endif\n\n";
265+
OS << formatv(" {}, // {}\n", Table.GetStringOffset(Int.Name), Int.Name);
266+
267+
OS << R"(
268+
}; // IntrinsicNameOffsetTable
269+
270+
#endif
271+
272+
)";
249273
}
250274

251275
void IntrinsicEmitter::EmitIntrinsicToOverloadTable(

0 commit comments

Comments
 (0)