GlobalISel: extend legalizer interface to handle multiple types.

Instructions like G_ICMP have multiple types that may need to be legalized (the
boolean output and nearly arbitrary inputs in this case). So the legalizer must
be capable of deciding what to do for each of them separately.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@279554 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Tim Northover 2016-08-23 19:30:42 +00:00
parent 3891c455e2
commit 585ed95521
6 changed files with 170 additions and 83 deletions

View File

@ -60,20 +60,22 @@ public:
/// Legalize an instruction by reducing the width of the underlying scalar
/// type.
LegalizeResult narrowScalar(MachineInstr &MI, LLT NarrowTy);
LegalizeResult narrowScalar(MachineInstr &MI, unsigned TypeIdx, LLT NarrowTy);
/// Legalize an instruction by performing the operation on a wider scalar type
/// (for example a 16-bit addition can be safely performed at 32-bits
/// precision, ignoring the unused bits).
LegalizeResult widenScalar(MachineInstr &MI, LLT WideTy);
LegalizeResult widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
/// Legalize a vector instruction by splitting into multiple components, each
/// acting on the same scalar type as the original but with fewer elements.
LegalizeResult fewerElementsVector(MachineInstr &MI, LLT NarrowTy);
LegalizeResult fewerElementsVector(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy);
/// Legalize a vector instruction by increasing the number of vector elements
/// involved and ignoring the added elements later.
LegalizeResult moreElementsVector(MachineInstr &MI, LLT WideTy);
LegalizeResult moreElementsVector(MachineInstr &MI, unsigned TypeIdx,
LLT WideTy);
private:

View File

