Skip to content

Commit 9375962

Browse files
authored
[TableGen][GlobalISel] Specialize more MatchTable Opcodes (#89736)
The vast majority of the following (very common) opcodes were always called with identical arguments: - `GIM_CheckType` for the root - `GIM_CheckRegBankForClass` for the root - `GIR_Copy` between the old and new root - `GIR_ConstrainSelectedInstOperands` on the new root - `GIR_BuildMI` to create the new root I added overloaded version of each opcode specialized for the root instructions. It always saves between 1 and 2 bytes per instance depending on the number of arguments specialized into the opcode. Some of these opcodes had between 5 and 15k occurences in the AArch64 GlobalISel Match Table. Additionally, the following opcodes are almost always used in the same sequence: - `GIR_EraseFromParent 0` + `GIR_Done` - `GIR_EraseRootFromParent_Done` has been created to do both. Saves 2 bytes per occurence. - `GIR_IsSafeToFold` was *always* called for each InsnID except 0. - Changed the opcode to take the number of instructions to check after `MI[0]` The savings from these are pretty neat. For `AArch64GenGlobalISel.inc`: - `AArch64InstructionSelector.cpp.o` goes down from 772kb to 704kb (-10% code size) - Self-reported MatchTable size goes from 420380 bytes to 352426 bytes (~ -17%) A smaller match table means a faster match table because we spend less time iterating and decoding. I don't have a solid measurement methodology for GlobalISel performance so I don't have precise numbers but I saw a few % of improvements in a simple testcase.
1 parent 7da6342 commit 9375962

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1073
-1042
lines changed

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ enum {
217217
/// - OpIdx(ULEB128) - Operand index
218218
/// - Ty(1) - Expected type
219219
GIM_CheckType,
220+
/// GIM_CheckType but InsnID is omitted and defaults to zero.
221+
GIM_RootCheckType,
220222

221223
/// Check the type of a pointer to any address space.
222224
/// - InsnID(ULEB128) - Instruction ID
@@ -229,6 +231,8 @@ enum {
229231
/// - OpIdx(ULEB128) - Operand index
230232
/// - RC(2) - Expected register bank (specified as a register class)
231233
GIM_CheckRegBankForClass,
234+
/// GIM_CheckRegBankForClass but InsnID is omitted and defaults to zero.
235+
GIM_RootCheckRegBankForClass,
232236

233237
/// Check the operand matches a complex predicate
234238
/// - InsnID(ULEB128) - Instruction ID
@@ -278,9 +282,9 @@ enum {
278282
/// - OpIdx(ULEB128) - Operand index
279283
GIM_CheckIsImm,
280284

281-
/// Check if the specified operand is safe to fold into the current
282-
/// instruction.
283-
/// - InsnID(ULEB128) - Instruction ID
285+
/// Checks if the matched instructions numbered [1, 1+N) can
286+
/// be folded into the root (inst 0).
287+
/// - Num(1)
284288
GIM_CheckIsSafeToFold,
285289

286290
/// Check the specified operands are identical.
@@ -338,6 +342,8 @@ enum {
338342
/// - InsnID(ULEB128) - Instruction ID to define
339343
/// - Opcode(2) - The new opcode to use
340344
GIR_BuildMI,
345+
/// GIR_BuildMI but InsnID is omitted and defaults to zero.
346+
GIR_BuildRootMI,
341347

342348
/// Builds a constant and stores its result in a TempReg.
343349
/// - TempRegID(ULEB128) - Temp Register to define.
@@ -349,6 +355,8 @@ enum {
349355
/// - OldInsnID(ULEB128) - Instruction ID to copy from
350356
/// - OpIdx(ULEB128) - The operand to copy
351357
GIR_Copy,
358+
/// GIR_Copy but with both New/OldInsnIDs omitted and defaulting to zero.
359+
GIR_RootToRootCopy,
352360

353361
/// Copy an operand to the specified instruction or add a zero register if the
354362
/// operand is a zero immediate.
@@ -506,6 +514,9 @@ enum {
506514
/// description.
507515
/// - InsnID(ULEB128) - Instruction ID to modify
508516
GIR_ConstrainSelectedInstOperands,
517+
/// GIR_ConstrainSelectedInstOperands but InsnID is omitted and defaults to
518+
/// zero.
519+
GIR_RootConstrainSelectedInstOperands,
509520

510521
/// Merge all memory operands into instruction.
511522
/// - InsnID(ULEB128) - Instruction ID to modify
@@ -518,6 +529,9 @@ enum {
518529
/// - InsnID(ULEB128) - Instruction ID to erase
519530
GIR_EraseFromParent,
520531

532+
/// Combines both a GIR_EraseFromParent 0 + GIR_Done
533+
GIR_EraseRootFromParent_Done,
534+
521535
/// Create a new temporary register that's not constrained.
522536
/// - TempRegID(ULEB128) - The temporary register ID to initialize.
523537
/// - Ty(1) - Expected type

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

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ bool GIMatchTableExecutor::executeMatchTable(
131131
return V;
132132
};
133133

134+
const auto eraseImpl = [&](MachineInstr *MI) {
135+
// If we're erasing the insertion point, ensure we don't leave a dangling
136+
// pointer in the builder.
137+
if (Builder.getInsertPt() == MI)
138+
Builder.setInsertPt(*MI->getParent(), ++MI->getIterator());
139+
if (Observer)
140+
Observer->erasingInstr(*MI);
141+
MI->eraseFromParent();
142+
};
143+
134144
while (true) {
135145
assert(CurrentIdx != ~0u && "Invalid MatchTable index");
136146
uint8_t MatcherOpcode = MatchTable[CurrentIdx++];
@@ -661,8 +671,9 @@ bool GIMatchTableExecutor::executeMatchTable(
661671

662672
break;
663673
}
674+
case GIM_RootCheckType:
664675
case GIM_CheckType: {
665-
uint64_t InsnID = readULEB();
676+
uint64_t InsnID = (MatcherOpcode == GIM_RootCheckType) ? 0 : readULEB();
666677
uint64_t OpIdx = readULEB();
667678
int TypeID = readS8();
668679
DEBUG_WITH_TYPE(TgtExecutor::getName(),
@@ -741,8 +752,11 @@ bool GIMatchTableExecutor::executeMatchTable(
741752
State.RecordedTypes[TypeIdx] = MRI.getType(Op.getReg());
742753
break;
743754
}
755+
756+
case GIM_RootCheckRegBankForClass:
744757
case GIM_CheckRegBankForClass: {
745-
uint64_t InsnID = readULEB();
758+
uint64_t InsnID =
759+
(MatcherOpcode == GIM_RootCheckRegBankForClass) ? 0 : readULEB();
746760
uint64_t OpIdx = readULEB();
747761
uint16_t RCEnum = readU16();
748762
DEBUG_WITH_TYPE(TgtExecutor::getName(),
@@ -898,14 +912,16 @@ bool GIMatchTableExecutor::executeMatchTable(
898912
break;
899913
}
900914
case GIM_CheckIsSafeToFold: {
901-
uint64_t InsnID = readULEB();
915+
uint64_t NumInsn = MatchTable[CurrentIdx++];
902916
DEBUG_WITH_TYPE(TgtExecutor::getName(),
903-
dbgs() << CurrentIdx << ": GIM_CheckIsSafeToFold(MIs["
904-
<< InsnID << "])\n");
905-
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
906-
if (!isObviouslySafeToFold(*State.MIs[InsnID], *State.MIs[0])) {
907-
if (handleReject() == RejectAndGiveUp)
908-
return false;
917+
dbgs() << CurrentIdx << ": GIM_CheckIsSafeToFold(N = "
918+
<< NumInsn << ")\n");
919+
MachineInstr &Root = *State.MIs[0];
920+
for (unsigned K = 1, E = NumInsn + 1; K < E; ++K) {
921+
if (!isObviouslySafeToFold(*State.MIs[K], Root)) {
922+
if (handleReject() == RejectAndGiveUp)
923+
return false;
924+
}
909925
}
910926
break;
911927
}
@@ -1011,8 +1027,9 @@ bool GIMatchTableExecutor::executeMatchTable(
10111027
break;
10121028
}
10131029

1030+
case GIR_BuildRootMI:
10141031
case GIR_BuildMI: {
1015-
uint64_t NewInsnID = readULEB();
1032+
uint64_t NewInsnID = (MatcherOpcode == GIR_BuildRootMI) ? 0 : readULEB();
10161033
uint16_t Opcode = readU16();
10171034
if (NewInsnID >= OutMIs.size())
10181035
OutMIs.resize(NewInsnID + 1);
@@ -1034,9 +1051,12 @@ bool GIMatchTableExecutor::executeMatchTable(
10341051
break;
10351052
}
10361053

1054+
case GIR_RootToRootCopy:
10371055
case GIR_Copy: {
1038-
uint64_t NewInsnID = readULEB();
1039-
uint64_t OldInsnID = readULEB();
1056+
uint64_t NewInsnID =
1057+
(MatcherOpcode == GIR_RootToRootCopy) ? 0 : readULEB();
1058+
uint64_t OldInsnID =
1059+
(MatcherOpcode == GIR_RootToRootCopy) ? 0 : readULEB();
10401060
uint64_t OpIdx = readULEB();
10411061
assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction");
10421062
OutMIs[NewInsnID].add(State.MIs[OldInsnID]->getOperand(OpIdx));
@@ -1361,8 +1381,11 @@ bool GIMatchTableExecutor::executeMatchTable(
13611381
break;
13621382
}
13631383

1384+
case GIR_RootConstrainSelectedInstOperands:
13641385
case GIR_ConstrainSelectedInstOperands: {
1365-
uint64_t InsnID = readULEB();
1386+
uint64_t InsnID = (MatcherOpcode == GIR_RootConstrainSelectedInstOperands)
1387+
? 0
1388+
: readULEB();
13661389
assert(OutMIs[InsnID] && "Attempted to add to undefined instruction");
13671390
constrainSelectedInstRegOperands(*OutMIs[InsnID].getInstr(), TII, TRI,
13681391
RBI);
@@ -1372,7 +1395,6 @@ bool GIMatchTableExecutor::executeMatchTable(
13721395
<< InsnID << "])\n");
13731396
break;
13741397
}
1375-
13761398
case GIR_MergeMemOperands: {
13771399
uint64_t InsnID = readULEB();
13781400
uint64_t NumInsn = MatchTable[CurrentIdx++];
@@ -1391,24 +1413,24 @@ bool GIMatchTableExecutor::executeMatchTable(
13911413
DEBUG_WITH_TYPE(TgtExecutor::getName(), dbgs() << ")\n");
13921414
break;
13931415
}
1394-
13951416
case GIR_EraseFromParent: {
13961417
uint64_t InsnID = readULEB();
13971418
MachineInstr *MI = State.MIs[InsnID];
13981419
assert(MI && "Attempted to erase an undefined instruction");
13991420
DEBUG_WITH_TYPE(TgtExecutor::getName(),
14001421
dbgs() << CurrentIdx << ": GIR_EraseFromParent(MIs["
14011422
<< InsnID << "])\n");
1402-
// If we're erasing the insertion point, ensure we don't leave a dangling
1403-
// pointer in the builder.
1404-
if (Builder.getInsertPt() == MI)
1405-
Builder.setInsertPt(*MI->getParent(), ++MI->getIterator());
1406-
if (Observer)
1407-
Observer->erasingInstr(*MI);
1408-
MI->eraseFromParent();
1423+
eraseImpl(MI);
14091424
break;
14101425
}
1411-
1426+
case GIR_EraseRootFromParent_Done: {
1427+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
1428+
dbgs()
1429+
<< CurrentIdx << ": GIR_EraseRootFromParent_Done\n");
1430+
eraseImpl(State.MIs[0]);
1431+
propagateFlags();
1432+
return true;
1433+
}
14121434
case GIR_MakeTempReg: {
14131435
uint64_t TempRegID = readULEB();
14141436
int TypeID = readS8();

llvm/test/TableGen/ContextlessPredicates.td

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,50 +22,50 @@ def : Pat<(test_atomic_op_frag GPR32:$ptr, GPR32:$val) ,
2222

2323
// CHECK_NOPT-LABEL: const uint8_t *MyTargetInstructionSelector::getMatchTable() const {
2424
// CHECK_NOPT-NEXT: constexpr static uint8_t MatchTable0[] = {
25-
// CHECK_NOPT-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ GIMT_Encode4(58), // Rule ID 0 //
25+
// CHECK_NOPT-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ GIMT_Encode4(52), // Rule ID 0 //
2626
// CHECK_NOPT-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
2727
// CHECK_NOPT-NEXT: GIM_CheckOpcode, /*MI*/0, GIMT_Encode2(TargetOpcode::G_ATOMICRMW_XCHG),
2828
// CHECK_NOPT-NEXT: GIM_CheckMemorySizeEqualTo, /*MI*/0, /*MMO*/0, /*Size*/GIMT_Encode4(4),
2929
// CHECK_NOPT-NEXT: // MIs[0] DstI[dst]
30-
// CHECK_NOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
31-
// CHECK_NOPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
30+
// CHECK_NOPT-NEXT: GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s32,
31+
// CHECK_NOPT-NEXT: GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
3232
// CHECK_NOPT-NEXT: // MIs[0] ptr
3333
// CHECK_NOPT-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32,
34-
// CHECK_NOPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
34+
// CHECK_NOPT-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
3535
// CHECK_NOPT-NEXT: // MIs[0] val
36-
// CHECK_NOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
37-
// CHECK_NOPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
36+
// CHECK_NOPT-NEXT: GIM_RootCheckType, /*Op*/2, /*Type*/GILLT_s32,
37+
// CHECK_NOPT-NEXT: GIM_RootCheckRegBankForClass, /*Op*/2, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
3838
// CHECK_NOPT-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_test_atomic_op_frag),
3939
// CHECK_NOPT-NEXT: // (atomic_swap:{ *:[i32] } GPR32:{ *:[i32] }:$ptr, GPR32:{ *:[i32] }:$val)<<P:Predicate_test_atomic_op_frag>> => (INSN:{ *:[i32] } GPR32:{ *:[i32] }:$ptr, GPR32:{ *:[i32] }:$val)
4040
// CHECK_NOPT-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::INSN),
41-
// CHECK_NOPT-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
41+
// CHECK_NOPT-NEXT: GIR_RootConstrainSelectedInstOperands,
4242
// CHECK_NOPT-NEXT: // GIR_Coverage, 0,
4343
// CHECK_NOPT-NEXT: GIR_Done,
44-
// CHECK_NOPT-NEXT: // Label 0: @58
44+
// CHECK_NOPT-NEXT: // Label 0: @52
4545
// CHECK_NOPT-NEXT: GIM_Reject,
4646
// CHECK_NOPT-NEXT: };
4747
// CHECK_NOPT-NEXT: return MatchTable0;
4848
// CHECK_NOPT-NEXT: }
4949

5050
// CHECK_OPT-LABEL: const uint8_t *MyTargetInstructionSelector::getMatchTable() const {
5151
// CHECK_OPT-NEXT: constexpr static uint8_t MatchTable0[] = {
52-
// CHECK_OPT-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ GIMT_Encode4(55), // Rule ID 0 //
52+
// CHECK_OPT-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ GIMT_Encode4(49), // Rule ID 0 //
5353
// CHECK_OPT-NEXT: GIM_CheckOpcode, /*MI*/0, GIMT_Encode2(TargetOpcode::G_ATOMICRMW_XCHG),
54-
// CHECK_OPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
55-
// CHECK_OPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
54+
// CHECK_OPT-NEXT: GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s32,
55+
// CHECK_OPT-NEXT: GIM_RootCheckType, /*Op*/2, /*Type*/GILLT_s32,
5656
// CHECK_OPT-NEXT: GIM_CheckMemorySizeEqualTo, /*MI*/0, /*MMO*/0, /*Size*/GIMT_Encode4(4),
57-
// CHECK_OPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
57+
// CHECK_OPT-NEXT: GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
5858
// CHECK_OPT-NEXT: // MIs[0] ptr
5959
// CHECK_OPT-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32,
60-
// CHECK_OPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
61-
// CHECK_OPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
60+
// CHECK_OPT-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
61+
// CHECK_OPT-NEXT: GIM_RootCheckRegBankForClass, /*Op*/2, /*RC*/GIMT_Encode2(MyTarget::GPR32RegClassID),
6262
// CHECK_OPT-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_test_atomic_op_frag),
6363
// CHECK_OPT-NEXT: // (atomic_swap:{ *:[i32] } GPR32:{ *:[i32] }:$ptr, GPR32:{ *:[i32] }:$val)<<P:Predicate_test_atomic_op_frag>> => (INSN:{ *:[i32] } GPR32:{ *:[i32] }:$ptr, GPR32:{ *:[i32] }:$val)
6464
// CHECK_OPT-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::INSN),
65-
// CHECK_OPT-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
65+
// CHECK_OPT-NEXT: GIR_RootConstrainSelectedInstOperands,
6666
// CHECK_OPT-NEXT: // GIR_Coverage, 0,
6767
// CHECK_OPT-NEXT: GIR_Done,
68-
// CHECK_OPT-NEXT: // Label 0: @55
68+
// CHECK_OPT-NEXT: // Label 0: @49
6969
// CHECK_OPT-NEXT: GIM_Reject,
7070
// CHECK_OPT-NEXT: };
7171
// CHECK_OPT-NEXT: return MatchTable0;

0 commit comments

Comments
 (0)