[NFCI[TableGen] Minor improvements to Intrinsic::getAttributes (#152761)

This change implements several small improvements to
`Intrinsic::getAttributes`:

1. Use `SequenceToOffsetTable` to emit `ArgAttrIdTable`. This enables
reuse of entries when they share a common prefix. This reduces the size
of this table from 546 to 484 entries, which is 248 bytes.
2. Fix `AttributeComparator` to purely compare argument attributes and
not look at function attributes. This avoids unnecessary duplicates in
the uniqueing process and eliminates 2 entries from
`ArgAttributesInfoTable`, saving 8 bytes.
3. Improve `Intrinsic::getAttributes` code to not initialize all entries
of `AS` always. Currently, we initialize all entries of the array `AS`
even if we may not use all of them. In addition to the runtime cost, for
Clang release builds, since the initialization loop is unrolled, it
consumes ~330 bytes of code to initialize the `AS` array. Address this
by declaring the storage for AS using just a char array with appropriate
`alignas` (similar to how `SmallVectorStorage` defines its inline
elements).
This commit is contained in:
Rahul Joshi 2025-08-12 07:15:08 -07:00 committed by GitHub
parent 24f5385a85
commit 89ea9df6a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -455,15 +455,9 @@ struct FnAttributeComparator {
struct AttributeComparator { struct AttributeComparator {
bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
// Order all intrinsics with no functiona attributes before all intrinsics // This comparator is used to unique just the argument attributes of an
// with function attributes. // intrinsic without considering any function attributes.
bool HasFnAttrLHS = hasFnAttributes(*L); return L->ArgumentAttributes < R->ArgumentAttributes;
bool HasFnAttrRHS = hasFnAttributes(*R);
// Order by argument attributes if function `hasFnAttributes` is equal.
// This is reliable because each side is already sorted internally.
return std::tie(HasFnAttrLHS, L->ArgumentAttributes) <
std::tie(HasFnAttrRHS, R->ArgumentAttributes);
} }
}; };
} // End anonymous namespace } // End anonymous namespace
@ -659,7 +653,7 @@ static constexpr uint16_t IntrinsicsToAttributesMap[] = {)";
// //
// getIntrinsicArgAttributeSet(C, ArgAttrID, FT->getContainedType(ArgNo)); // getIntrinsicArgAttributeSet(C, ArgAttrID, FT->getContainedType(ArgNo));
// //
// Create a table that records, for each argument attributes, the set of // Create a table that records, for each argument attributes, the list of
// <ArgNo, ArgAttrID> pairs that are needed to construct its argument // <ArgNo, ArgAttrID> pairs that are needed to construct its argument
// attributes. These tables for all intrinsics will be concatenated into one // attributes. These tables for all intrinsics will be concatenated into one
// large table and then for each intrinsic, we remember the Staring index and // large table and then for each intrinsic, we remember the Staring index and
@ -667,79 +661,77 @@ static constexpr uint16_t IntrinsicsToAttributesMap[] = {)";
// non-empty attributes), so that we can build the attribute list for an // non-empty attributes), so that we can build the attribute list for an
// intrinsic without using a switch-case. // intrinsic without using a switch-case.
// Find the max number of attributes to create the local array and create using ArgNoAttrIDPair = std::pair<uint16_t, uint16_t>;
// a concatenated list of <ArgNo, AttrID> pairs.
struct ArgNoAttrIDPair {
uint16_t ArgNo, ArgAttrID;
ArgNoAttrIDPair(uint16_t ArgNo, uint16_t ArgAttrID)
: ArgNo(ArgNo), ArgAttrID(ArgAttrID) {}
};
// For each unique ID in UniqAttributes, reacord the starting index in the // Emit the table of concatenated <ArgNo, AttrId> using SequenceToOffsetTable
// flattened ArgNoAttrIDPair table, and the number of non-empty arg // so that entries can be reused if possible. Individual sequences in this
// attributes. // table do not have any terminator.
struct ArgAttributesInfo { using ArgAttrIDSubTable = SmallVector<ArgNoAttrIDPair>;
uint16_t StartIndex; SequenceToOffsetTable<ArgAttrIDSubTable> ArgAttrIdSequenceTable(std::nullopt);
uint16_t NumAttrs; SmallVector<ArgAttrIDSubTable> ArgAttrIdSubTables(
ArgAttributesInfo(uint16_t StartIndex, uint16_t NumAttrs) UniqAttributes.size()); // Indexed by UniqueID.
: StartIndex(StartIndex), NumAttrs(NumAttrs) {}
};
SmallVector<ArgNoAttrIDPair> ArgAttrIdTable;
SmallVector<ArgAttributesInfo> ArgAttributesInfoTable(UniqAttributes.size(),
{0, 0});
// Find the max number of attributes to create the local array.
unsigned MaxNumAttrs = 0; unsigned MaxNumAttrs = 0;
for (const auto [IntPtr, UniqueID] : UniqAttributes) { for (const auto [IntPtr, UniqueID] : UniqAttributes) {
const CodeGenIntrinsic &Int = *IntPtr; const CodeGenIntrinsic &Int = *IntPtr;
unsigned NumAttrs = 0; ArgAttrIDSubTable SubTable;
unsigned StartIndex = ArgAttrIdTable.size();
for (const auto &[ArgNo, Attrs] : enumerate(Int.ArgumentAttributes)) { for (const auto &[ArgNo, Attrs] : enumerate(Int.ArgumentAttributes)) {
if (Attrs.empty()) if (Attrs.empty())
continue; continue;
uint16_t ArgAttrID = UniqArgAttributes.find(Attrs)->second; uint16_t ArgAttrID = UniqArgAttributes.find(Attrs)->second;
ArgAttrIdTable.emplace_back((uint16_t)ArgNo, ArgAttrID); SubTable.emplace_back((uint16_t)ArgNo, ArgAttrID);
++NumAttrs;
} }
ArgAttrIdSubTables[UniqueID] = SubTable;
// Record the start index and size of the list for this unique ID. if (!SubTable.empty())
if (NumAttrs) ArgAttrIdSequenceTable.add(SubTable);
ArgAttributesInfoTable[UniqueID] = unsigned NumAttrs = SubTable.size() + hasFnAttributes(Int);
ArgAttributesInfo(StartIndex, NumAttrs);
NumAttrs += hasFnAttributes(Int);
MaxNumAttrs = std::max(MaxNumAttrs, NumAttrs); MaxNumAttrs = std::max(MaxNumAttrs, NumAttrs);
} }
if (ArgAttrIdTable.size() >= std::numeric_limits<uint16_t>::max()) ArgAttrIdSequenceTable.layout();
if (ArgAttrIdSequenceTable.size() >= std::numeric_limits<uint16_t>::max())
PrintFatalError("Size of ArgAttrIdTable exceeds supported limit"); PrintFatalError("Size of ArgAttrIdTable exceeds supported limit");
// Emit the 2 tables (flattened ArgNo, ArgAttrID) and ArgAttrIdTableIndex // Emit the 2 tables (flattened ArgNo, ArgAttrID) and ArgAttributesInfoTable.
OS << R"( OS << formatv(R"(
namespace { namespace {{
struct ArgNoAttrIDPair { struct ArgNoAttrIDPair {{
uint16_t ArgNo, ArgAttrID; uint16_t ArgNo, ArgAttrID;
}; };
} // namespace } // namespace
static constexpr ArgNoAttrIDPair ArgAttrIdTable[] = { // Number of entries: {}
)"; static constexpr ArgNoAttrIDPair ArgAttrIdTable[] = {{
for (const auto &[ArgNo, ArgAttrID] : ArgAttrIdTable) )",
OS << formatv(" {{{}, {}},\n", ArgNo, ArgAttrID); ArgAttrIdSequenceTable.size());
OS << R"(}; // ArgAttrIdTable
namespace { ArgAttrIdSequenceTable.emit(OS, [](raw_ostream &OS, ArgNoAttrIDPair Elem) {
struct ArgAttributesInfo { OS << formatv("{{{}, {}}", Elem.first, Elem.second);
});
OS << formatv(R"(}; // ArgAttrIdTable
namespace {{
struct ArgAttributesInfo {{
uint16_t StartIndex; uint16_t StartIndex;
uint16_t NumAttrs; uint16_t NumAttrs;
}; };
} // namespace } // namespace
static constexpr ArgAttributesInfo ArgAttributesInfoTable[] = { // Number of entries: {}
)"; static constexpr ArgAttributesInfo ArgAttributesInfoTable[] = {{
for (const auto &[StartIndex, NumAttrs] : ArgAttributesInfoTable) )",
ArgAttrIdSubTables.size());
for (const auto &SubTable : ArgAttrIdSubTables) {
unsigned NumAttrs = SubTable.size();
unsigned StartIndex = NumAttrs ? ArgAttrIdSequenceTable.get(SubTable) : 0;
OS << formatv(" {{{}, {}},\n", StartIndex, NumAttrs); OS << formatv(" {{{}, {}},\n", StartIndex, NumAttrs);
}
OS << "}; // ArgAttributesInfoTable\n"; OS << "}; // ArgAttributesInfoTable\n";
// Now emit the Intrinsic::getAttributes function. This will first map // Now emit the Intrinsic::getAttributes function. This will first map
@ -756,7 +748,9 @@ AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id,
uint16_t PackedID = IntrinsicsToAttributesMap[id - 1]; uint16_t PackedID = IntrinsicsToAttributesMap[id - 1];
uint8_t FnAttrID = PackedID >> 8; uint8_t FnAttrID = PackedID >> 8;
uint8_t ArgAttrID = PackedID & 0xFF; uint8_t ArgAttrID = PackedID & 0xFF;
std::pair<unsigned, AttributeSet> AS[{}]; using PairTy = std::pair<unsigned, AttributeSet>;
alignas(PairTy) char ASStorage[sizeof(PairTy) * {}];
PairTy *AS = reinterpret_cast<PairTy *>(ASStorage);
// Construct an ArrayRef for easier range checking. // Construct an ArrayRef for easier range checking.
ArrayRef<ArgAttributesInfo> ArgAttributesInfoTableAR(ArgAttributesInfoTable); ArrayRef<ArgAttributesInfo> ArgAttributesInfoTableAR(ArgAttributesInfoTable);