@ -27,6 +27,38 @@ class MachineInstr;
class Type;
class VectorType;
/// Legalization is decided based on an instruction's opcode, which type slot
/// we're considering, and what the existing type is. These aspects are gathered
/// together for convenience in the InstrAspect class.
struct InstrAspect {
unsigned Opcode;
unsigned Idx;
LLT Type;
InstrAspect(unsigned Opcode, LLT Type) : Opcode(Opcode), Idx(0), Type(Type) {}
InstrAspect(unsigned Opcode, unsigned Idx, LLT Type)
: Opcode(Opcode), Idx(Idx), Type(Type) {}
bool operator==(const InstrAspect &RHS) const {
return Opcode == RHS.Opcode && Idx == RHS.Idx && Type == RHS.Type;
}
};
template <> struct DenseMapInfo<InstrAspect> {
static inline InstrAspect getEmptyKey() { return {-1u, 0, LLT{}}; }
static inline InstrAspect getTombstoneKey() { return {-2u, 0, LLT{}}; }
static unsigned getHashValue(const InstrAspect &Val) {
return DenseMapInfo<std::pair<uint64_t, LLT>>::getHashValue(
{(uint64_t)Val.Opcode << 32 | Val.Idx, Val.Type});
}
static bool isEqual(const InstrAspect &LHS, const InstrAspect &RHS) {
return LHS == RHS;
}
};
class MachineLegalizer {
public:
enum LegalizeAction : std::uint8_t {
@ -78,9 +110,9 @@ public:
/// More friendly way to set an action for common types that have an LLT
/// representation.
void setAction(unsigned Opcode, LLT Ty, LegalizeAction Action) {
void setAction(const InstrAspect &Aspect, LegalizeAction Action) {
TablesInitialized = false;
Actions[std::make_pair(Opcode, Ty)] = Action;
Actions[Aspect] = Action;
}
/// If an operation on a given vector type (say <M x iN>) isn't explicitly
@ -96,23 +128,32 @@ public:
/// Determine what action should be taken to legalize the given generic
/// instruction and type. Requires computeTables to have been called.
/// instruction opcode, type-index and type. Requires computeTables to have
/// been called.
///
/// \returns a pair consisting of the kind of legalization that should be
/// performed and the destination type.
std::pair<LegalizeAction, LLT> getAction(unsigned Opcode, LLT) const;
std::pair<LegalizeAction, LLT> getAction(const MachineInstr &MI) const;
std::pair<LegalizeAction, LLT> getAction(const InstrAspect &Aspect) const;
/// Determine what action should be taken to legalize the given generic instruction.
///
/// \returns a tuple consisting of the LegalizeAction that should be
/// performed, the type-index it should be performed on and the destination
/// type.
std::tuple<LegalizeAction, unsigned, LLT>
getAction(const MachineInstr &MI) const;
/// Iterate the given function (typically something like doubling the width)
/// on Ty until we find a legal type for this operation.
LLT findLegalType(unsigned Opcode, LLT Ty,
std::function<LLT(LLT)> NextType) const {
LLT findLegalType(const InstrAspect &Aspect,
std::function<LLT(LLT)> NextType) const {
LegalizeAction Action;
LLT Ty = Aspect.Type;
do {
Ty = NextType(Ty);
auto ActionIt = Actions.find(std::make_pair(Opcode, Ty));
auto ActionIt = Actions.find({Aspect.Opcode, Aspect.Idx, Ty});
if (ActionIt == Actions.end())
Action = DefaultActions.find(Opcode)->second;
Action = DefaultActions.find(Aspect.Opcode)->second;
else
Action = ActionIt->second;
} while(Action != Legal);
@ -121,26 +162,28 @@ public:
/// Find what type it's actually OK to perform the given operation on, given
/// the general approach we've decided to take.
LLT findLegalType(unsigned Opcode, LLT Ty, LegalizeAction Action) const;
LLT findLegalType(const InstrAspect &Aspect, LegalizeAction Action) const;
std::pair<LegalizeAction, LLT> findLegalAction(unsigned Opcode, LLT Ty,
std::pair<LegalizeAction, LLT> findLegalAction(const InstrAspect &Aspect,
LegalizeAction Action) const {
return std::make_pair(Action, findLegalType(Opcode, Ty, Action));
return std::make_pair(Action, findLegalType(Aspect, Action));
}
bool isLegal(const MachineInstr &MI) const;
private:
typedef DenseMap<std::pair<unsigned, LLT>, LegalizeAction> ActionMap;
typedef DenseMap<InstrAspect, LegalizeAction> ActionMap;
typedef DenseMap<std::pair<unsigned, LLT>, LegalizeAction> SIVActionMap;
ActionMap Actions;
ActionMap ScalarInVectorActions;
SIVActionMap ScalarInVectorActions;
DenseMap<std::pair<unsigned, LLT>, uint16_t> MaxLegalVectorElts;
DenseMap<unsigned, LegalizeAction> DefaultActions;
bool TablesInitialized;
};
} // End namespace llvm.
#endif

View File

@ -34,15 +34,15 @@ MachineLegalizeHelper::MachineLegalizeHelper(MachineFunction &MF)
MachineLegalizeHelper::LegalizeResult MachineLegalizeHelper::legalizeInstr(
MachineInstr &MI, const MachineLegalizer &Legalizer) {
auto Action = Legalizer.getAction(MI);
switch (Action.first) {
switch (std::get<0>(Action)) {
case MachineLegalizer::Legal:
return AlreadyLegal;
case MachineLegalizer::NarrowScalar:
return narrowScalar(MI, Action.second);
return narrowScalar(MI, std::get<1>(Action), std::get<2>(Action));
case MachineLegalizer::WidenScalar:
return widenScalar(MI, Action.second);
return widenScalar(MI, std::get<1>(Action), std::get<2>(Action));
case MachineLegalizer::FewerElements:
return fewerElementsVector(MI, Action.second);
return fewerElementsVector(MI, std::get<1>(Action), std::get<2>(Action));
default:
return UnableToLegalize;
}
@ -63,7 +63,9 @@ void MachineLegalizeHelper::extractParts(unsigned Reg, LLT Ty, int NumParts,
}
MachineLegalizeHelper::LegalizeResult
MachineLegalizeHelper::narrowScalar(MachineInstr &MI, LLT NarrowTy) {
MachineLegalizeHelper::narrowScalar(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy) {
assert(TypeIdx == 0 && "don't know how to handle secondary types yet");
switch (MI.getOpcode()) {
default:
return UnableToLegalize;
@ -103,7 +105,10 @@ MachineLegalizeHelper::narrowScalar(MachineInstr &MI, LLT NarrowTy) {
}
MachineLegalizeHelper::LegalizeResult
MachineLegalizeHelper::widenScalar(MachineInstr &MI, LLT WideTy) {
MachineLegalizeHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx,
LLT WideTy) {
assert(TypeIdx == 0 && "don't know how to handle secondary types yet");
unsigned WideSize = WideTy.getSizeInBits();
MIRBuilder.setInstr(MI);
@ -172,7 +177,9 @@ MachineLegalizeHelper::widenScalar(MachineInstr &MI, LLT WideTy) {
}
MachineLegalizeHelper::LegalizeResult
MachineLegalizeHelper::fewerElementsVector(MachineInstr &MI, LLT NarrowTy) {
MachineLegalizeHelper::fewerElementsVector(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy) {
assert(TypeIdx == 0 && "don't know how to handle secondary types yet");
switch (MI.getOpcode()) {
default:
return UnableToLegalize;

View File

@ -35,12 +35,12 @@ MachineLegalizer::MachineLegalizer() : TablesInitialized(false) {
void MachineLegalizer::computeTables() {
for (auto &Op : Actions) {
LLT Ty = Op.first.second;
LLT Ty = Op.first.Type;
if (!Ty.isVector())
continue;
auto &Entry =
MaxLegalVectorElts[std::make_pair(Op.first.first, Ty.getElementType())];
auto &Entry = MaxLegalVectorElts[std::make_pair(Op.first.Opcode,
Ty.getElementType())];
Entry = std::max(Entry, Ty.getNumElements());
}
@ -52,27 +52,30 @@ void MachineLegalizer::computeTables() {
// we have any hope of doing well with something like <13 x i3>. Even the common
// cases should do better than what we have now.
std::pair<MachineLegalizer::LegalizeAction, LLT>
MachineLegalizer::getAction(unsigned Opcode, LLT Ty) const {
MachineLegalizer::getAction(const InstrAspect &Aspect) const {
assert(TablesInitialized && "backend forgot to call computeTables");
// These *have* to be implemented for now, they're the fundamental basis of
// how everything else is transformed.
// FIXME: the long-term plan calls for expansion in terms of load/store (if
// they're not legal).
if (Opcode == TargetOpcode::G_SEQUENCE || Opcode == TargetOpcode::G_EXTRACT)
return std::make_pair(Legal, Ty);
if (Aspect.Opcode == TargetOpcode::G_SEQUENCE ||
Aspect.Opcode == TargetOpcode::G_EXTRACT)
return std::make_pair(Legal, Aspect.Type);
auto ActionIt = Actions.find(std::make_pair(Opcode, Ty));
auto ActionIt = Actions.find(Aspect);
if (ActionIt != Actions.end())
return findLegalAction(Opcode, Ty, ActionIt->second);
return findLegalAction(Aspect, ActionIt->second);
unsigned Opcode = Aspect.Opcode;
LLT Ty = Aspect.Type;
if (!Ty.isVector()) {
auto DefaultAction = DefaultActions.find(Opcode);
auto DefaultAction = DefaultActions.find(Aspect.Opcode);
if (DefaultAction != DefaultActions.end() && DefaultAction->second == Legal)
return std::make_pair(Legal, Ty);
assert(DefaultAction->second == NarrowScalar && "unexpected default");
return findLegalAction(Opcode, Ty, NarrowScalar);
return findLegalAction(Aspect, NarrowScalar);
}
LLT EltTy = Ty.getElementType();
@ -81,13 +84,13 @@ MachineLegalizer::getAction(unsigned Opcode, LLT Ty) const {
auto ScalarAction = ScalarInVectorActions.find(std::make_pair(Opcode, EltTy));
if (ScalarAction != ScalarInVectorActions.end() &&
ScalarAction->second != Legal)
return findLegalAction(Opcode, EltTy, ScalarAction->second);
return findLegalAction(Aspect, ScalarAction->second);
// The element type is legal in principle, but the number of elements is
// wrong.
auto MaxLegalElts = MaxLegalVectorElts.lookup(std::make_pair(Opcode, EltTy));
if (MaxLegalElts > NumElts)
return findLegalAction(Opcode, Ty, MoreElements);
return findLegalAction(Aspect, MoreElements);
if (MaxLegalElts == 0) {
// Scalarize if there's no legal vector type, which is just a special case
@ -95,41 +98,46 @@ MachineLegalizer::getAction(unsigned Opcode, LLT Ty) const {
return std::make_pair(FewerElements, EltTy);
}
return findLegalAction(Opcode, Ty, FewerElements);
return findLegalAction(Aspect, FewerElements);
}
std::pair<MachineLegalizer::LegalizeAction, LLT>
std::tuple<MachineLegalizer::LegalizeAction, unsigned, LLT>
MachineLegalizer::getAction(const MachineInstr &MI) const {
return getAction(MI.getOpcode(), MI.getType());
for (unsigned i = 0; i < MI.getNumTypes(); ++i) {
auto Action = getAction({MI.getOpcode(), i, MI.getType(i)});
if (Action.first != Legal)
return {Action.first, i, Action.second};
}
return {Legal, 0, LLT{}};
}
bool MachineLegalizer::isLegal(const MachineInstr &MI) const {
return getAction(MI).first == Legal;
return std::get<0>(getAction(MI)) == Legal;
}
LLT MachineLegalizer::findLegalType(unsigned Opcode, LLT Ty,
LLT MachineLegalizer::findLegalType(const InstrAspect &Aspect,
LegalizeAction Action) const {
switch(Action) {
default:
llvm_unreachable("Cannot find legal type");
case Legal:
return Ty;
return Aspect.Type;
case NarrowScalar: {
return findLegalType(Opcode, Ty,
return findLegalType(Aspect,
[&](LLT Ty) -> LLT { return Ty.halfScalarSize(); });
}
case WidenScalar: {
return findLegalType(Opcode, Ty, [&](LLT Ty) -> LLT {
return findLegalType(Aspect, [&](LLT Ty) -> LLT {
return Ty.getSizeInBits() < 8 ? LLT::scalar(8) : Ty.doubleScalarSize();
});
}
case FewerElements: {
return findLegalType(Opcode, Ty,
return findLegalType(Aspect,
[&](LLT Ty) -> LLT { return Ty.halfElements(); });
}
case MoreElements: {
return findLegalType(
Opcode, Ty, [&](LLT Ty) -> LLT { return Ty.doubleElements(); });
return findLegalType(Aspect,
[&](LLT Ty) -> LLT { return Ty.doubleElements(); });
}
}
}

View File

@ -26,6 +26,7 @@ using namespace llvm;
AArch64MachineLegalizer::AArch64MachineLegalizer() {
using namespace TargetOpcode;
const LLT p0 = LLT::pointer(0);
const LLT s1 = LLT::scalar(1);
const LLT s8 = LLT::scalar(8);
const LLT s16 = LLT::scalar(16);
@ -37,43 +38,49 @@ AArch64MachineLegalizer::AArch64MachineLegalizer() {
for (auto BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR}) {
for (auto Ty : {s32, s64, v2s32, v4s32, v2s64})
setAction(BinOp, Ty, Legal);
setAction({BinOp, Ty}, Legal);
for (auto Ty : {s8, s16})
setAction(BinOp, Ty, WidenScalar);
setAction({BinOp, Ty}, WidenScalar);
}
for (auto BinOp : {G_SHL, G_LSHR, G_ASHR, G_SDIV, G_UDIV})
for (auto Ty : {s32, s64})
setAction(BinOp, Ty, Legal);
setAction({BinOp, Ty}, Legal);
for (auto BinOp : {G_FADD, G_FSUB, G_FMUL, G_FDIV})
for (auto Ty : {s32, s64})
setAction(BinOp, Ty, Legal);
setAction({BinOp, Ty}, Legal);
for (auto MemOp : {G_LOAD, G_STORE}) {
for (auto Ty : {s8, s16, s32, s64})
setAction(MemOp, Ty, Legal);
setAction({MemOp, Ty}, Legal);
setAction(MemOp, s1, WidenScalar);
setAction({MemOp, s1}, WidenScalar);
// And everything's fine in addrspace 0.
setAction({MemOp, 1, p0}, Legal);
}
for (auto Ty : {s32, s64}) {
setAction(TargetOpcode::G_CONSTANT, Ty, Legal);
setAction(TargetOpcode::G_FCONSTANT, Ty, Legal);
setAction({TargetOpcode::G_CONSTANT, Ty}, Legal);
setAction({TargetOpcode::G_FCONSTANT, Ty}, Legal);
}
for (auto Ty : {s1, s8, s16})
setAction(TargetOpcode::G_CONSTANT, Ty, WidenScalar);
setAction({TargetOpcode::G_CONSTANT, Ty}, WidenScalar);
setAction(TargetOpcode::G_FCONSTANT, s16, WidenScalar);
setAction({TargetOpcode::G_FCONSTANT, s16}, WidenScalar);
setAction(G_BR, LLT::unsized(), Legal);
setAction({G_BR, LLT::unsized()}, Legal);
setAction(G_FRAME_INDEX, LLT::pointer(0), Legal);
setAction({G_FRAME_INDEX, p0}, Legal);
setAction(G_PTRTOINT, s64, Legal);
setAction(G_INTTOPTR, LLT::pointer(0), Legal);
setAction({G_PTRTOINT, 0, s64}, Legal);
setAction({G_PTRTOINT, 1, p0}, Legal);
setAction({G_INTTOPTR, 0, p0}, Legal);
setAction({G_INTTOPTR, 1, s64}, Legal);
computeTables();
}

View File

@ -52,51 +52,71 @@ namespace {
TEST(MachineLegalizerTest, ScalarRISC) {
using namespace TargetOpcode;
MachineLegalizer L;
// Typical RISCy set of operations based on AArch64.
L.setAction(TargetOpcode::G_ADD, LLT::scalar(8), WidenScalar);
L.setAction(TargetOpcode::G_ADD, LLT::scalar(16), WidenScalar);
L.setAction(TargetOpcode::G_ADD, LLT::scalar(32), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::scalar(64), Legal);
L.setAction({G_ADD, LLT::scalar(8)}, WidenScalar);
L.setAction({G_ADD, LLT::scalar(16)}, WidenScalar);
L.setAction({G_ADD, LLT::scalar(32)}, Legal);
L.setAction({G_ADD, LLT::scalar(64)}, Legal);
L.computeTables();
// Check we infer the correct types and actually do what we're told.
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::scalar(8)),
ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(8)}),
std::make_pair(WidenScalar, LLT::scalar(32)));
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::scalar(16)),
ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(16)}),
std::make_pair(WidenScalar, LLT::scalar(32)));
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::scalar(32)),
ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(32)}),
std::make_pair(Legal, LLT::scalar(32)));
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::scalar(64)),
ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(64)}),
std::make_pair(Legal, LLT::scalar(64)));
// Make sure the default for over-sized types applies.
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::scalar(128)),
ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(128)}),
std::make_pair(NarrowScalar, LLT::scalar(64)));
}
TEST(MachineLegalizerTest, VectorRISC) {
using namespace TargetOpcode;
MachineLegalizer L;
// Typical RISCy set of operations based on ARM.
L.setScalarInVectorAction(TargetOpcode::G_ADD, LLT::scalar(8), Legal);
L.setScalarInVectorAction(TargetOpcode::G_ADD, LLT::scalar(16), Legal);
L.setScalarInVectorAction(TargetOpcode::G_ADD, LLT::scalar(32), Legal);
L.setScalarInVectorAction(G_ADD, LLT::scalar(8), Legal);
L.setScalarInVectorAction(G_ADD, LLT::scalar(16), Legal);
L.setScalarInVectorAction(G_ADD, LLT::scalar(32), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::vector(8, 8), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::vector(16, 8), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::vector(4, 16), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::vector(8, 16), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::vector(2, 32), Legal);
L.setAction(TargetOpcode::G_ADD, LLT::vector(4, 32), Legal);
L.setAction({G_ADD, LLT::vector(8, 8)}, Legal);
L.setAction({G_ADD, LLT::vector(16, 8)}, Legal);
L.setAction({G_ADD, LLT::vector(4, 16)}, Legal);
L.setAction({G_ADD, LLT::vector(8, 16)}, Legal);
L.setAction({G_ADD, LLT::vector(2, 32)}, Legal);
L.setAction({G_ADD, LLT::vector(4, 32)}, Legal);
L.computeTables();
// Check we infer the correct types and actually do what we're told for some
// simple cases.
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::vector(2, 8)),
ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}),
std::make_pair(MoreElements, LLT::vector(8, 8)));
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::vector(8, 8)),
ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 8)}),
std::make_pair(Legal, LLT::vector(8, 8)));
ASSERT_EQ(L.getAction(TargetOpcode::G_ADD, LLT::vector(8, 32)),
ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 32)}),
std::make_pair(FewerElements, LLT::vector(4, 32)));
}
TEST(MachineLegalizerTest, MultipleTypes) {
using namespace TargetOpcode;
MachineLegalizer L;
// Typical RISCy set of operations based on AArch64.
L.setAction({G_PTRTOINT, 0, LLT::scalar(64)}, Legal);
L.setAction({G_PTRTOINT, 1, LLT::pointer(0)}, Legal);
L.setAction({G_PTRTOINT, 0, LLT::scalar(32)}, WidenScalar);
L.computeTables();
// Check we infer the correct types and actually do what we're told.
ASSERT_EQ(L.getAction({G_PTRTOINT, 0, LLT::scalar(64)}),
std::make_pair(Legal, LLT::scalar(64)));
ASSERT_EQ(L.getAction({G_PTRTOINT, 1, LLT::pointer(0)}),
std::make_pair(Legal, LLT::pointer(0)));
}
}