Skip to content

Commit ed5f8f2

Browse files
authored
[LLVM][DecoderEmitter] Add option to use function table in decodeToMCInst (#144814)
Add option `use-fn-table-in-decode-to-mcinst` to use a table of function pointers instead of a switch case in the generated `decodeToMCInst` function. When the number of switch cases in this function is large, the generated code takes a long time to compile in release builds. Using a table of function pointers instead improves the compile time significantly (~3x speedup in compiling the code in a downstream target). This option will allow targets to opt into this mode if they desire for better build times. Tested with `check-llvm-mc` with the option enabled by default.
1 parent f608716 commit ed5f8f2

File tree

2 files changed

+153
-24
lines changed

2 files changed

+153
-24
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// RUN: llvm-tblgen -gen-disassembler -use-fn-table-in-decode-to-mcinst -I %p/../../include %s | FileCheck %s
2+
3+
include "llvm/Target/Target.td"
4+
5+
def archInstrInfo : InstrInfo { }
6+
7+
def arch : Target {
8+
let InstructionSet = archInstrInfo;
9+
}
10+
11+
let Namespace = "arch" in {
12+
def R0 : Register<"r0">;
13+
def R1 : Register<"r1">;
14+
def R2 : Register<"r2">;
15+
def R3 : Register<"r3">;
16+
}
17+
def Regs : RegisterClass<"Regs", [i32], 32, (add R0, R1, R2, R3)>;
18+
19+
class TestInstruction : Instruction {
20+
let Size = 1;
21+
let OutOperandList = (outs);
22+
field bits<8> Inst;
23+
field bits<8> SoftFail = 0;
24+
}
25+
26+
// Define instructions to generate 4 cases in decodeToMCInst.
27+
// Lower 2 bits define the number of operands. Each register operand
28+
// needs 2 bits to encode.
29+
30+
// An instruction with no inputs. Encoded with lower 2 bits = 0 and upper
31+
// 6 bits = 0 as well.
32+
def Inst0 : TestInstruction {
33+
let Inst = 0x0;
34+
let InOperandList = (ins);
35+
let AsmString = "Inst0";
36+
}
37+
38+
// An instruction with a single input. Encoded with lower 2 bits = 1 and the
39+
// single input in bits 2-3.
40+
def Inst1 : TestInstruction {
41+
bits<2> r0;
42+
let Inst{1-0} = 1;
43+
let Inst{3-2} = r0;
44+
let InOperandList = (ins Regs:$r0);
45+
let AsmString = "Inst1";
46+
}
47+
48+
// An instruction with two inputs. Encoded with lower 2 bits = 2 and the
49+
// inputs in bits 2-3 and 4-5.
50+
def Inst2 : TestInstruction {
51+
bits<2> r0;
52+
bits<2> r1;
53+
let Inst{1-0} = 2;
54+
let Inst{3-2} = r0;
55+
let Inst{5-4} = r1;
56+
let InOperandList = (ins Regs:$r0, Regs:$r1);
57+
let AsmString = "Inst2";
58+
}
59+
60+
// An instruction with three inputs. Encoded with lower 2 bits = 3 and the
61+
// inputs in bits 2-3 and 4-5 and 6-7.
62+
def Inst3 : TestInstruction {
63+
bits<2> r0;
64+
bits<2> r1;
65+
bits<2> r2;
66+
let Inst{1-0} = 3;
67+
let Inst{3-2} = r0;
68+
let Inst{5-4} = r1;
69+
let Inst{7-6} = r2;
70+
let InOperandList = (ins Regs:$r0, Regs:$r1, Regs:$r2);
71+
let AsmString = "Inst3";
72+
}
73+
74+
// CHECK-LABEL: DecodeStatus decodeFn0(DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const MCDisassembler *Decoder, bool &DecodeComplete)
75+
// CHECK-LABEL: DecodeStatus decodeFn1(DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const MCDisassembler *Decoder, bool &DecodeComplete)
76+
// CHECK-LABEL: DecodeStatus decodeFn2(DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const MCDisassembler *Decoder, bool &DecodeComplete)
77+
// CHECK-LABEL: DecodeStatus decodeFn3(DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const MCDisassembler *Decoder, bool &DecodeComplete)
78+
// CHECK-LABEL: decodeToMCInst(unsigned Idx, DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const MCDisassembler *Decoder, bool &DecodeComplete)
79+
// CHECK: static constexpr DecodeFnTy decodeFnTable[]
80+
// CHECK-NEXT: decodeFn0,
81+
// CHECK-NEXT: decodeFn1,
82+
// CHECK-NEXT: decodeFn2,
83+
// CHECK-NEXT: decodeFn3,
84+
// CHECK: return decodeFnTable[Idx](S, insn, MI, Address, Decoder, DecodeComplete)

llvm/utils/TableGen/DecoderEmitter.cpp

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ static cl::opt<bool> LargeTable(
8383
"in the table instead of the default 16 bits."),
8484
cl::init(false), cl::cat(DisassemblerEmitterCat));
8585

