Skip to content

Commit 98602f8

Browse files
committed
[LLVM][TableGen][DecoderEmitter] Add option to use lambdas in decodeToMCInst
Add option `use-lambda-in-decode-to-mcinst` to use a table of lambdas 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 lambdas 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 d10079e commit 98602f8

File tree

2 files changed

+130
-8
lines changed

2 files changed

+130
-8
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-lambda-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: decodeToMCInst
75+
// CHECK: decodeLambda0 =
76+
// CHECK: decodeLambda1 =
77+
// CHECK: decodeLambda2 =
78+
// CHECK: decodeLambda3 =
79+
// CHECK: decodeLambdaTable[]
80+
// CHECK-NEXT: decodeLambda0
81+
// CHECK-NEXT: decodeLambda1
82+
// CHECK-NEXT: decodeLambda2
83+
// CHECK-NEXT: decodeLambda3
84+
// CHECK: return decodeLambdaTable[Idx]

llvm/utils/TableGen/DecoderEmitter.cpp

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ 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> UseLambdaInDecodetoMCInst(
87+
"use-lambda-in-decode-to-mcinst",
88+
cl::desc("Use a table of lambdas instead of a switch case in the\n"
89+
"generated `decodeToMCInst` function. Helps improve compile time\n"
90+
"of the generated code."),
91+
cl::init(false), cl::cat(DisassemblerEmitterCat));
92+
8693
STATISTIC(NumEncodings, "Number of encodings considered");
8794
STATISTIC(NumEncodingsLackingDisasm,
8895
"Number of encodings without disassembler info");
@@ -1082,15 +1089,46 @@ void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS,
10821089
<< "using TmpType = "
10831090
"std::conditional_t<std::is_integral<InsnType>::"
10841091
"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";
1092+
1093+
if (UseLambdaInDecodetoMCInst) {
1094+
// Emit one lambda for each case first.
1095+
for (const auto &[Index, Decoder] : enumerate(Decoders)) {
1096+
OS << Indent << "auto decodeLambda" << Index << " = [](DecodeStatus S,\n"
1097+
<< Indent << " InsnType insn, MCInst &MI,\n"
1098+
<< Indent << " uint64_t Address, \n"
1099+
<< Indent << " const MCDisassembler *Decoder,\n"
1100+
<< Indent << " bool &DecodeComplete) {\n";
1101+
OS << Indent + 2 << "[[maybe_unused]] TmpType tmp;\n";
1102+
OS << Decoder;
1103+
OS << Indent + 2 << "return S;\n";
1104+
OS << Indent << "};\n";
1105+
}
1106+
// Build a table of lambdas.
1107+
1108+
OS << R"(
1109+
using LambdaTy =
1110+
function_ref<DecodeStatus(DecodeStatus, InsnType, MCInst &, uint64_t,
1111+
const MCDisassembler *, bool &)>;
1112+
)";
1113+
OS << Indent << "const static LambdaTy decodeLambdaTable[] = {\n";
1114+
for (size_t Index : llvm::seq(Decoders.size()))
1115+
OS << Indent + 2 << "decodeLambda" << Index << ",\n";
1116+
OS << Indent << "};\n";
1117+
OS << Indent << "if (Idx >= " << Decoders.size() << ")\n";
1118+
OS << Indent + 2 << "llvm_unreachable(\"Invalid index!\");\n";
1119+
OS << Indent
1120+
<< "return decodeLambdaTable[Idx](S, insn, MI, Address, Decoder, "
1121+
"DecodeComplete);\n";
1122+
} else {
1123+
OS << Indent << "switch (Idx) {\n";
1124+
OS << Indent << "default: llvm_unreachable(\"Invalid index!\");\n";
1125+
for (const auto &[Index, Decoder] : enumerate(Decoders)) {
1126+
OS << Indent << "case " << Index << ":\n";
1127+
OS << Decoder;
1128+
OS << Indent + 2 << "return S;\n";
1129+
}
1130+
OS << Indent << "}\n";
10921131
}
1093-
OS << Indent << "}\n";
10941132
Indent -= 2;
10951133
OS << Indent << "}\n";
10961134
}

0 commit comments

Comments
 (0)