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.
444 lines
14 KiB
C++
444 lines
14 KiB
C++
//===- DAGISelMatcher.cpp - Representation of DAG pattern matcher ---------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DAGISelMatcher.h"
|
|
#include "Common/CodeGenDAGPatterns.h"
|
|
#include "Common/CodeGenInstruction.h"
|
|
#include "Common/CodeGenRegisters.h"
|
|
#include "Common/CodeGenTarget.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
using namespace llvm;
|
|
|
|
void Matcher::anchor() {}
|
|
|
|
void Matcher::dump() const { print(errs()); }
|
|
|
|
void Matcher::print(raw_ostream &OS, indent Indent) const {
|
|
printImpl(OS, Indent);
|
|
if (Next)
|
|
return Next->print(OS, Indent);
|
|
}
|
|
|
|
void Matcher::printOne(raw_ostream &OS) const { printImpl(OS, indent(0)); }
|
|
|
|
/// unlinkNode - Unlink the specified node from this chain. If Other == this,
|
|
/// we unlink the next pointer and return it. Otherwise we unlink Other from
|
|
/// the list and return this.
|
|
Matcher *Matcher::unlinkNode(Matcher *Other) {
|
|
if (this == Other)
|
|
return takeNext();
|
|
|
|
// Scan until we find the predecessor of Other.
|
|
Matcher *Cur = this;
|
|
for (; Cur && Cur->getNext() != Other; Cur = Cur->getNext())
|
|
/*empty*/;
|
|
|
|
if (!Cur)
|
|
return nullptr;
|
|
Cur->takeNext();
|
|
Cur->setNext(Other->takeNext());
|
|
return this;
|
|
}
|
|
|
|
/// canMoveBefore - Return true if this matcher is the same as Other, or if
|
|
/// we can move this matcher past all of the nodes in-between Other and this
|
|
/// node. Other must be equal to or before this.
|
|
bool Matcher::canMoveBefore(const Matcher *Other) const {
|
|
for (;; Other = Other->getNext()) {
|
|
assert(Other && "Other didn't come before 'this'?");
|
|
if (this == Other)
|
|
return true;
|
|
|
|
// We have to be able to move this node across the Other node.
|
|
if (!canMoveBeforeNode(Other))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// canMoveBeforeNode - Return true if it is safe to move the current matcher
|
|
/// across the specified one.
|
|
bool Matcher::canMoveBeforeNode(const Matcher *Other) const {
|
|
// We can move simple predicates before record nodes.
|
|
if (isSimplePredicateNode())
|
|
return Other->isSimplePredicateOrRecordNode();
|
|
|
|
// We can move record nodes across simple predicates.
|
|
if (isSimplePredicateOrRecordNode())
|
|
return isSimplePredicateNode();
|
|
|
|
// We can't move record nodes across each other etc.
|
|
return false;
|
|
}
|
|
|
|
ScopeMatcher::~ScopeMatcher() {
|
|
for (Matcher *C : Children)
|
|
delete C;
|
|
}
|
|
|
|
SwitchOpcodeMatcher::~SwitchOpcodeMatcher() {
|
|
for (auto &C : Cases)
|
|
delete C.second;
|
|
}
|
|
|
|
SwitchTypeMatcher::~SwitchTypeMatcher() {
|
|
for (auto &C : Cases)
|
|
delete C.second;
|
|
}
|
|
|
|
CheckPredicateMatcher::CheckPredicateMatcher(const TreePredicateFn &pred,
|
|
ArrayRef<unsigned> Ops)
|
|
: Matcher(CheckPredicate), Pred(pred.getOrigPatFragRecord()),
|
|
Operands(Ops) {}
|
|
|
|
TreePredicateFn CheckPredicateMatcher::getPredicate() const {
|
|
return TreePredicateFn(Pred);
|
|
}
|
|
|
|
unsigned CheckPredicateMatcher::getNumOperands() const {
|
|
return Operands.size();
|
|
}
|
|
|
|
unsigned CheckPredicateMatcher::getOperandNo(unsigned i) const {
|
|
assert(i < Operands.size());
|
|
return Operands[i];
|
|
}
|
|
|
|
// printImpl methods.
|
|
|
|
void ScopeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "Scope\n";
|
|
for (const Matcher *C : Children) {
|
|
if (!C)
|
|
OS << Indent + 1 << "NULL POINTER\n";
|
|
else
|
|
C->print(OS, Indent + 2);
|
|
}
|
|
}
|
|
|
|
void RecordMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "Record\n";
|
|
}
|
|
|
|
void RecordChildMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "RecordChild: " << ChildNo << '\n';
|
|
}
|
|
|
|
void RecordMemRefMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "RecordMemRef\n";
|
|
}
|
|
|
|
void CaptureGlueInputMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CaptureGlueInput\n";
|
|
}
|
|
|
|
void MoveChildMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "MoveChild " << ChildNo << '\n';
|
|
}
|
|
|
|
void MoveSiblingMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "MoveSibling " << SiblingNo << '\n';
|
|
}
|
|
|
|
void MoveParentMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "MoveParent\n";
|
|
}
|
|
|
|
void CheckSameMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckSame " << MatchNumber << '\n';
|
|
}
|
|
|
|
void CheckChildSameMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckChild" << ChildNo << "Same\n";
|
|
}
|
|
|
|
void CheckPatternPredicateMatcher::printImpl(raw_ostream &OS,
|
|
indent Indent) const {
|
|
OS << Indent << "CheckPatternPredicate " << Predicate << '\n';
|
|
}
|
|
|
|
void CheckPredicateMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckPredicate " << getPredicate().getFnName() << '\n';
|
|
}
|
|
|
|
void CheckOpcodeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckOpcode " << Opcode.getEnumName() << '\n';
|
|
}
|
|
|
|
void SwitchOpcodeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "SwitchOpcode: {\n";
|
|
for (const auto &C : Cases) {
|
|
OS << Indent << "case " << C.first->getEnumName() << ":\n";
|
|
C.second->print(OS, Indent + 2);
|
|
}
|
|
OS << Indent << "}\n";
|
|
}
|
|
|
|
void CheckTypeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckType " << Type << ", ResNo=" << ResNo << '\n';
|
|
}
|
|
|
|
void SwitchTypeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "SwitchType: {\n";
|
|
for (const auto &C : Cases) {
|
|
OS << Indent << "case " << getEnumName(C.first) << ":\n";
|
|
C.second->print(OS, Indent + 2);
|
|
}
|
|
OS << Indent << "}\n";
|
|
}
|
|
|
|
void CheckChildTypeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckChildType " << ChildNo << " " << Type << '\n';
|
|
}
|
|
|
|
void CheckIntegerMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckInteger " << Value << '\n';
|
|
}
|
|
|
|
void CheckChildIntegerMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckChildInteger " << ChildNo << " " << Value << '\n';
|
|
}
|
|
|
|
void CheckCondCodeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckCondCode ISD::" << CondCodeName << '\n';
|
|
}
|
|
|
|
void CheckChild2CondCodeMatcher::printImpl(raw_ostream &OS,
|
|
indent Indent) const {
|
|
OS << Indent << "CheckChild2CondCode ISD::" << CondCodeName << '\n';
|
|
}
|
|
|
|
void CheckValueTypeMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckValueType " << getEnumName(VT) << '\n';
|
|
}
|
|
|
|
void CheckComplexPatMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckComplexPat " << Pattern.getSelectFunc() << '\n';
|
|
}
|
|
|
|
void CheckAndImmMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckAndImm " << Value << '\n';
|
|
}
|
|
|
|
void CheckOrImmMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckOrImm " << Value << '\n';
|
|
}
|
|
|
|
void CheckFoldableChainNodeMatcher::printImpl(raw_ostream &OS,
|
|
indent Indent) const {
|
|
OS << Indent << "CheckFoldableChainNode\n";
|
|
}
|
|
|
|
void CheckImmAllOnesVMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckAllOnesV\n";
|
|
}
|
|
|
|
void CheckImmAllZerosVMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CheckAllZerosV\n";
|
|
}
|
|
|
|
void EmitIntegerMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "EmitInteger " << Val << " VT=" << VT << '\n';
|
|
}
|
|
|
|
void EmitRegisterMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "EmitRegister ";
|
|
if (Reg)
|
|
OS << Reg->getName();
|
|
else
|
|
OS << "zero_reg";
|
|
OS << " VT=" << VT << '\n';
|
|
}
|
|
|
|
void EmitConvertToTargetMatcher::printImpl(raw_ostream &OS,
|
|
indent Indent) const {
|
|
OS << Indent << "EmitConvertToTarget " << Slot << '\n';
|
|
}
|
|
|
|
void EmitMergeInputChainsMatcher::printImpl(raw_ostream &OS,
|
|
indent Indent) const {
|
|
OS << Indent << "EmitMergeInputChains <todo: args>\n";
|
|
}
|
|
|
|
void EmitCopyToRegMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "EmitCopyToReg <todo: args>\n";
|
|
}
|
|
|
|
void EmitNodeXFormMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "EmitNodeXForm " << NodeXForm->getName() << " Slot=" << Slot
|
|
<< '\n';
|
|
}
|
|
|
|
void EmitNodeMatcherCommon::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent;
|
|
OS << (isa<MorphNodeToMatcher>(this) ? "MorphNodeTo: " : "EmitNode: ")
|
|
<< CGI.Namespace << "::" << CGI.getName() << ": <todo flags> ";
|
|
|
|
for (const ValueTypeByHwMode &VT : VTs)
|
|
OS << ' ' << VT;
|
|
OS << '(';
|
|
for (unsigned Operand : Operands)
|
|
OS << Operand << ' ';
|
|
OS << ")\n";
|
|
}
|
|
|
|
void CompleteMatchMatcher::printImpl(raw_ostream &OS, indent Indent) const {
|
|
OS << Indent << "CompleteMatch <todo args>\n";
|
|
OS << Indent << "Src = " << Pattern.getSrcPattern() << "\n";
|
|
OS << Indent << "Dst = " << Pattern.getDstPattern() << "\n";
|
|
}
|
|
|
|
bool CheckOpcodeMatcher::isEqualImpl(const Matcher *M) const {
|
|
// Note: pointer equality isn't enough here, we have to check the enum names
|
|
// to ensure that the nodes are for the same opcode.
|
|
return cast<CheckOpcodeMatcher>(M)->Opcode.getEnumName() ==
|
|
Opcode.getEnumName();
|
|
}
|
|
|
|
bool EmitNodeMatcherCommon::isEqualImpl(const Matcher *m) const {
|
|
const EmitNodeMatcherCommon *M = cast<EmitNodeMatcherCommon>(m);
|
|
return &M->CGI == &CGI && M->VTs == VTs && M->Operands == Operands &&
|
|
M->HasChain == HasChain && M->HasInGlue == HasInGlue &&
|
|
M->HasOutGlue == HasOutGlue && M->HasMemRefs == HasMemRefs &&
|
|
M->NumFixedArityOperands == NumFixedArityOperands;
|
|
}
|
|
|
|
void EmitNodeMatcher::anchor() {}
|
|
|
|
void MorphNodeToMatcher::anchor() {}
|
|
|
|
// isContradictoryImpl Implementations.
|
|
|
|
static bool TypesAreContradictory(const ValueTypeByHwMode &VT1,
|
|
const ValueTypeByHwMode &VT2) {
|
|
// If the two types are the same, then they are the same, so they don't
|
|
// contradict.
|
|
if (VT1 == VT2)
|
|
return false;
|
|
|
|
if (!VT1.isSimple() || !VT2.isSimple())
|
|
return false;
|
|
|
|
MVT T1 = VT1.getSimple();
|
|
MVT T2 = VT2.getSimple();
|
|
|
|
if (T1 == MVT::pAny)
|
|
return TypesAreContradictory(MVT(MVT::iPTR), T2) &&
|
|
TypesAreContradictory(MVT(MVT::cPTR), T2);
|
|
|
|
if (T2 == MVT::pAny)
|
|
return TypesAreContradictory(T1, MVT(MVT::iPTR)) &&
|
|
TypesAreContradictory(T1, MVT(MVT::cPTR));
|
|
|
|
// If either type is about iPtr, then they don't conflict unless the other
|
|
// one is not a scalar integer type.
|
|
if (T1 == MVT::iPTR)
|
|
return !T2.isInteger() || T2.isVector();
|
|
|
|
if (T2 == MVT::iPTR)
|
|
return !T1.isInteger() || T1.isVector();
|
|
|
|
if (T1 == MVT::cPTR)
|
|
return !T2.isCheriCapability() || T2.isVector();
|
|
|
|
if (T2 == MVT::cPTR)
|
|
return !T1.isCheriCapability() || T1.isVector();
|
|
|
|
// Otherwise, they are two different non-iPTR/cPTR types, they conflict.
|
|
return true;
|
|
}
|
|
|
|
bool CheckOpcodeMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const CheckOpcodeMatcher *COM = dyn_cast<CheckOpcodeMatcher>(M)) {
|
|
// One node can't have two different opcodes!
|
|
// Note: pointer equality isn't enough here, we have to check the enum names
|
|
// to ensure that the nodes are for the same opcode.
|
|
return COM->getOpcode().getEnumName() != getOpcode().getEnumName();
|
|
}
|
|
|
|
// If the node has a known type, and if the type we're checking for is
|
|
// different, then we know they contradict. For example, a check for
|
|
// ISD::STORE will never be true at the same time a check for Type i32 is.
|
|
if (const CheckTypeMatcher *CT = dyn_cast<CheckTypeMatcher>(M)) {
|
|
// If checking for a result the opcode doesn't have, it can't match.
|
|
if (CT->getResNo() >= getOpcode().getNumResults())
|
|
return true;
|
|
|
|
MVT NodeType = getOpcode().getKnownType(CT->getResNo());
|
|
if (NodeType != MVT::Other)
|
|
return TypesAreContradictory(NodeType, CT->getType());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CheckTypeMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const CheckTypeMatcher *CT = dyn_cast<CheckTypeMatcher>(M))
|
|
return TypesAreContradictory(getType(), CT->getType());
|
|
return false;
|
|
}
|
|
|
|
bool CheckChildTypeMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const CheckChildTypeMatcher *CC = dyn_cast<CheckChildTypeMatcher>(M)) {
|
|
// If the two checks are about different nodes, we don't know if they
|
|
// conflict!
|
|
if (CC->getChildNo() != getChildNo())
|
|
return false;
|
|
|
|
return TypesAreContradictory(getType(), CC->getType());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CheckIntegerMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const CheckIntegerMatcher *CIM = dyn_cast<CheckIntegerMatcher>(M))
|
|
return CIM->getValue() != getValue();
|
|
return false;
|
|
}
|
|
|
|
bool CheckChildIntegerMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const CheckChildIntegerMatcher *CCIM =
|
|
dyn_cast<CheckChildIntegerMatcher>(M)) {
|
|
// If the two checks are about different nodes, we don't know if they
|
|
// conflict!
|
|
if (CCIM->getChildNo() != getChildNo())
|
|
return false;
|
|
|
|
return CCIM->getValue() != getValue();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CheckValueTypeMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const CheckValueTypeMatcher *CVT = dyn_cast<CheckValueTypeMatcher>(M))
|
|
return CVT->getVT() != getVT();
|
|
return false;
|
|
}
|
|
|
|
bool CheckImmAllOnesVMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
// AllZeros is contradictory.
|
|
return isa<CheckImmAllZerosVMatcher>(M);
|
|
}
|
|
|
|
bool CheckImmAllZerosVMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
// AllOnes is contradictory.
|
|
return isa<CheckImmAllOnesVMatcher>(M);
|
|
}
|
|
|
|
bool CheckCondCodeMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const auto *CCCM = dyn_cast<CheckCondCodeMatcher>(M))
|
|
return CCCM->getCondCodeName() != getCondCodeName();
|
|
return false;
|
|
}
|
|
|
|
bool CheckChild2CondCodeMatcher::isContradictoryImpl(const Matcher *M) const {
|
|
if (const auto *CCCCM = dyn_cast<CheckChild2CondCodeMatcher>(M))
|
|
return CCCCM->getCondCodeName() != getCondCodeName();
|
|
return false;
|
|
}
|