Skip to content

Commit ed299b3

Browse files
authored
[GlobalISel] Optimize ULEB128 usage (#90565)
- Remove some cases where ULEB128 isn't needed - Add a fastDecodeULEB128 tailored for GlobalISel which does unchecked decoding optimized for the common case, which is 1 byte values. We rarely have >1 byte Inst IDs, OpIdx, etc. and those are the most common ULEB users by far. This specific LEB128 decode function generates almost 2x less instructions than the generic one.
1 parent 8480c93 commit ed299b3

File tree

6 files changed

+94
-13
lines changed

6 files changed

+94
-13
lines changed

llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ enum {
168168
/// operand.
169169
/// - InsnID(ULEB128) - Instruction ID
170170
/// - MMOIdx(ULEB128) - MMO index
171-
/// - NumAddrSpace(ULEB128) - Number of valid address spaces
171+
/// - NumAddrSpace(1) - Number of valid address spaces
172172
/// - AddrSpaceN(ULEB128) - An allowed space of the memory access
173173
/// - AddrSpaceN+1 ...
174174
GIM_CheckMemoryAddressSpace,
@@ -177,7 +177,7 @@ enum {
177177
/// memory operand.
178178
/// - InsnID(ULEB128) - Instruction ID
179179
/// - MMOIdx(ULEB128) - MMO index
180-
/// - MinAlign(ULEB128) - Minimum acceptable alignment
180+
/// - MinAlign(1) - Minimum acceptable alignment
181181
GIM_CheckMemoryAlignment,
182182

183183
/// Check the size of the memory access for the given machine memory operand
@@ -713,6 +713,28 @@ class GIMatchTableExecutor {
713713
memcpy(&Ret, MatchTable, sizeof(Ret));
714714
return Ret;
715715
}
716+
717+
public:
718+
// Faster ULEB128 decoder tailored for the Match Table Executor.
719+
//
720+
// - Arguments are fixed to avoid mid-function checks.
721+
// - Unchecked execution, assumes no error.
722+
// - Fast common case handling (1 byte values).
723+
LLVM_ATTRIBUTE_ALWAYS_INLINE static uint64_t
724+
fastDecodeULEB128(const uint8_t *LLVM_ATTRIBUTE_RESTRICT MatchTable,
725+
uint64_t &CurrentIdx) {
726+
uint64_t Value = MatchTable[CurrentIdx++];
727+
if (LLVM_UNLIKELY(Value >= 128)) {
728+
Value &= 0x7f;
729+
unsigned Shift = 7;
730+
do {
731+
uint64_t Slice = MatchTable[CurrentIdx] & 0x7f;
732+
Value += Slice << Shift;
733+
Shift += 7;
734+
} while (MatchTable[CurrentIdx++] >= 128);
735+
}
736+
return Value;
737+
}
716738
};
717739

718740
} // end namespace llvm

llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,7 @@ bool GIMatchTableExecutor::executeMatchTable(
100100
};
101101

102102
const auto readULEB = [&]() {
103-
unsigned N = 0;
104-
uint64_t Val = decodeULEB128(MatchTable + CurrentIdx, &N);
105-
CurrentIdx += N;
106-
return Val;
103+
return fastDecodeULEB128(MatchTable, CurrentIdx);
107104
};
108105

109106
// Convenience function to return a signed value. This avoids
@@ -476,7 +473,7 @@ bool GIMatchTableExecutor::executeMatchTable(
476473
}
477474
case GIM_CheckAtomicOrdering: {
478475
uint64_t InsnID = readULEB();
479-
auto Ordering = (AtomicOrdering)readULEB();
476+
auto Ordering = (AtomicOrdering)MatchTable[CurrentIdx++];
480477
DEBUG_WITH_TYPE(TgtExecutor::getName(),
481478
dbgs() << CurrentIdx << ": GIM_CheckAtomicOrdering(MIs["
482479
<< InsnID << "], " << (uint64_t)Ordering << ")\n");
@@ -493,7 +490,7 @@ bool GIMatchTableExecutor::executeMatchTable(
493490
}
494491
case GIM_CheckAtomicOrderingOrStrongerThan: {
495492
uint64_t InsnID = readULEB();
496-
auto Ordering = (AtomicOrdering)readULEB();
493+
auto Ordering = (AtomicOrdering)MatchTable[CurrentIdx++];
497494
DEBUG_WITH_TYPE(TgtExecutor::getName(),
498495
dbgs() << CurrentIdx
499496
<< ": GIM_CheckAtomicOrderingOrStrongerThan(MIs["
@@ -511,7 +508,7 @@ bool GIMatchTableExecutor::executeMatchTable(
511508
}
512509
case GIM_CheckAtomicOrderingWeakerThan: {
513510
uint64_t InsnID = readULEB();
514-
auto Ordering = (AtomicOrdering)readULEB();
511+
auto Ordering = (AtomicOrdering)MatchTable[CurrentIdx++];
515512
DEBUG_WITH_TYPE(TgtExecutor::getName(),
516513
dbgs() << CurrentIdx
517514
<< ": GIM_CheckAtomicOrderingWeakerThan(MIs["
@@ -531,7 +528,7 @@ bool GIMatchTableExecutor::executeMatchTable(
531528
uint64_t InsnID = readULEB();
532529
uint64_t MMOIdx = readULEB();
533530
// This accepts a list of possible address spaces.
534-
const uint64_t NumAddrSpace = readULEB();
531+
const uint64_t NumAddrSpace = MatchTable[CurrentIdx++];
535532

536533
if (State.MIs[InsnID]->getNumMemOperands() <= MMOIdx) {
537534
if (handleReject() == RejectAndGiveUp)
@@ -568,7 +565,7 @@ bool GIMatchTableExecutor::executeMatchTable(
568565
case GIM_CheckMemoryAlignment: {
569566
uint64_t InsnID = readULEB();
570567
uint64_t MMOIdx = readULEB();
571-
uint64_t MinAlign = readULEB();
568+
uint64_t MinAlign = MatchTable[CurrentIdx++];
572569

573570
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
574571

llvm/include/llvm/Support/Compiler.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,14 @@
278278
#define LLVM_ATTRIBUTE_RETURNS_NONNULL
279279
#endif
280280

281+
/// LLVM_ATTRIBUTE_RESTRICT - Annotates a pointer to tell the compiler that
282+
/// it is not aliased in the current scope.
283+
#if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
284+
#define LLVM_ATTRIBUTE_RESTRICT __restrict
285+
#else
286+
#define LLVM_ATTRIBUTE_RESTRICT
287+
#endif
288+
281289
/// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a
282290
/// pointer that does not alias any other valid pointer.
283291
#ifdef __GNUC__

llvm/unittests/CodeGen/GlobalISel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ set(LLVM_LINK_COMPONENTS
1515
add_llvm_unittest(GlobalISelTests
1616
ConstantFoldingTest.cpp
1717
CSETest.cpp
18+
GIMatchTableExecutorTest.cpp
1819
LegalizerTest.cpp
1920
LegalizerHelperTest.cpp
2021
LegalizerInfoTest.cpp
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===- GIMatchTableExecutorTest.cpp ---------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace llvm;
13+
14+
TEST(GlobalISelLEB128Test, fastDecodeULEB128) {
15+
#define EXPECT_DECODE_ULEB128_EQ(EXPECTED, VALUE) \
16+
do { \
17+
uint64_t ActualSize = 0; \
18+
uint64_t Actual = GIMatchTableExecutor::fastDecodeULEB128( \
19+
reinterpret_cast<const uint8_t *>(VALUE), ActualSize); \
20+
EXPECT_EQ(sizeof(VALUE) - 1, ActualSize); \
21+
EXPECT_EQ(EXPECTED, Actual); \
22+
} while (0)
23+
24+
EXPECT_DECODE_ULEB128_EQ(0u, "\x00");
25+
EXPECT_DECODE_ULEB128_EQ(1u, "\x01");
26+
EXPECT_DECODE_ULEB128_EQ(63u, "\x3f");
27+
EXPECT_DECODE_ULEB128_EQ(64u, "\x40");
28+
EXPECT_DECODE_ULEB128_EQ(0x7fu, "\x7f");
29+
EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x01");
30+
EXPECT_DECODE_ULEB128_EQ(0x81u, "\x81\x01");
31+
EXPECT_DECODE_ULEB128_EQ(0x90u, "\x90\x01");
32+
EXPECT_DECODE_ULEB128_EQ(0xffu, "\xff\x01");
33+
EXPECT_DECODE_ULEB128_EQ(0x100u, "\x80\x02");
34+
EXPECT_DECODE_ULEB128_EQ(0x101u, "\x81\x02");
35+
EXPECT_DECODE_ULEB128_EQ(4294975616ULL, "\x80\xc1\x80\x80\x10");
36+
37+
// Decode ULEB128 with extra padding bytes
38+
EXPECT_DECODE_ULEB128_EQ(0u, "\x80\x00");
39+
EXPECT_DECODE_ULEB128_EQ(0u, "\x80\x80\x00");
40+
EXPECT_DECODE_ULEB128_EQ(0x7fu, "\xff\x00");
41+
EXPECT_DECODE_ULEB128_EQ(0x7fu, "\xff\x80\x00");
42+
EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x81\x00");
43+
EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x81\x80\x00");
44+
EXPECT_DECODE_ULEB128_EQ(0x80u, "\x80\x81\x80\x80\x80\x80\x80\x80\x80\x00");
45+
EXPECT_DECODE_ULEB128_EQ(0x80000000'00000000ul,
46+
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01");
47+
48+
#undef EXPECT_DECODE_ULEB128_EQ
49+
}

llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,13 +1565,14 @@ bool MemoryAddressSpacePredicateMatcher::isIdentical(
15651565

15661566
void MemoryAddressSpacePredicateMatcher::emitPredicateOpcodes(
15671567
MatchTable &Table, RuleMatcher &Rule) const {
1568+
assert(AddrSpaces.size() < 256);
15681569
Table << MatchTable::Opcode("GIM_CheckMemoryAddressSpace")
15691570
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
15701571
<< MatchTable::Comment("MMO")
15711572
<< MatchTable::ULEB128Value(MMOIdx)
15721573
// Encode number of address spaces to expect.
15731574
<< MatchTable::Comment("NumAddrSpace")
1574-
<< MatchTable::ULEB128Value(AddrSpaces.size());
1575+
<< MatchTable::IntValue(1, AddrSpaces.size());
15751576
for (unsigned AS : AddrSpaces)
15761577
Table << MatchTable::Comment("AddrSpace") << MatchTable::ULEB128Value(AS);
15771578

@@ -1590,10 +1591,13 @@ bool MemoryAlignmentPredicateMatcher::isIdentical(
15901591

15911592
void MemoryAlignmentPredicateMatcher::emitPredicateOpcodes(
15921593
MatchTable &Table, RuleMatcher &Rule) const {
1594+
// TODO: we could support more, just need to emit the right opcode or switch
1595+
// to log alignment.
1596+
assert(MinAlign < 256);
15931597
Table << MatchTable::Opcode("GIM_CheckMemoryAlignment")
15941598
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
15951599
<< MatchTable::Comment("MMO") << MatchTable::ULEB128Value(MMOIdx)
1596-
<< MatchTable::Comment("MinAlign") << MatchTable::ULEB128Value(MinAlign)
1600+
<< MatchTable::Comment("MinAlign") << MatchTable::IntValue(1, MinAlign)
15971601
<< MatchTable::LineBreak;
15981602
}
15991603

0 commit comments

Comments
 (0)