Skip to content

Commit 1d9291c

Browse files
committed
[MC] Rewrite tablegen for printInstrAlias to comiple faster, NFC
Before this change, the *InstPrinter.cpp files of each target where some of the slowest objects to compile in all of LLVM. See this snippet produced by ClangBuildAnalyzer: https://reviews.llvm.org/P8171$96 Search for "InstPrinter", and see that it shows up in a few places. Tablegen was emitting a large switch containing a sequence of operand checks, each of which created many conditions and many BBs. Register allocation and jump threading both did not scale well with such a large repetitive sequence of basic blocks. So, this change essentially turns those control flow structures into data. The previous structure looked like: switch (Opc) { case TGT::ADD: // check alias 1 if (MI->getOperandCount() == N && // check num opnds MI->getOperand(0).isReg() && // check opnd 0 ... MI->getOperand(1).isImm() && // check opnd 1 AsmString = "foo"; break; } // check alias 2 if (...) ... return false; The new structure looks like: OpToPatterns: Sorted table of opcodes mapping to pattern indices. \-> Patterns: List of patterns. Previous table points to subrange of patterns to match. \-> Conds: The if conditions above encoded as a kind and 32-bit value. See MCInstPrinter.cpp for the details of how the new data structures are interpreted. Here are some before and after metrics. Time to compile AArch64InstPrinter.cpp: 0m29.062s vs. 0m2.203s size of the obj: 3.9M vs. 676K size of clang.exe: 97M vs. 96M I have not benchmarked disassembly performance, but typically disassemblers are bottlenecked on IO and string processing, not alias matching, so I'm not sure it's interesting enough to be worth doing. Reviewers: RKSimon, andreadb, xbolva00, craig.topper Reviewed By: craig.topper Differential Revision: https://reviews.llvm.org/D70650
1 parent 84fdd9d commit 1d9291c

File tree

3 files changed

+305
-95
lines changed

3 files changed

+305
-95
lines changed

