Skip to content

Commit 5ccc29f

Browse files
committed
[TableGen] Optimize intrinsic info type signature encoding
Add intrinsic emitter option to change the "fixed encoding" table used for encoding intrinsic type signature to use 16-bit encoding as opposed to 32-bit. To better segragate LLVM Core from this encoding detail, add a function `decodeIITFixedEncoding` to the emitted code to decode the fixed encoding. This allows TableGen intrinsic emitter to choose 16 or 32-bit fixed encoding withot changing the LLVM code. When using 16-bit encoding, we seem to reduce the total static storage size of this info by 50%. Currently measure data is as follows - Current size = 14193*4 + 16058 + 3 = 72833 bytes. - New size = 14193*2 + 19879 + 3 = 48268 bytes. - Reduction = 50.9%
1 parent 7b2fe84 commit 5ccc29f

File tree

4 files changed

+87
-38
lines changed

4 files changed

+87
-38
lines changed

llvm/benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ add_benchmark(DummyYAML DummyYAML.cpp PARTIAL_SOURCES_INTENDED)
66
add_benchmark(xxhash xxhash.cpp PARTIAL_SOURCES_INTENDED)
77
add_benchmark(GetIntrinsicForClangBuiltin GetIntrinsicForClangBuiltin.cpp PARTIAL_SOURCES_INTENDED)
88
add_benchmark(FormatVariadicBM FormatVariadicBM.cpp PARTIAL_SOURCES_INTENDED)
9+
add_benchmark(GetIntrinsicInfoTableEntriesBM GetIntrinsicInfoTableEntriesBM.cpp PARTIAL_SOURCES_INTENDED)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===- GetIntrinsicInfoTableEntries.cpp - IIT signature benchmark ---------===//
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 "benchmark/benchmark.h"
10+
#include "llvm/ADT/SmallVector.h"
11+
#include "llvm/IR/Function.h"
12+
#include "llvm/IR/Intrinsics.h"
13+
#include <variant>
14+
15+
using namespace llvm;
16+
using namespace Intrinsic;
17+
18+
static void BM_GetIntrinsicInfoTableEntries(benchmark::State &state) {
19+
SmallVector<IITDescriptor> Table;
20+
for (auto _ : state) {
21+
for (ID ID = 1; ID < num_intrinsics; ++ID) {
22+
// This makes sure the vector does not keep growing, as well as after the
23+
// first iteration does not result in additional allocations.
24+
Table.clear();
25+
getIntrinsicInfoTableEntries(ID, Table);
26+
}
27+
}
28+
}
29+
30+
BENCHMARK(BM_GetIntrinsicInfoTableEntries);
31+
32+
BENCHMARK_MAIN();

llvm/lib/IR/Function.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,22 +1381,24 @@ static void DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
13811381

