llvm-project/llvm/utils/TableGen/DAGISelEmitter.cpp
Craig Topper 08de4fd0d4
[SelectionDAG] Move HwMode expansion from tablegen to SelectionISel. (#174471)
The way HwMode is currently implemented, tablegen duplicates each
pattern that is dependent on hardware mode. The HwMode predicate is
added as a pattern predicate on the duplicated pattern.
    
RISC-V uses HwMode on the GPR register class which means almost every
isel pattern is affected by HwMode. This results in the isel table
being nearly twice the size it would be if we only had a single GPR
size.

This patch proposes to do the expansion at instruction selection time
instead. To accomplish this new opcodes like OPC_CheckTypeByHwMode
are added to the isel table. The unique combinations of types and HwMode
are converted to an index that is the payload for the new opcodes.
TableGen emits a new virtual function getValueTypeByHwMode that uses
this index and the current HwMode to look up the type.

This reduces the size of the isel table on RISC-V from ~2.38 million
bytes to ~1.38 million bytes.

I did not add an OPC_SwitchTypeByHwMode opcode yet. If the VT requires a
hardware mode, we emit an OPC_Scope+OPC_CheckTypeByHwMode instead. I
expect adding an OPC_SwitchTypeByHwMode could further reduce the table
size. I will investigate this as a follow up.
    
Many of the matcher classes in tablegen now use ValueTypeByHwMode
insteadof MVT. This may have an impact on the memory usage and runtime of
tablegen. We can mitigate some of this by splitting the matchers into MVT and
ValueTypeByHwMode versions. We can also explore alternate data
structures for ValueTypeByHwMode instead of a std::map. Maybe a sorted vector.

A similar change can be made to GlobalISel as a follow up.
2026-01-15 09:35:02 -08:00

220 lines
7.6 KiB
C++

//===- DAGISelEmitter.cpp - Generate an instruction selector --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend emits a DAG instruction selector.
//
//===----------------------------------------------------------------------===//
#include "Common/CodeGenDAGPatterns.h"
#include "Common/CodeGenInstruction.h"
#include "Common/CodeGenTarget.h"
#include "DAGISelMatcher.h"
#include "llvm/Support/Debug.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TGTimer.h"
#include "llvm/TableGen/TableGenBackend.h"
using namespace llvm;
#define DEBUG_TYPE "dag-isel-emitter"
namespace {
/// DAGISelEmitter - The top-level class which coordinates construction
/// and emission of the instruction selector.
class DAGISelEmitter {
const RecordKeeper &Records; // Just so we can get at the timing functions.
const CodeGenDAGPatterns CGP;
public:
explicit DAGISelEmitter(const RecordKeeper &R) : Records(R), CGP(R, false) {}
void run(raw_ostream &OS);
};
} // End anonymous namespace
//===----------------------------------------------------------------------===//
// DAGISelEmitter Helper methods
//
/// Compute the number of instructions for this pattern.
/// This is a temporary hack. We should really include the instruction
/// latencies in this calculation.
static unsigned getResultPatternCost(const TreePatternNode &P,
const CodeGenDAGPatterns &CGP) {
if (P.isLeaf())
return 0;
unsigned Cost = 0;
const Record *Op = P.getOperator();
if (Op->isSubClassOf("Instruction")) {
Cost++;
const CodeGenInstruction &II = CGP.getTargetInfo().getInstruction(Op);
if (II.usesCustomInserter)
Cost += 10;
}
for (const TreePatternNode &Child : P.children())
Cost += getResultPatternCost(Child, CGP);
return Cost;
}
/// getResultPatternCodeSize - Compute the code size of instructions for this
/// pattern.
static unsigned getResultPatternSize(const TreePatternNode &P,
const CodeGenDAGPatterns &CGP) {
if (P.isLeaf())
return 0;
unsigned Cost = 0;
const Record *Op = P.getOperator();
if (Op->isSubClassOf("Instruction")) {
Cost += Op->getValueAsInt("CodeSize");
}
for (const TreePatternNode &Child : P.children())
Cost += getResultPatternSize(Child, CGP);
return Cost;
}
namespace {
// PatternSortingPredicate - return true if we prefer to match LHS before RHS.
// In particular, we want to match maximal patterns first and lowest cost within
// a particular complexity first.
struct PatternSortingPredicate {
PatternSortingPredicate(const CodeGenDAGPatterns &cgp) : CGP(cgp) {}
const CodeGenDAGPatterns &CGP;
bool operator()(const PatternToMatch *LHS, const PatternToMatch *RHS) {
const TreePatternNode &LT = LHS->getSrcPattern();
const TreePatternNode &RT = RHS->getSrcPattern();
bool LHSIsVector = false;
bool RHSIsVector = false;
bool LHSIsFP = false;
bool RHSIsFP = false;
if (LT.getNumTypes() != 0) {
for (auto [_, VT] : LT.getType(0)) {
LHSIsVector |= VT.isVector();
LHSIsFP |= VT.isFloatingPoint();
}
}
if (RT.getNumTypes() != 0) {
for (auto [_, VT] : RT.getType(0)) {
RHSIsVector |= VT.isVector();
RHSIsFP |= VT.isFloatingPoint();
}
}
if (LHSIsVector != RHSIsVector)
return RHSIsVector;
if (LHSIsFP != RHSIsFP)
return RHSIsFP;
// Otherwise, if the patterns might both match, sort based on complexity,
// which means that we prefer to match patterns that cover more nodes in the
// input over nodes that cover fewer.
int LHSSize = LHS->getPatternComplexity(CGP);
int RHSSize = RHS->getPatternComplexity(CGP);
if (LHSSize > RHSSize)
return true; // LHS -> bigger -> less cost
if (LHSSize < RHSSize)
return false;
// If the patterns have equal complexity, compare generated instruction cost
unsigned LHSCost = getResultPatternCost(LHS->getDstPattern(), CGP);
unsigned RHSCost = getResultPatternCost(RHS->getDstPattern(), CGP);
if (LHSCost < RHSCost)
return true;
if (LHSCost > RHSCost)
return false;
unsigned LHSPatSize = getResultPatternSize(LHS->getDstPattern(), CGP);
unsigned RHSPatSize = getResultPatternSize(RHS->getDstPattern(), CGP);
if (LHSPatSize < RHSPatSize)
return true;
if (LHSPatSize > RHSPatSize)
return false;
// Sort based on the UID of the pattern, to reflect source order.
// Note that this is not guaranteed to be unique, since a single source
// pattern may have been resolved into multiple match patterns due to
// alternative fragments. To ensure deterministic output, always use
// std::stable_sort with this predicate.
return LHS->getID() < RHS->getID();
}
};
} // End anonymous namespace
void DAGISelEmitter::run(raw_ostream &OS) {
TGTimer &Timer = Records.getTimer();
Timer.startTimer("Parse patterns");
emitSourceFileHeader("DAG Instruction Selector for the " +
CGP.getTargetInfo().getName().str() + " target",
OS);
OS << "// *** NOTE: This file is #included into the middle of the target\n"
<< "// *** instruction selector class. These functions are really "
<< "methods.\n\n";
OS << "// If GET_DAGISEL_DECL is #defined with any value, only function\n"
"// declarations will be included when this file is included.\n"
"// If GET_DAGISEL_BODY is #defined, its value should be the name of\n"
"// the instruction selector class. Function bodies will be emitted\n"
"// and each function's name will be qualified with the name of the\n"
"// class.\n"
"//\n"
"// When neither of the GET_DAGISEL* macros is defined, the functions\n"
"// are emitted inline.\n\n";
LLVM_DEBUG(errs() << "\n\nALL PATTERNS TO MATCH:\n\n";
for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(),
E = CGP.ptm_end();
I != E; ++I) {
errs() << "PATTERN: ";
I->getSrcPattern().dump();
errs() << "\nRESULT: ";
I->getDstPattern().dump();
errs() << "\n";
});
// Add all the patterns to a temporary list so we can sort them.
Timer.startTimer("Sort patterns");
std::vector<const PatternToMatch *> Patterns;
for (const PatternToMatch &PTM : CGP.ptms())
Patterns.push_back(&PTM);
// We want to process the matches in order of minimal cost. Sort the patterns
// so the least cost one is at the start.
llvm::stable_sort(Patterns, PatternSortingPredicate(CGP));
// Convert each variant of each pattern into a Matcher.
Timer.startTimer("Convert to matchers");
SmallVector<Matcher *, 0> PatternMatchers;
for (const PatternToMatch *PTM : Patterns) {
for (unsigned Variant = 0;; ++Variant) {
if (Matcher *M = ConvertPatternToMatcher(*PTM, Variant, CGP))
PatternMatchers.push_back(M);
else
break;
}
}
std::unique_ptr<Matcher> TheMatcher =
std::make_unique<ScopeMatcher>(std::move(PatternMatchers));
Timer.startTimer("Optimize matchers");
OptimizeMatcher(TheMatcher, CGP);
// Matcher->dump();
Timer.startTimer("Emit matcher table");
EmitMatcherTable(TheMatcher.get(), CGP, OS);
}
static TableGen::Emitter::OptClass<DAGISelEmitter>
X("gen-dag-isel", "Generate a DAG instruction selector");