llvm/include/llvm/MC/MCInstPrinter.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace llvm {
1616

1717
class MCAsmInfo;
1818
class MCInst;
19+
class MCOperand;
1920
class MCInstrInfo;
2021
class MCRegisterInfo;
2122
class MCSubtargetInfo;
@@ -34,6 +35,8 @@ enum Style {
3435

3536
} // end namespace HexStyle
3637

38+
struct AliasMatchingData;
39+
3740
/// This is an instance of a target assembly language printer that
3841
/// converts an MCInst to valid target assembly syntax.
3942
class MCInstPrinter {
@@ -58,6 +61,10 @@ class MCInstPrinter {
5861
/// Utility function for printing annotations.
5962
void printAnnotation(raw_ostream &OS, StringRef Annot);
6063

64+
/// Helper for matching MCInsts to alias patterns when printing instructions.
65+
const char *matchAliasPatterns(const MCInst *MI, const MCSubtargetInfo *STI,
66+
const AliasMatchingData &M);
67+
6168
public:
6269
MCInstPrinter(const MCAsmInfo &mai, const MCInstrInfo &mii,
6370
const MCRegisterInfo &mri) : MAI(mai), MII(mii), MRI(mri) {}
@@ -104,6 +111,48 @@ class MCInstPrinter {
104111
format_object<uint64_t> formatHex(uint64_t Value) const;
105112
};
106113

114+
/// Map from opcode to pattern list by binary search.
115+
struct PatternsForOpcode {
116+
uint32_t Opcode;
117+
uint16_t PatternStart;
118+
uint16_t NumPatterns;
119+
};
120+
121+
/// Data for each alias pattern. Includes feature bits, string, number of
122+
/// operands, and a variadic list of conditions to check.
123+
struct AliasPattern {
124+
uint32_t AsmStrOffset;
125+
uint32_t AliasCondStart;
126+
uint8_t NumOperands;
127+
uint8_t NumConds;
128+
};
129+
130+
struct AliasPatternCond {
131+
enum CondKind : uint8_t {
132+
K_Feature, // Match only if a feature is enabled.
133+
K_NegFeature, // Match only if a feature is disabled.
134+
K_Ignore, // Match any operand.
135+
K_Reg, // Match a specific register.
136+
K_TiedReg, // Match another already matched register.
137+
K_Imm, // Match a specific immediate.
138+
K_RegClass, // Match registers in a class.
139+
K_Custom, // Call custom matcher by index.
140+
};
141+
142+
CondKind Kind;
143+
uint32_t Value;
144+
};
145+
146+
/// Tablegenerated data structures needed to match alias patterns.
147+
struct AliasMatchingData {
148+
ArrayRef<PatternsForOpcode> OpToPatterns;
149+
ArrayRef<AliasPattern> Patterns;
150+
ArrayRef<AliasPatternCond> PatternConds;
151+
StringRef AsmStrings;
152+
bool (*ValidateMCOperand)(const MCOperand &MCOp, const MCSubtargetInfo &STI,
153+
unsigned PredicateIndex);
154+
};
155+
107156
} // end namespace llvm
108157

109158
#endif // LLVM_MC_MCINSTPRINTER_H

llvm/lib/MC/MCInstPrinter.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#include "llvm/ADT/ArrayRef.h"
1111
#include "llvm/ADT/StringRef.h"
1212
#include "llvm/MC/MCAsmInfo.h"
13+
#include "llvm/MC/MCInst.h"
1314
#include "llvm/MC/MCInstrInfo.h"
15+
#include "llvm/MC/MCSubtargetInfo.h"
1416
#include "llvm/Support/ErrorHandling.h"
1517
#include "llvm/Support/Format.h"
1618
#include "llvm/Support/raw_ostream.h"
@@ -57,6 +59,94 @@ void MCInstPrinter::printAnnotation(raw_ostream &OS, StringRef Annot) {
5759
}
5860
}
5961

62+
static bool matchAliasCondition(const MCInst &MI, const MCSubtargetInfo *STI,
63+
const MCRegisterInfo &MRI, unsigned &OpIdx,
64+
const AliasMatchingData &M,
65+
const AliasPatternCond &C) {
66+
// Feature tests are special, they don't consume operands.
67+
if (C.Kind == AliasPatternCond::K_Feature)
68+
return STI->getFeatureBits().test(C.Value);
69+
if (C.Kind == AliasPatternCond::K_NegFeature)
70+
return !STI->getFeatureBits().test(C.Value);
71+
72+
// Get and consume an operand.
73+
const MCOperand &Opnd = MI.getOperand(OpIdx);
74+
++OpIdx;
75+
76+
// Check the specific condition for the operand.
77+
switch (C.Kind) {
78+
case AliasPatternCond::K_Imm:
79+
// Operand must be a specific immediate.
80+
return Opnd.isImm() && Opnd.getImm() == int32_t(C.Value);
81+
case AliasPatternCond::K_Reg:
82+
// Operand must be a specific register.
83+
return Opnd.isReg() && Opnd.getReg() == C.Value;
84+
case AliasPatternCond::K_TiedReg:
85+
// Operand must match the register of another operand.
86+
return Opnd.isReg() && Opnd.getReg() == MI.getOperand(C.Value).getReg();
87+
case AliasPatternCond::K_RegClass:
88+
// Operand must be a register in this class. Value is a register class id.
89+
return Opnd.isReg() && MRI.getRegClass(C.Value).contains(Opnd.getReg());
90+
case AliasPatternCond::K_Custom:
91+
// Operand must match some custom criteria.
92+
return M.ValidateMCOperand(Opnd, *STI, C.Value);
93+
case AliasPatternCond::K_Ignore:
94+
// Operand can be anything.
95+
return true;
96+
case AliasPatternCond::K_Feature:
97+
case AliasPatternCond::K_NegFeature:
98+
llvm_unreachable("handled earlier");
99+
}
100+
llvm_unreachable("invalid kind");
101+
}
102+
103+
const char *MCInstPrinter::matchAliasPatterns(const MCInst *MI,
104+
const MCSubtargetInfo *STI,
105+
const AliasMatchingData &M) {
106+
// Binary search by opcode. Return false if there are no aliases for this
107+
// opcode.
108+
auto It = lower_bound(M.OpToPatterns, MI->getOpcode(),
109+
[](const PatternsForOpcode &L, unsigned Opcode) {
110+
return L.Opcode < Opcode;
111+
});
112+
if (It == M.OpToPatterns.end() || It->Opcode != MI->getOpcode())
113+
return nullptr;
114+
115+
// Try all patterns for this opcode.
116+
uint32_t AsmStrOffset = ~0U;
117+
ArrayRef<AliasPattern> Patterns =
118+
M.Patterns.slice(It->PatternStart, It->NumPatterns);
119+
for (const AliasPattern &P : Patterns) {
120+
// Check operand count first.
121+
if (MI->getNumOperands() != P.NumOperands)
122+
return nullptr;
123+
124+
// Test all conditions for this pattern.
125+
ArrayRef<AliasPatternCond> Conds =
126+
M.PatternConds.slice(P.AliasCondStart, P.NumConds);
127+
unsigned OpIdx = 0;
128+
if (llvm::all_of(Conds, [&](const AliasPatternCond &C) {
129+
return matchAliasCondition(*MI, STI, MRI, OpIdx, M, C);
130+
})) {
131+
// If all conditions matched, use this asm string.
132+
AsmStrOffset = P.AsmStrOffset;
133+
break;
134+
}
135+
}
136+
137+
// If no alias matched, don't print an alias.
138+
if (AsmStrOffset == ~0U)
139+
return nullptr;
140+
141+
// Go to offset AsmStrOffset and use the null terminated string there. The
142+
// offset should point to the beginning of an alias string, so it should
143+
// either be zero or be preceded by a null byte.
144+
assert(AsmStrOffset < M.AsmStrings.size() &&
145+
(AsmStrOffset == 0 || M.AsmStrings[AsmStrOffset - 1] == '\0') &&
146+
"bad asm string offset");
147+
return M.AsmStrings.data() + AsmStrOffset;
148+
}
149+
60150
/// Utility functions to make adding mark ups simpler.
61151
StringRef MCInstPrinter::markup(StringRef s) const {
62152
if (getUseMarkup())

0 commit comments

Comments
 (0)