Skip to content

Commit 89b08c8

Browse files
authored
[TableGen] Simplify generated code for isSubclass (#117351)
Implement isSubclass with direct lookup into some tables instead of nested switches. Part of the motivation for this is improving compile time when clang-18 is used as a host compiler, since it seems to have trouble with very large switch statements.
1 parent 76e6c8d commit 89b08c8

File tree

1 file changed

+48
-43
lines changed

1 file changed

+48
-43
lines changed

llvm/utils/TableGen/AsmMatcherEmitter.cpp

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,53 +2547,58 @@ static void emitIsSubclass(CodeGenTarget &Target,
25472547
OS << " if (A == B)\n";
25482548
OS << " return true;\n\n";
25492549

2550-
bool EmittedSwitch = false;
2550+
// TODO: Use something like SequenceToOffsetTable to allow sequences to
2551+
// overlap in this table.
2552+
SmallVector<bool> SuperClassData;
2553+
2554+
OS << " [[maybe_unused]] static constexpr struct {\n";
2555+
OS << " uint32_t Offset;\n";
2556+
OS << " uint16_t Start;\n";
2557+
OS << " uint16_t Length;\n";
2558+
OS << " } Table[] = {\n";
2559+
OS << " {0, 0, 0},\n"; // InvalidMatchClass
2560+
OS << " {0, 0, 0},\n"; // OptionalMatchClass
25512561
for (const auto &A : Infos) {
2552-
std::vector<StringRef> SuperClasses;
2553-
if (A.IsOptional)
2554-
SuperClasses.push_back("OptionalMatchClass");
2555-
for (const auto &B : Infos) {
2556-
if (&A != &B && A.isSubsetOf(B))
2557-
SuperClasses.push_back(B.Name);
2558-
}
2559-
2560-
if (SuperClasses.empty())
2561-
continue;
2562-
2563-
// If this is the first SuperClass, emit the switch header.
2564-
if (!EmittedSwitch) {
2565-
OS << " switch (A) {\n";
2566-
OS << " default:\n";
2567-
OS << " return false;\n";
2568-
EmittedSwitch = true;
2569-
}
2570-
2571-
OS << "\n case " << A.Name << ":\n";
2572-
2573-
if (SuperClasses.size() == 1) {
2574-
OS << " return B == " << SuperClasses.back() << ";\n";
2575-
continue;
2576-
}
2577-
2578-
if (!SuperClasses.empty()) {
2579-
OS << " switch (B) {\n";
2580-
OS << " default: return false;\n";
2581-
for (StringRef SC : SuperClasses)
2582-
OS << " case " << SC << ": return true;\n";
2583-
OS << " }\n";
2584-
} else {
2585-
// No case statement to emit
2586-
OS << " return false;\n";
2587-
}
2562+
SmallVector<bool> SuperClasses;
2563+
SuperClasses.push_back(false); // InvalidMatchClass
2564+
SuperClasses.push_back(A.IsOptional); // OptionalMatchClass
2565+
for (const auto &B : Infos)
2566+
SuperClasses.push_back(&A != &B && A.isSubsetOf(B));
2567+
2568+
// Trim leading and trailing zeros.
2569+
auto End = find_if(reverse(SuperClasses), [](bool B) { return B; }).base();
2570+
auto Start =
2571+
std::find_if(SuperClasses.begin(), End, [](bool B) { return B; });
2572+
2573+
unsigned Offset = SuperClassData.size();
2574+
SuperClassData.append(Start, End);
2575+
2576+
OS << " {" << Offset << ", " << (Start - SuperClasses.begin()) << ", "
2577+
<< (End - Start) << "},\n";
25882578
}
2579+
OS << " };\n\n";
25892580

2590-
// If there were case statements emitted into the string stream write the
2591-
// default.
2592-
if (EmittedSwitch)
2593-
OS << " }\n";
2594-
else
2581+
if (SuperClassData.empty()) {
25952582
OS << " return false;\n";
2596-
2583+
} else {
2584+
// Dump the boolean data packed into bytes.
2585+
SuperClassData.append(-SuperClassData.size() % 8, false);
2586+
OS << " static constexpr uint8_t Data[] = {\n";
2587+
for (unsigned I = 0, E = SuperClassData.size(); I < E; I += 8) {
2588+
unsigned Byte = 0;
2589+
for (unsigned J = 0; J < 8; ++J)
2590+
Byte |= (unsigned)SuperClassData[I + J] << J;
2591+
OS << formatv(" {:X2},\n", Byte);
2592+
}
2593+
OS << " };\n\n";
2594+
2595+
OS << " auto &Entry = Table[A];\n";
2596+
OS << " unsigned Idx = B - Entry.Start;\n";
2597+
OS << " if (Idx >= Entry.Length)\n";
2598+
OS << " return false;\n";
2599+
OS << " Idx += Entry.Offset;\n";
2600+
OS << " return (Data[Idx / 8] >> (Idx % 8)) & 1;\n";
2601+
}
25972602
OS << "}\n\n";
25982603
}
25992604

0 commit comments

Comments
 (0)