Skip to content

[TableGen] Add PreferSmallerInstructions for Targets. #83587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions llvm/include/llvm/Target/Target.td
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,20 @@ class AsmParser {
// method shall be called for all operands as opposed to only those
// that have their own specified custom parsers.
bit CallCustomParserForAllOperands = false;

// PreferSmallerInstructions - Should the assembly matcher prefer the smaller
// instructions.
//
// This is useful for the ARM instructions set where smaller encodings must
// be preferentially selected.
//
// The preference order is:
// Instrution size (if this option is enabled, smallest first)
// Number of Operands (least first),
// Operand Classes (lexicographically by operand),
// (Optional) Instruction id (see AsmMatcherEmitter.cpp for details),
// Number of required features (least first)
bit PreferSmallerInstructions = false;
}
def DefaultAsmParser : AsmParser;

Expand Down
45 changes: 34 additions & 11 deletions llvm/utils/TableGen/AsmMatcherEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ class AsmVariantInfo {
int AsmVariantNo;
};

bool getPreferSmallerInstructions(CodeGenTarget const &Target) {
return Target.getAsmParser()->getValueAsBit("PreferSmallerInstructions");
}

/// MatchableInfo - Helper class for storing the necessary information for an
/// instruction or alias which is capable of being matched.
struct MatchableInfo {
Expand Down Expand Up @@ -502,6 +506,9 @@ struct MatchableInfo {
/// matchable came from.
Record *const TheDef;

// ResInstSize - The size of the resulting instruction for this matchable.
unsigned ResInstSize;

/// DefRec - This is the definition that it came from.
PointerUnion<const CodeGenInstruction *, const CodeGenInstAlias *> DefRec;

Expand Down Expand Up @@ -543,10 +550,12 @@ struct MatchableInfo {

MatchableInfo(const CodeGenInstruction &CGI)
: AsmVariantID(0), AsmString(CGI.AsmString), TheDef(CGI.TheDef),
DefRec(&CGI), UseInstAsmMatchConverter(true) {}
ResInstSize(TheDef->getValueAsInt("Size")), DefRec(&CGI),
UseInstAsmMatchConverter(true) {}

MatchableInfo(std::unique_ptr<const CodeGenInstAlias> Alias)
: AsmVariantID(0), AsmString(Alias->AsmString), TheDef(Alias->TheDef),
ResInstSize(Alias->ResultInst->TheDef->getValueAsInt("Size")),
DefRec(Alias.release()), UseInstAsmMatchConverter(TheDef->getValueAsBit(
"UseInstAsmMatchConverter")) {}

Expand All @@ -555,9 +564,9 @@ struct MatchableInfo {
// where it was copied while being in an owning state.
MatchableInfo(const MatchableInfo &RHS)
: AsmVariantID(RHS.AsmVariantID), AsmString(RHS.AsmString),
TheDef(RHS.TheDef), DefRec(RHS.DefRec), ResOperands(RHS.ResOperands),
Mnemonic(RHS.Mnemonic), AsmOperands(RHS.AsmOperands),
RequiredFeatures(RHS.RequiredFeatures),
TheDef(RHS.TheDef), ResInstSize(RHS.ResInstSize), DefRec(RHS.DefRec),
ResOperands(RHS.ResOperands), Mnemonic(RHS.Mnemonic),
AsmOperands(RHS.AsmOperands), RequiredFeatures(RHS.RequiredFeatures),
ConversionFnKind(RHS.ConversionFnKind),
HasDeprecation(RHS.HasDeprecation),
UseInstAsmMatchConverter(RHS.UseInstAsmMatchConverter) {
Expand Down Expand Up @@ -608,12 +617,18 @@ struct MatchableInfo {
void buildInstructionResultOperands();
void buildAliasResultOperands(bool AliasConstraintsAreChecked);

/// operator< - Compare two matchables.
bool operator<(const MatchableInfo &RHS) const {
/// shouldBeMatchedBefore - Compare two matchables for ordering.
bool shouldBeMatchedBefore(const MatchableInfo &RHS,
bool PreferSmallerInstructions) const {
// The primary comparator is the instruction mnemonic.
if (int Cmp = Mnemonic.compare_insensitive(RHS.Mnemonic))
return Cmp == -1;

// (Optionally) Order by the resultant instuctions size.
// eg. for ARM thumb instructions smaller encodings should be preferred.
if (PreferSmallerInstructions && ResInstSize != RHS.ResInstSize)
return ResInstSize < RHS.ResInstSize;

if (AsmOperands.size() != RHS.AsmOperands.size())
return AsmOperands.size() < RHS.AsmOperands.size();

Expand Down Expand Up @@ -652,7 +667,8 @@ struct MatchableInfo {
/// couldMatchAmbiguouslyWith - Check whether this matchable could
/// ambiguously match the same set of operands as \p RHS (without being a
/// strictly superior match).
bool couldMatchAmbiguouslyWith(const MatchableInfo &RHS) const {
bool couldMatchAmbiguouslyWith(const MatchableInfo &RHS,
bool PreferSmallerInstructions) const {
// The primary comparator is the instruction mnemonic.
if (Mnemonic != RHS.Mnemonic)
return false;
Expand All @@ -661,6 +677,10 @@ struct MatchableInfo {
if (AsmVariantID != RHS.AsmVariantID)
return false;

// The size of instruction is unambiguous.
if (PreferSmallerInstructions && ResInstSize != RHS.ResInstSize)
return false;

// The number of operands is unambiguous.
if (AsmOperands.size() != RHS.AsmOperands.size())
return false;
Expand Down Expand Up @@ -3221,20 +3241,23 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
AsmMatcherInfo Info(AsmParser, Target, Records);
Info.buildInfo();

bool PreferSmallerInstructions = getPreferSmallerInstructions(Target);
// Sort the instruction table using the partial order on classes. We use
// stable_sort to ensure that ambiguous instructions are still
// deterministically ordered.
llvm::stable_sort(
Info.Matchables,
[](const std::unique_ptr<MatchableInfo> &a,
const std::unique_ptr<MatchableInfo> &b) { return *a < *b; });
[PreferSmallerInstructions](const std::unique_ptr<MatchableInfo> &A,
const std::unique_ptr<MatchableInfo> &B) {
return A->shouldBeMatchedBefore(*B, PreferSmallerInstructions);
});

#ifdef EXPENSIVE_CHECKS
// Verify that the table is sorted and operator < works transitively.
for (auto I = Info.Matchables.begin(), E = Info.Matchables.end(); I != E;
++I) {
for (auto J = I; J != E; ++J) {
assert(!(**J < **I));
assert(!(*J)->shouldBeMatchedBefore(**I, PreferSmallerInstructions));
}
}
#endif
Expand All @@ -3253,7 +3276,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
const MatchableInfo &A = **I;
const MatchableInfo &B = **J;

if (A.couldMatchAmbiguouslyWith(B)) {
if (A.couldMatchAmbiguouslyWith(B, PreferSmallerInstructions)) {
errs() << "warning: ambiguous matchables:\n";
A.dump();
errs() << "\nis incomparable with:\n";
Expand Down