Skip to content

Commit 0de0354

Browse files
authored
[LLVM][TableGen] Decrease code size of Intrinsic::getAttributes (#110573)
Decrease code size of `Intrinsic::getAttributes` function by uniquing the function and argument attributes separately and using the `IntrinsicsToAttributesMap` to store argument attribute ID in low 8 bits and function attribute ID in upper 8 bits. This reduces the number of cases to handle in the generated switch from 368 to 131, which is ~2.8x reduction in the number of switch cases. Also eliminate the fixed size array `AS` and `NumAttrs` variable, and instead call `AttributeList::get` directly from each case, with an inline array of the <index, AttribueSet> pairs.
1 parent 0eb2602 commit 0de0354

File tree

2 files changed

+91
-80
lines changed

2 files changed

+91
-80
lines changed

llvm/test/TableGen/intrinsic-attrs.td

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
include "llvm/IR/Intrinsics.td"
44

5-
// ... this intrinsic.
65
def int_random_gen : Intrinsic<[llvm_i32_ty], [], [IntrNoMem, IntrHasSideEffects]>;
76

87
def int_deref_ptr_ret : Intrinsic<[llvm_ptr_ty], [], [Dereferenceable<RetIndex, 16>]>;
@@ -24,14 +23,16 @@ def int_deref_ptr_ret : Intrinsic<[llvm_ptr_ty], [], [Dereferenceable<RetIndex,
2423
// CHECK-NEXT: });
2524

2625

27-
// CHECK: 1, // llvm.deref.ptr.ret
28-
// CHECK: 2, // llvm.random.gen
26+
// CHECK: getAttributes(LLVMContext &C, ID id)
27+
// CHECK: 0 << 8 | 0, // llvm.deref.ptr.ret
28+
// CHECK: 1 << 8 | 1, // llvm.random.gen
2929

3030
// CHECK: case 1:
31-
// CHECK-NEXT: AS[0] = {0, getIntrinsicArgAttributeSet(C, 0)};
32-
// CHECK-NEXT: AS[1] = {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, 0)};
33-
// CHECK-NEXT: NumAttrs = 2;
34-
35-
// CHECK: case 2:
36-
// CHECK-NEXT: AS[0] = {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, 1)};
37-
// CHECK-NEXT: NumAttrs = 1;
31+
// CHECK-NEXT: return AttributeList::get(C, {
32+
// CHECK-NEXT: {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, FnAttrID)}
33+
// CHECK-NEXT: });
34+
// CHECK-NEXT: case 0:
35+
// CHECK-NEXT: return AttributeList::get(C, {
36+
// CHECK-NEXT: {0, getIntrinsicArgAttributeSet(C, 0)},
37+
// CHECK-NEXT: {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, FnAttrID)}
38+
// CHECK-NEXT: });

llvm/utils/TableGen/IntrinsicEmitter.cpp

Lines changed: 80 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <array>
3030
#include <cassert>
3131
#include <cctype>
32+
#include <limits>
3233
#include <map>
3334
#include <optional>
3435
#include <string>
@@ -379,8 +380,17 @@ static constexpr {} IIT_Table[] = {{
379380
OS << "#endif\n\n"; // End of GET_INTRINSIC_GENERATOR_GLOBAL
380381
}
381382

383+
/// Returns the effective MemoryEffects for intrinsic \p Int.
384+
static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) {
385+
MemoryEffects ME = Int.ME;
386+
// TODO: IntrHasSideEffects should affect not only readnone intrinsics.
387+
if (ME.doesNotAccessMemory() && Int.hasSideEffects)
388+
ME = MemoryEffects::unknown();
389+
return ME;
390+
}
391+
382392
static bool compareFnAttributes(const CodeGenIntrinsic *L,
383-
const CodeGenIntrinsic *R, bool Default) {
393+
const CodeGenIntrinsic *R) {
384394
auto TieBoolAttributes = [](const CodeGenIntrinsic *I) -> auto {
385395
// Sort throwing intrinsics after non-throwing intrinsics.
386396
return std::tie(I->canThrow, I->isNoDuplicate, I->isNoMerge, I->isNoReturn,
@@ -396,50 +406,46 @@ static bool compareFnAttributes(const CodeGenIntrinsic *L,
396406
return TieL < TieR;
397407

398408
// Try to order by readonly/readnone attribute.
399-
uint32_t LME = L->ME.toIntValue();
400-
uint32_t RME = R->ME.toIntValue();
409+
uint32_t LME = getEffectiveME(*L).toIntValue();
410+
uint32_t RME = getEffectiveME(*R).toIntValue();
401411
if (LME != RME)
402412
return LME > RME;
403413

404-
return Default;
414+
return false;
415+
}
416+
417+
/// Returns true if \p Int has a non-empty set of function attributes. Note that
418+
/// NoUnwind = !canThrow, so we need to negate it's sense to test if the
419+
// intrinsic has NoUnwind attribute.
420+
static bool hasFnAttributes(const CodeGenIntrinsic &Int) {
421+
return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync ||
422+
Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate ||
423+
Int.isNoMerge || Int.isConvergent || Int.isSpeculatable ||
424+
Int.isStrictFP || getEffectiveME(Int) != MemoryEffects::unknown();
405425
}
406426

407427
namespace {
408428
struct FnAttributeComparator {
409429
bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
410-
return compareFnAttributes(L, R, false);
430+
return compareFnAttributes(L, R);
411431
}
412432
};
413433

414434
struct AttributeComparator {
415435
bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
416-
// Order by argument attributes if function attributes are equal.
436+
// Order all intrinsics with no functiona attributes before all intrinsics
437+
// with function attributes.
438+
bool HasFnAttrLHS = hasFnAttributes(*L);
439+
bool HasFnAttrRHS = hasFnAttributes(*R);
440+
441+
// Order by argument attributes if function `hasFnAttributes` is equal.
417442
// This is reliable because each side is already sorted internally.
418-
return compareFnAttributes(L, R,
419-
L->ArgumentAttributes < R->ArgumentAttributes);
443+
return std::tie(HasFnAttrLHS, L->ArgumentAttributes) <
444+
std::tie(HasFnAttrRHS, R->ArgumentAttributes);
420445
}
421446
};
422447
} // End anonymous namespace
423448

424-
/// Returns the effective MemoryEffects for intrinsic \p Int.
425-
static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) {
426-
MemoryEffects ME = Int.ME;
427-
// TODO: IntrHasSideEffects should affect not only readnone intrinsics.
428-
if (ME.doesNotAccessMemory() && Int.hasSideEffects)
429-
ME = MemoryEffects::unknown();
430-
return ME;
431-
}
432-
433-
/// Returns true if \p Int has a non-empty set of function attributes. Note that
434-
/// NoUnwind = !canThrow, so we need to negate it's sense to test if the
435-
// intrinsic has NoUnwind attribute.
436-
static bool hasFnAttributes(const CodeGenIntrinsic &Int) {
437-
return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync ||
438-
Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate ||
439-
Int.isNoMerge || Int.isConvergent || Int.isSpeculatable ||
440-
Int.isStrictFP || getEffectiveME(Int) != MemoryEffects::unknown();
441-
}
442-
443449
/// Returns the name of the IR enum for argument attribute kind \p Kind.
444450
static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {
445451
switch (Kind) {
@@ -576,75 +582,79 @@ static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) {
576582
AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id) {
577583
)";
578584

579-
// Compute the maximum number of attribute arguments and the map.
580-
typedef std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator>
581-
UniqAttrMapTy;
582-
UniqAttrMapTy UniqAttributes;
583-
unsigned MaxArgAttrs = 0;
584-
unsigned AttrNum = 0;
585+
// Compute the maximum number of attribute arguments and the map. For function
586+
// attributes, we only consider whether the intrinsics has any function
587+
// arguments or not.
588+
std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator>
589+
UniqAttributes;
585590
for (const CodeGenIntrinsic &Int : Ints) {
586-
MaxArgAttrs =
587-
std::max(MaxArgAttrs, unsigned(Int.ArgumentAttributes.size()));
588-
unsigned &N = UniqAttributes[&Int];
589-
if (N)
590-
continue;
591-
N = ++AttrNum;
592-
assert(N < 65536 && "Too many unique attributes for table!");
591+
unsigned ID = UniqAttributes.size();
592+
UniqAttributes.try_emplace(&Int, ID);
593593
}
594594

595+
// Assign a 16-bit packed ID for each intrinsic. The lower 8-bits will be its
596+
// "argument attribute ID" (index in UniqAttributes) and upper 8 bits will be
597+
// its "function attribute ID" (index in UniqFnAttributes).
598+
if (UniqAttributes.size() > 256)
599+
PrintFatalError("Too many unique argument attributes for table!");
600+
if (UniqFnAttributes.size() > 256)
601+
PrintFatalError("Too many unique function attributes for table!");
602+
595603
// Emit an array of AttributeList. Most intrinsics will have at least one
596604
// entry, for the function itself (index ~1), which is usually nounwind.
597605
OS << " static constexpr uint16_t IntrinsicsToAttributesMap[] = {";
598-
for (const CodeGenIntrinsic &Int : Ints)
599-
OS << formatv("\n {}, // {}", UniqAttributes[&Int], Int.Name);
606+
for (const CodeGenIntrinsic &Int : Ints) {
607+
uint16_t FnAttrIndex = hasFnAttributes(Int) ? UniqFnAttributes[&Int] : 0;
608+
OS << formatv("\n {} << 8 | {}, // {}", FnAttrIndex,
609+
UniqAttributes[&Int], Int.Name);
610+
}
600611

601612
OS << formatv(R"(
602613
};
603-
std::pair<unsigned, AttributeSet> AS[{}];
604-
unsigned NumAttrs = 0;
605-
if (id != 0) {{
606-
switch(IntrinsicsToAttributesMap[id - 1]) {{
607-
default: llvm_unreachable("Invalid attribute number");
608-
)",
609-
MaxArgAttrs + 1);
614+
if (id == 0)
615+
return AttributeList();
616+
617+
uint16_t PackedID = IntrinsicsToAttributesMap[id - 1];
618+
uint8_t FnAttrID = PackedID >> 8;
619+
switch(PackedID & 0xFF) {{
620+
default: llvm_unreachable("Invalid attribute number");
621+
)");
610622

611623
for (const auto [IntPtr, UniqueID] : UniqAttributes) {
612-
OS << formatv(" case {}:\n", UniqueID);
624+
OS << formatv(" case {}:\n", UniqueID);
613625
const CodeGenIntrinsic &Int = *IntPtr;
614626

615627
// Keep track of the number of attributes we're writing out.
616-
unsigned NumAttrs = 0;
628+
unsigned NumAttrs =
629+
llvm::count_if(Int.ArgumentAttributes,
630+
[](const auto &Attrs) { return !Attrs.empty(); });
631+
NumAttrs += hasFnAttributes(Int);
632+
if (NumAttrs == 0) {
633+
OS << " return AttributeList();\n";
634+
continue;
635+
}
617636

637+
OS << " return AttributeList::get(C, {\n";
638+
ListSeparator LS(",\n");
618639
for (const auto &[AttrIdx, Attrs] : enumerate(Int.ArgumentAttributes)) {
619640
if (Attrs.empty())
620641
continue;
621642

622643
unsigned ArgAttrID = UniqArgAttributes.find(Attrs)->second;
623-
OS << formatv(
624-
" AS[{}] = {{{}, getIntrinsicArgAttributeSet(C, {})};\n",
625-
NumAttrs++, AttrIdx, ArgAttrID);
644+
OS << LS
645+
<< formatv(" {{{}, getIntrinsicArgAttributeSet(C, {})}", AttrIdx,
646+
ArgAttrID);
626647
}
627648

628649
if (hasFnAttributes(Int)) {
629-
unsigned FnAttrID = UniqFnAttributes.find(&Int)->second;
630-
OS << formatv(" AS[{}] = {{AttributeList::FunctionIndex, "
631-
"getIntrinsicFnAttributeSet(C, {})};\n",
632-
NumAttrs++, FnAttrID);
633-
}
634-
635-
if (NumAttrs) {
636-
OS << formatv(R"( NumAttrs = {};
637-
break;
638-
)",
639-
NumAttrs);
640-
} else {
641-
OS << " return AttributeList();\n";
650+
OS << LS
651+
<< " {AttributeList::FunctionIndex, "
652+
"getIntrinsicFnAttributeSet(C, FnAttrID)}";
642653
}
654+
OS << "\n });\n";
643655
}
644656

645-
OS << R"( }
646-
}
647-
return AttributeList::get(C, ArrayRef(AS, NumAttrs));
657+
OS << R"( }
648658
}
649659
#endif // GET_INTRINSIC_ATTRIBUTES
650660

0 commit comments

Comments
 (0)