13821382
void Intrinsic::getIntrinsicInfoTableEntries(ID id,
13831383
SmallVectorImpl<IITDescriptor> &T){
1384+
static_assert(sizeof(IIT_Table[0]) == 2,
1385+
"Expect 16-bit entries in IIT_Table");
13841386
// Check to see if the intrinsic's type was expressible by the table.
1385-
unsigned TableVal = IIT_Table[id-1];
1387+
uint16_t TableVal = IIT_Table[id - 1];
13861388

13871389
// Decode the TableVal into an array of IITValues.
1388-
SmallVector<unsigned char, 8> IITValues;
1390+
SmallVector<unsigned char> IITValues;
13891391
ArrayRef<unsigned char> IITEntries;
13901392
unsigned NextElt = 0;
1391-
if ((TableVal >> 31) != 0) {
1393+
if (TableVal >> 15) {
13921394
// This is an offset into the IIT_LongEncodingTable.
13931395
IITEntries = IIT_LongEncodingTable;
13941396

13951397
// Strip sentinel bit.
1396-
NextElt = (TableVal << 1) >> 1;
1398+
NextElt = TableVal & 0x7fff;
13971399
} else {
1398-
// Decode the TableVal into an array of IITValues. If the entry was encoded
1399-
// into a single word in the table itself, decode it now.
1400+
// If the entry was encoded into a single word in the table itself, decode
1401+
// it from an array of nibbles to an array of bytes.
14001402
do {
14011403
IITValues.push_back(TableVal & 0xF);
14021404
TableVal >>= 4;

llvm/utils/TableGen/IntrinsicEmitter.cpp

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class IntrinsicEmitter {
6161
void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints,
6262
raw_ostream &OS);
6363
void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
64+
6465
void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
6566
void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints,
6667
bool IsClang, raw_ostream &OS);
@@ -282,11 +283,34 @@ static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) {
282283
return TypeSig;
283284
}
284285

286+
// Pack the type signature into 32-bit fixed encoding word.
287+
std::optional<unsigned> encodePacked(const TypeSigTy &TypeSig) {
288+
if (TypeSig.size() > 8)
289+
return std::nullopt;
290+
291+
unsigned Result = 0;
292+
for (unsigned char C : reverse(TypeSig)) {
293+
if (C > 15)
294+
return std::nullopt;
295+
Result = (Result << 4) | C;
296+
}
297+
return Result;
298+
}
299+
285300
void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints,
286301
raw_ostream &OS) {
287-
// If we can compute a 32-bit fixed encoding for this intrinsic, do so and
302+
// Note: the code below can be switched to use 32-bit fixed encoding by
303+
// flipping the flag below.
304+
constexpr bool Use16BitFixedEncoding = true;
305+
using EncodingTy =
306+
std::conditional_t<Use16BitFixedEncoding, uint16_t, unsigned>;
307+
const unsigned Mask = Use16BitFixedEncoding ? 0x7FFF : 0x7FFFFFFF;
308+
const unsigned MSBPostion = Use16BitFixedEncoding ? 15 : 31;
309+
StringRef TypeName = Use16BitFixedEncoding ? "uint16_t" : "unsigned";
310+
311+
// If we can compute a 16/32-bit fixed encoding for this intrinsic, do so and
288312
// capture it in this vector, otherwise store a ~0U.
289-
std::vector<unsigned> FixedEncodings;
313+
std::vector<EncodingTy> FixedEncodings;
290314
SequenceToOffsetTable<TypeSigTy> LongEncodingTable;
291315

292316
FixedEncodings.reserve(Ints.size());
@@ -296,69 +320,59 @@ void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints,
296320
// Get the signature for the intrinsic.
297321
TypeSigTy TypeSig = ComputeTypeSignature(Int);
298322

299-
// Check to see if we can encode it into a 32-bit word. We can only encode
300-
// 8 nibbles into a 32-bit word.
301-
if (TypeSig.size() <= 8) {
302-
// Attempt to pack elements of TypeSig into a 32-bit word, starting from
303-
// the most significant nibble.
304-
unsigned Result = 0;
305-
bool Failed = false;
306-
for (unsigned char C : reverse(TypeSig)) {
307-
if (C > 15) {
308-
Failed = true;
309-
break;
310-
}
311-
Result = (Result << 4) | C;
312-
}
313-
314-
// If this could be encoded into a 31-bit word, return it.
315-
if (!Failed && (Result >> 31) == 0) {
316-
FixedEncodings.push_back(Result);
317-
continue;
318-
}
323+
// Check to see if we can encode it into a 16/32 bit word.
324+
std::optional<unsigned> Result = encodePacked(TypeSig);
325+
if (Result && (*Result & Mask) == Result) {
326+
FixedEncodings.push_back(static_cast<EncodingTy>(*Result));
327+
continue;
319328
}
320329

321-
// Otherwise, we're going to unique the sequence into the
322-
// LongEncodingTable, and use its offset in the 32-bit table instead.
323330
LongEncodingTable.add(TypeSig);
324331

325332
// This is a placehold that we'll replace after the table is laid out.
326-
FixedEncodings.push_back(~0U);
333+
FixedEncodings.push_back(static_cast<EncodingTy>(~0U));
327334
}
328335

329336
LongEncodingTable.layout();
330337

331-
OS << R"(// Global intrinsic function declaration type table.
338+
OS << formatv(R"(// Global intrinsic function declaration type table.
332339
#ifdef GET_INTRINSIC_GENERATOR_GLOBAL
333-
static constexpr unsigned IIT_Table[] = {
334-
)";
340+
static constexpr {0} IIT_Table[] = {{
341+
)",
342+
TypeName);
335343

344+
unsigned MaxOffset = 0;
336345
for (auto [Idx, FixedEncoding, Int] : enumerate(FixedEncodings, Ints)) {
337346
if ((Idx & 7) == 7)
338347
OS << "\n ";
339348

340349
// If the entry fit in the table, just emit it.
341-
if (FixedEncoding != ~0U) {
350+
if ((FixedEncoding & Mask) == FixedEncoding) {
342351
OS << "0x" << Twine::utohexstr(FixedEncoding) << ", ";
343352
continue;
344353
}
345354

346355
TypeSigTy TypeSig = ComputeTypeSignature(Int);
356+
unsigned Offset = LongEncodingTable.get(TypeSig);
357+
MaxOffset = std::max(MaxOffset, Offset);
347358

348359
// Otherwise, emit the offset into the long encoding table. We emit it this
349360
// way so that it is easier to read the offset in the .def file.
350-
OS << "(1U<<31) | " << LongEncodingTable.get(TypeSig) << ", ";
361+
OS << formatv("(1U<<{0}) | {1}, ", MSBPostion, Offset);
351362
}
352363

353364
OS << "0\n};\n\n";
354365

366+
// verify that all offsets will fit in 16/32 bits.
367+
if ((MaxOffset & Mask) != MaxOffset)
368+
PrintFatalError("Offset of long encoding table exceeds encoding bits");
369+
355370
// Emit the shared table of register lists.
356371
OS << "static constexpr unsigned char IIT_LongEncodingTable[] = {\n";
357372
if (!LongEncodingTable.empty())
358373
LongEncodingTable.emit(
359374
OS, [](raw_ostream &OS, unsigned char C) { OS << (unsigned)C; });
360-
OS << " 255\n};\n\n";
361-
375+
OS << " 255\n};\n";
362376
OS << "#endif\n\n"; // End of GET_INTRINSIC_GENERATOR_GLOBAL
363377
}
364378

0 commit comments

Comments
 (0)