86+
static cl::opt<bool> UseFnTableInDecodetoMCInst(
87+
"use-fn-table-in-decode-to-mcinst",
88+
cl::desc(
89+
"Use a table of function pointers instead of a switch case in the\n"
90+
"generated `decodeToMCInst` function. Helps improve compile time\n"
91+
"of the generated code."),
92+
cl::init(false), cl::cat(DisassemblerEmitterCat));
93+
8694
STATISTIC(NumEncodings, "Number of encodings considered");
8795
STATISTIC(NumEncodingsLackingDisasm,
8896
"Number of encodings without disassembler info");
@@ -1066,31 +1074,67 @@ void DecoderEmitter::emitPredicateFunction(formatted_raw_ostream &OS,
10661074
void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS,
10671075
DecoderSet &Decoders,
10681076
indent Indent) const {
1069-
// The decoder function is just a big switch statement based on the
1070-
// input decoder index.
1071-
OS << Indent << "template <typename InsnType>\n";
1072-
OS << Indent << "static DecodeStatus decodeToMCInst(DecodeStatus S,"
1073-
<< " unsigned Idx, InsnType insn, MCInst &MI,\n";
1074-
OS << Indent << " uint64_t "
1075-
<< "Address, const MCDisassembler *Decoder, bool &DecodeComplete) {\n";
1076-
Indent += 2;
1077-
OS << Indent << "DecodeComplete = true;\n";
1077+
// The decoder function is just a big switch statement or a table of function
1078+
// pointers based on the input decoder index.
1079+
10781080
// TODO: When InsnType is large, using uint64_t limits all fields to 64 bits
10791081
// It would be better for emitBinaryParser to use a 64-bit tmp whenever
10801082
// possible but fall back to an InsnType-sized tmp for truly large fields.
1081-
OS << Indent
1082-
<< "using TmpType = "
1083-
"std::conditional_t<std::is_integral<InsnType>::"
1084-
"value, InsnType, uint64_t>;\n";
1085-
OS << Indent << "TmpType tmp;\n";
1086-
OS << Indent << "switch (Idx) {\n";
1087-
OS << Indent << "default: llvm_unreachable(\"Invalid index!\");\n";
1088-
for (const auto &[Index, Decoder] : enumerate(Decoders)) {
1089-
OS << Indent << "case " << Index << ":\n";
1090-
OS << Decoder;
1091-
OS << Indent + 2 << "return S;\n";
1083+
StringRef TmpTypeDecl =
1084+
"using TmpType = std::conditional_t<std::is_integral<InsnType>::value, "
1085+
"InsnType, uint64_t>;\n";
1086+
StringRef DecodeParams =
1087+
"DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const "
1088+
"MCDisassembler *Decoder, bool &DecodeComplete";
1089+
1090+
if (UseFnTableInDecodetoMCInst) {
1091+
// Emit a function for each case first.
1092+
for (const auto &[Index, Decoder] : enumerate(Decoders)) {
1093+
OS << Indent << "template <typename InsnType>\n";
1094+
OS << Indent << "DecodeStatus decodeFn" << Index << "(" << DecodeParams
1095+
<< ") {\n";
1096+
Indent += 2;
1097+
OS << Indent << TmpTypeDecl;
1098+
OS << Indent << "[[maybe_unused]] TmpType tmp;\n";
1099+
OS << Decoder;
1100+
OS << Indent << "return S;\n";
1101+
Indent -= 2;
1102+
OS << Indent << "}\n\n";
1103+
}
1104+
}
1105+
1106+
OS << Indent << "// Handling " << Decoders.size() << " cases.\n";
1107+
OS << Indent << "template <typename InsnType>\n";
1108+
OS << Indent << "static DecodeStatus decodeToMCInst(unsigned Idx, "
1109+
<< DecodeParams << ") {\n";
1110+
Indent += 2;
1111+
OS << Indent << "DecodeComplete = true;\n";
1112+
1113+
if (UseFnTableInDecodetoMCInst) {
1114+
// Build a table of function pointers.
1115+
OS << Indent << "using DecodeFnTy = DecodeStatus (*)(" << DecodeParams
1116+
<< ");\n";
1117+
OS << Indent << "static constexpr DecodeFnTy decodeFnTable[] = {\n";
1118+
for (size_t Index : llvm::seq(Decoders.size()))
1119+
OS << Indent + 2 << "decodeFn" << Index << ",\n";
1120+
OS << Indent << "};\n";
1121+
OS << Indent << "if (Idx >= " << Decoders.size() << ")\n";
1122+
OS << Indent + 2 << "llvm_unreachable(\"Invalid index!\");\n";
1123+
OS << Indent
1124+
<< "return decodeFnTable[Idx](S, insn, MI, Address, Decoder, "
1125+
"DecodeComplete);\n";
1126+
} else {
1127+
OS << Indent << TmpTypeDecl;
1128+
OS << Indent << "TmpType tmp;\n";
1129+
OS << Indent << "switch (Idx) {\n";
1130+
OS << Indent << "default: llvm_unreachable(\"Invalid index!\");\n";
1131+
for (const auto &[Index, Decoder] : enumerate(Decoders)) {
1132+
OS << Indent << "case " << Index << ":\n";
1133+
OS << Decoder;
1134+
OS << Indent + 2 << "return S;\n";
1135+
}
1136+
OS << Indent << "}\n";
10921137
}
1093-
OS << Indent << "}\n";
10941138
Indent -= 2;
10951139
OS << Indent << "}\n";
10961140
}
@@ -1267,7 +1311,8 @@ std::pair<unsigned, bool> FilterChooser::getDecoderIndex(DecoderSet &Decoders,
12671311
// FIXME: emitDecoder() function can take a buffer directly rather than
12681312
// a stream.
12691313
raw_svector_ostream S(Decoder);
1270-
bool HasCompleteDecoder = emitDecoder(S, indent(4), Opc);
1314+
indent Indent(UseFnTableInDecodetoMCInst ? 2 : 4);
1315+
bool HasCompleteDecoder = emitDecoder(S, Indent, Opc);
12711316

12721317
// Using the full decoder string as the key value here is a bit
12731318
// heavyweight, but is effective. If the string comparisons become a
@@ -2371,7 +2416,7 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
23712416
<< " makeUp(insn, Len);";
23722417
}
23732418
OS << R"(
2374-
S = decodeToMCInst(S, DecodeIdx, insn, MI, Address, DisAsm, DecodeComplete);
2419+
S = decodeToMCInst(DecodeIdx, S, insn, MI, Address, DisAsm, DecodeComplete);
23752420
assert(DecodeComplete);
23762421
23772422
LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc
@@ -2393,7 +2438,7 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
23932438
MCInst TmpMI;
23942439
TmpMI.setOpcode(Opc);
23952440
bool DecodeComplete;
2396-
S = decodeToMCInst(S, DecodeIdx, insn, TmpMI, Address, DisAsm, DecodeComplete);
2441+
S = decodeToMCInst(DecodeIdx, S, insn, TmpMI, Address, DisAsm, DecodeComplete);
23972442
LLVM_DEBUG(dbgs() << Loc << ": OPC_TryDecode: opcode " << Opc
23982443
<< ", using decoder " << DecodeIdx << ": ");
23992444

0 commit comments

Comments
 (0)