llvm-for-llvmta/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h

1488 lines
62 KiB
C
Raw Normal View History

2022-04-25 10:02:23 +02:00
//===- llvm/CodeGen/GlobalISel/LegalizerInfo.h ------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
/// Interface for Targets to specify which operations they can successfully
/// select and how the others should be expanded most efficiently.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H
#define LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdint>
#include <tuple>
#include <unordered_map>
#include <utility>
namespace llvm {
extern cl::opt<bool> DisableGISelLegalityCheck;
class LegalizerHelper;
class MachineInstr;
class MachineRegisterInfo;
class MCInstrInfo;
class GISelChangeObserver;
namespace LegalizeActions {
enum LegalizeAction : std::uint8_t {
/// The operation is expected to be selectable directly by the target, and
/// no transformation is necessary.
Legal,
/// The operation should be synthesized from multiple instructions acting on
/// a narrower scalar base-type. For example a 64-bit add might be
/// implemented in terms of 32-bit add-with-carry.
NarrowScalar,
/// The operation should be implemented in terms of a wider scalar
/// base-type. For example a <2 x s8> add could be implemented as a <2
/// x s32> add (ignoring the high bits).
WidenScalar,
/// The (vector) operation should be implemented by splitting it into
/// sub-vectors where the operation is legal. For example a <8 x s64> add
/// might be implemented as 4 separate <2 x s64> adds.
FewerElements,
/// The (vector) operation should be implemented by widening the input
/// vector and ignoring the lanes added by doing so. For example <2 x i8> is
/// rarely legal, but you might perform an <8 x i8> and then only look at
/// the first two results.
MoreElements,
/// Perform the operation on a different, but equivalently sized type.
Bitcast,
/// The operation itself must be expressed in terms of simpler actions on
/// this target. E.g. a SREM replaced by an SDIV and subtraction.
Lower,
/// The operation should be implemented as a call to some kind of runtime
/// support library. For example this usually happens on machines that don't
/// support floating-point operations natively.
Libcall,
/// The target wants to do something special with this combination of
/// operand and type. A callback will be issued when it is needed.
Custom,
/// This operation is completely unsupported on the target. A programming
/// error has occurred.
Unsupported,
/// Sentinel value for when no action was found in the specified table.
NotFound,
/// Fall back onto the old rules.
/// TODO: Remove this once we've migrated
UseLegacyRules,
};
} // end namespace LegalizeActions
raw_ostream &operator<<(raw_ostream &OS, LegalizeActions::LegalizeAction Action);
using LegalizeActions::LegalizeAction;
/// 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 = 0;
LLT Type;
InstrAspect(unsigned Opcode, LLT Type) : Opcode(Opcode), 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;
}
};
/// The LegalityQuery object bundles together all the information that's needed
/// to decide whether a given operation is legal or not.
/// For efficiency, it doesn't make a copy of Types so care must be taken not
/// to free it before using the query.
struct LegalityQuery {
unsigned Opcode;
ArrayRef<LLT> Types;
struct MemDesc {
uint64_t SizeInBits;
uint64_t AlignInBits;
AtomicOrdering Ordering;
};
/// Operations which require memory can use this to place requirements on the
/// memory type for each MMO.
ArrayRef<MemDesc> MMODescrs;
constexpr LegalityQuery(unsigned Opcode, const ArrayRef<LLT> Types,
const ArrayRef<MemDesc> MMODescrs)
: Opcode(Opcode), Types(Types), MMODescrs(MMODescrs) {}
constexpr LegalityQuery(unsigned Opcode, const ArrayRef<LLT> Types)
: LegalityQuery(Opcode, Types, {}) {}
raw_ostream &print(raw_ostream &OS) const;
};
/// The result of a query. It either indicates a final answer of Legal or
/// Unsupported or describes an action that must be taken to make an operation
/// more legal.
struct LegalizeActionStep {
/// The action to take or the final answer.
LegalizeAction Action;
/// If describing an action, the type index to change. Otherwise zero.
unsigned TypeIdx;
/// If describing an action, the new type for TypeIdx. Otherwise LLT{}.
LLT NewType;
LegalizeActionStep(LegalizeAction Action, unsigned TypeIdx,
const LLT NewType)
: Action(Action), TypeIdx(TypeIdx), NewType(NewType) {}
bool operator==(const LegalizeActionStep &RHS) const {
return std::tie(Action, TypeIdx, NewType) ==
std::tie(RHS.Action, RHS.TypeIdx, RHS.NewType);
}
};
using LegalityPredicate = std::function<bool (const LegalityQuery &)>;
using LegalizeMutation =
std::function<std::pair<unsigned, LLT>(const LegalityQuery &)>;
namespace LegalityPredicates {
struct TypePairAndMemDesc {
LLT Type0;
LLT Type1;
uint64_t MemSize;
uint64_t Align;
bool operator==(const TypePairAndMemDesc &Other) const {
return Type0 == Other.Type0 && Type1 == Other.Type1 &&
Align == Other.Align &&
MemSize == Other.MemSize;
}
/// \returns true if this memory access is legal with for the access described
/// by \p Other (The alignment is sufficient for the size and result type).
bool isCompatible(const TypePairAndMemDesc &Other) const {
return Type0 == Other.Type0 && Type1 == Other.Type1 &&
Align >= Other.Align &&
MemSize == Other.MemSize;
}
};
/// True iff P0 and P1 are true.
template<typename Predicate>
Predicate all(Predicate P0, Predicate P1) {
return [=](const LegalityQuery &Query) {
return P0(Query) && P1(Query);
};
}
/// True iff all given predicates are true.
template<typename Predicate, typename... Args>
Predicate all(Predicate P0, Predicate P1, Args... args) {
return all(all(P0, P1), args...);
}
/// True iff P0 or P1 are true.
template<typename Predicate>
Predicate any(Predicate P0, Predicate P1) {
return [=](const LegalityQuery &Query) {
return P0(Query) || P1(Query);
};
}
/// True iff any given predicates are true.
template<typename Predicate, typename... Args>
Predicate any(Predicate P0, Predicate P1, Args... args) {
return any(any(P0, P1), args...);
}
/// True iff the given type index is the specified type.
LegalityPredicate typeIs(unsigned TypeIdx, LLT TypesInit);
/// True iff the given type index is one of the specified types.
LegalityPredicate typeInSet(unsigned TypeIdx,
std::initializer_list<LLT> TypesInit);
/// True iff the given type index is not the specified type.
inline LegalityPredicate typeIsNot(unsigned TypeIdx, LLT Type) {
return [=](const LegalityQuery &Query) {
return Query.Types[TypeIdx] != Type;
};
}
/// True iff the given types for the given pair of type indexes is one of the
/// specified type pairs.
LegalityPredicate
typePairInSet(unsigned TypeIdx0, unsigned TypeIdx1,
std::initializer_list<std::pair<LLT, LLT>> TypesInit);
/// True iff the given types for the given pair of type indexes is one of the
/// specified type pairs.
LegalityPredicate typePairAndMemDescInSet(
unsigned TypeIdx0, unsigned TypeIdx1, unsigned MMOIdx,
std::initializer_list<TypePairAndMemDesc> TypesAndMemDescInit);
/// True iff the specified type index is a scalar.
LegalityPredicate isScalar(unsigned TypeIdx);
/// True iff the specified type index is a vector.
LegalityPredicate isVector(unsigned TypeIdx);
/// True iff the specified type index is a pointer (with any address space).
LegalityPredicate isPointer(unsigned TypeIdx);
/// True iff the specified type index is a pointer with the specified address
/// space.
LegalityPredicate isPointer(unsigned TypeIdx, unsigned AddrSpace);
/// True if the type index is a vector with element type \p EltTy
LegalityPredicate elementTypeIs(unsigned TypeIdx, LLT EltTy);
/// True iff the specified type index is a scalar that's narrower than the given
/// size.
LegalityPredicate scalarNarrowerThan(unsigned TypeIdx, unsigned Size);
/// True iff the specified type index is a scalar that's wider than the given
/// size.
LegalityPredicate scalarWiderThan(unsigned TypeIdx, unsigned Size);
/// True iff the specified type index is a scalar or vector with an element type
/// that's narrower than the given size.
LegalityPredicate scalarOrEltNarrowerThan(unsigned TypeIdx, unsigned Size);
/// True iff the specified type index is a scalar or a vector with an element
/// type that's wider than the given size.
LegalityPredicate scalarOrEltWiderThan(unsigned TypeIdx, unsigned Size);
/// True iff the specified type index is a scalar whose size is not a power of
/// 2.
LegalityPredicate sizeNotPow2(unsigned TypeIdx);
/// True iff the specified type index is a scalar or vector whose element size
/// is not a power of 2.
LegalityPredicate scalarOrEltSizeNotPow2(unsigned TypeIdx);
/// True if the total bitwidth of the specified type index is \p Size bits.
LegalityPredicate sizeIs(unsigned TypeIdx, unsigned Size);
/// True iff the specified type indices are both the same bit size.
LegalityPredicate sameSize(unsigned TypeIdx0, unsigned TypeIdx1);
/// True iff the first type index has a larger total bit size than second type
/// index.
LegalityPredicate largerThan(unsigned TypeIdx0, unsigned TypeIdx1);
/// True iff the first type index has a smaller total bit size than second type
/// index.
LegalityPredicate smallerThan(unsigned TypeIdx0, unsigned TypeIdx1);
/// True iff the specified MMO index has a size that is not a power of 2
LegalityPredicate memSizeInBytesNotPow2(unsigned MMOIdx);
/// True iff the specified type index is a vector whose element count is not a
/// power of 2.
LegalityPredicate numElementsNotPow2(unsigned TypeIdx);
/// True iff the specified MMO index has at an atomic ordering of at Ordering or
/// stronger.
LegalityPredicate atomicOrderingAtLeastOrStrongerThan(unsigned MMOIdx,
AtomicOrdering Ordering);
} // end namespace LegalityPredicates
namespace LegalizeMutations {
/// Select this specific type for the given type index.
LegalizeMutation changeTo(unsigned TypeIdx, LLT Ty);
/// Keep the same type as the given type index.
LegalizeMutation changeTo(unsigned TypeIdx, unsigned FromTypeIdx);
/// Keep the same scalar or element type as the given type index.
LegalizeMutation changeElementTo(unsigned TypeIdx, unsigned FromTypeIdx);
/// Keep the same scalar or element type as the given type.
LegalizeMutation changeElementTo(unsigned TypeIdx, LLT Ty);
/// Change the scalar size or element size to have the same scalar size as type
/// index \p FromIndex. Unlike changeElementTo, this discards pointer types and
/// only changes the size.
LegalizeMutation changeElementSizeTo(unsigned TypeIdx, unsigned FromTypeIdx);
/// Widen the scalar type or vector element type for the given type index to the
/// next power of 2.
LegalizeMutation widenScalarOrEltToNextPow2(unsigned TypeIdx, unsigned Min = 0);
/// Add more elements to the type for the given type index to the next power of
/// 2.
LegalizeMutation moreElementsToNextPow2(unsigned TypeIdx, unsigned Min = 0);
/// Break up the vector type for the given type index into the element type.
LegalizeMutation scalarize(unsigned TypeIdx);
} // end namespace LegalizeMutations
/// A single rule in a legalizer info ruleset.
/// The specified action is chosen when the predicate is true. Where appropriate
/// for the action (e.g. for WidenScalar) the new type is selected using the
/// given mutator.
class LegalizeRule {
LegalityPredicate Predicate;
LegalizeAction Action;
LegalizeMutation Mutation;
public:
LegalizeRule(LegalityPredicate Predicate, LegalizeAction Action,
LegalizeMutation Mutation = nullptr)
: Predicate(Predicate), Action(Action), Mutation(Mutation) {}
/// Test whether the LegalityQuery matches.
bool match(const LegalityQuery &Query) const {
return Predicate(Query);
}
LegalizeAction getAction() const { return Action; }
/// Determine the change to make.
std::pair<unsigned, LLT> determineMutation(const LegalityQuery &Query) const {
if (Mutation)
return Mutation(Query);
return std::make_pair(0, LLT{});
}
};
class LegalizeRuleSet {
/// When non-zero, the opcode we are an alias of
unsigned AliasOf;
/// If true, there is another opcode that aliases this one
bool IsAliasedByAnother;
SmallVector<LegalizeRule, 2> Rules;
#ifndef NDEBUG
/// If bit I is set, this rule set contains a rule that may handle (predicate
/// or perform an action upon (or both)) the type index I. The uncertainty
/// comes from free-form rules executing user-provided lambda functions. We
/// conservatively assume such rules do the right thing and cover all type
/// indices. The bitset is intentionally 1 bit wider than it absolutely needs
/// to be to distinguish such cases from the cases where all type indices are
/// individually handled.
SmallBitVector TypeIdxsCovered{MCOI::OPERAND_LAST_GENERIC -
MCOI::OPERAND_FIRST_GENERIC + 2};
SmallBitVector ImmIdxsCovered{MCOI::OPERAND_LAST_GENERIC_IMM -
MCOI::OPERAND_FIRST_GENERIC_IMM + 2};
#endif
unsigned typeIdx(unsigned TypeIdx) {
assert(TypeIdx <=
(MCOI::OPERAND_LAST_GENERIC - MCOI::OPERAND_FIRST_GENERIC) &&
"Type Index is out of bounds");
#ifndef NDEBUG
TypeIdxsCovered.set(TypeIdx);
#endif
return TypeIdx;
}
unsigned immIdx(unsigned ImmIdx) {
assert(ImmIdx <= (MCOI::OPERAND_LAST_GENERIC_IMM -
MCOI::OPERAND_FIRST_GENERIC_IMM) &&
"Imm Index is out of bounds");
#ifndef NDEBUG
ImmIdxsCovered.set(ImmIdx);
#endif
return ImmIdx;
}
void markAllIdxsAsCovered() {
#ifndef NDEBUG
TypeIdxsCovered.set();
ImmIdxsCovered.set();
#endif
}
void add(const LegalizeRule &Rule) {
assert(AliasOf == 0 &&
"RuleSet is aliased, change the representative opcode instead");
Rules.push_back(Rule);
}
static bool always(const LegalityQuery &) { return true; }
/// Use the given action when the predicate is true.
/// Action should not be an action that requires mutation.
LegalizeRuleSet &actionIf(LegalizeAction Action,
LegalityPredicate Predicate) {
add({Predicate, Action});
return *this;
}
/// Use the given action when the predicate is true.
/// Action should be an action that requires mutation.
LegalizeRuleSet &actionIf(LegalizeAction Action, LegalityPredicate Predicate,
LegalizeMutation Mutation) {
add({Predicate, Action, Mutation});
return *this;
}
/// Use the given action when type index 0 is any type in the given list.
/// Action should not be an action that requires mutation.
LegalizeRuleSet &actionFor(LegalizeAction Action,
std::initializer_list<LLT> Types) {
using namespace LegalityPredicates;
return actionIf(Action, typeInSet(typeIdx(0), Types));
}
/// Use the given action when type index 0 is any type in the given list.
/// Action should be an action that requires mutation.
LegalizeRuleSet &actionFor(LegalizeAction Action,
std::initializer_list<LLT> Types,
LegalizeMutation Mutation) {
using namespace LegalityPredicates;
return actionIf(Action, typeInSet(typeIdx(0), Types), Mutation);
}
/// Use the given action when type indexes 0 and 1 is any type pair in the
/// given list.
/// Action should not be an action that requires mutation.
LegalizeRuleSet &actionFor(LegalizeAction Action,
std::initializer_list<std::pair<LLT, LLT>> Types) {
using namespace LegalityPredicates;
return actionIf(Action, typePairInSet(typeIdx(0), typeIdx(1), Types));
}
/// Use the given action when type indexes 0 and 1 is any type pair in the
/// given list.
/// Action should be an action that requires mutation.
LegalizeRuleSet &actionFor(LegalizeAction Action,
std::initializer_list<std::pair<LLT, LLT>> Types,
LegalizeMutation Mutation) {
using namespace LegalityPredicates;
return actionIf(Action, typePairInSet(typeIdx(0), typeIdx(1), Types),
Mutation);
}
/// Use the given action when type index 0 is any type in the given list and
/// imm index 0 is anything. Action should not be an action that requires
/// mutation.
LegalizeRuleSet &actionForTypeWithAnyImm(LegalizeAction Action,
std::initializer_list<LLT> Types) {
using namespace LegalityPredicates;
immIdx(0); // Inform verifier imm idx 0 is handled.
return actionIf(Action, typeInSet(typeIdx(0), Types));
}
LegalizeRuleSet &actionForTypeWithAnyImm(
LegalizeAction Action, std::initializer_list<std::pair<LLT, LLT>> Types) {
using namespace LegalityPredicates;
immIdx(0); // Inform verifier imm idx 0 is handled.
return actionIf(Action, typePairInSet(typeIdx(0), typeIdx(1), Types));
}
/// Use the given action when type indexes 0 and 1 are both in the given list.
/// That is, the type pair is in the cartesian product of the list.
/// Action should not be an action that requires mutation.
LegalizeRuleSet &actionForCartesianProduct(LegalizeAction Action,
std::initializer_list<LLT> Types) {
using namespace LegalityPredicates;
return actionIf(Action, all(typeInSet(typeIdx(0), Types),
typeInSet(typeIdx(1), Types)));
}
/// Use the given action when type indexes 0 and 1 are both in their
/// respective lists.
/// That is, the type pair is in the cartesian product of the lists
/// Action should not be an action that requires mutation.
LegalizeRuleSet &
actionForCartesianProduct(LegalizeAction Action,
std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1) {
using namespace LegalityPredicates;
return actionIf(Action, all(typeInSet(typeIdx(0), Types0),
typeInSet(typeIdx(1), Types1)));
}
/// Use the given action when type indexes 0, 1, and 2 are all in their
/// respective lists.
/// That is, the type triple is in the cartesian product of the lists
/// Action should not be an action that requires mutation.
LegalizeRuleSet &actionForCartesianProduct(
LegalizeAction Action, std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1, std::initializer_list<LLT> Types2) {
using namespace LegalityPredicates;
return actionIf(Action, all(typeInSet(typeIdx(0), Types0),
all(typeInSet(typeIdx(1), Types1),
typeInSet(typeIdx(2), Types2))));
}
public:
LegalizeRuleSet() : AliasOf(0), IsAliasedByAnother(false), Rules() {}
bool isAliasedByAnother() { return IsAliasedByAnother; }
void setIsAliasedByAnother() { IsAliasedByAnother = true; }
void aliasTo(unsigned Opcode) {
assert((AliasOf == 0 || AliasOf == Opcode) &&
"Opcode is already aliased to another opcode");
assert(Rules.empty() && "Aliasing will discard rules");
AliasOf = Opcode;
}
unsigned getAlias() const { return AliasOf; }
/// The instruction is legal if predicate is true.
LegalizeRuleSet &legalIf(LegalityPredicate Predicate) {
// We have no choice but conservatively assume that the free-form
// user-provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Legal, Predicate);
}
/// The instruction is legal when type index 0 is any type in the given list.
LegalizeRuleSet &legalFor(std::initializer_list<LLT> Types) {
return actionFor(LegalizeAction::Legal, Types);
}
/// The instruction is legal when type indexes 0 and 1 is any type pair in the
/// given list.
LegalizeRuleSet &legalFor(std::initializer_list<std::pair<LLT, LLT>> Types) {
return actionFor(LegalizeAction::Legal, Types);
}
/// The instruction is legal when type index 0 is any type in the given list
/// and imm index 0 is anything.
LegalizeRuleSet &legalForTypeWithAnyImm(std::initializer_list<LLT> Types) {
markAllIdxsAsCovered();
return actionForTypeWithAnyImm(LegalizeAction::Legal, Types);
}
LegalizeRuleSet &legalForTypeWithAnyImm(
std::initializer_list<std::pair<LLT, LLT>> Types) {
markAllIdxsAsCovered();
return actionForTypeWithAnyImm(LegalizeAction::Legal, Types);
}
/// The instruction is legal when type indexes 0 and 1 along with the memory
/// size and minimum alignment is any type and size tuple in the given list.
LegalizeRuleSet &legalForTypesWithMemDesc(
std::initializer_list<LegalityPredicates::TypePairAndMemDesc>
TypesAndMemDesc) {
return actionIf(LegalizeAction::Legal,
LegalityPredicates::typePairAndMemDescInSet(
typeIdx(0), typeIdx(1), /*MMOIdx*/ 0, TypesAndMemDesc));
}
/// The instruction is legal when type indexes 0 and 1 are both in the given
/// list. That is, the type pair is in the cartesian product of the list.
LegalizeRuleSet &legalForCartesianProduct(std::initializer_list<LLT> Types) {
return actionForCartesianProduct(LegalizeAction::Legal, Types);
}
/// The instruction is legal when type indexes 0 and 1 are both their
/// respective lists.
LegalizeRuleSet &legalForCartesianProduct(std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1) {
return actionForCartesianProduct(LegalizeAction::Legal, Types0, Types1);
}
/// The instruction is legal when type indexes 0, 1, and 2 are both their
/// respective lists.
LegalizeRuleSet &legalForCartesianProduct(std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1,
std::initializer_list<LLT> Types2) {
return actionForCartesianProduct(LegalizeAction::Legal, Types0, Types1,
Types2);
}
LegalizeRuleSet &alwaysLegal() {
using namespace LegalizeMutations;
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Legal, always);
}
/// The specified type index is coerced if predicate is true.
LegalizeRuleSet &bitcastIf(LegalityPredicate Predicate,
LegalizeMutation Mutation) {
// We have no choice but conservatively assume that lowering with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Bitcast, Predicate, Mutation);
}
/// The instruction is lowered.
LegalizeRuleSet &lower() {
using namespace LegalizeMutations;
// We have no choice but conservatively assume that predicate-less lowering
// properly handles all type indices by design:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Lower, always);
}
/// The instruction is lowered if predicate is true. Keep type index 0 as the
/// same type.
LegalizeRuleSet &lowerIf(LegalityPredicate Predicate) {
using namespace LegalizeMutations;
// We have no choice but conservatively assume that lowering with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Lower, Predicate);
}
/// The instruction is lowered if predicate is true.
LegalizeRuleSet &lowerIf(LegalityPredicate Predicate,
LegalizeMutation Mutation) {
// We have no choice but conservatively assume that lowering with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Lower, Predicate, Mutation);
}
/// The instruction is lowered when type index 0 is any type in the given
/// list. Keep type index 0 as the same type.
LegalizeRuleSet &lowerFor(std::initializer_list<LLT> Types) {
return actionFor(LegalizeAction::Lower, Types);
}
/// The instruction is lowered when type index 0 is any type in the given
/// list.
LegalizeRuleSet &lowerFor(std::initializer_list<LLT> Types,
LegalizeMutation Mutation) {
return actionFor(LegalizeAction::Lower, Types, Mutation);
}
/// The instruction is lowered when type indexes 0 and 1 is any type pair in
/// the given list. Keep type index 0 as the same type.
LegalizeRuleSet &lowerFor(std::initializer_list<std::pair<LLT, LLT>> Types) {
return actionFor(LegalizeAction::Lower, Types);
}
/// The instruction is lowered when type indexes 0 and 1 is any type pair in
/// the given list.
LegalizeRuleSet &lowerFor(std::initializer_list<std::pair<LLT, LLT>> Types,
LegalizeMutation Mutation) {
return actionFor(LegalizeAction::Lower, Types, Mutation);
}
/// The instruction is lowered when type indexes 0 and 1 are both in their
/// respective lists.
LegalizeRuleSet &lowerForCartesianProduct(std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1) {
using namespace LegalityPredicates;
return actionForCartesianProduct(LegalizeAction::Lower, Types0, Types1);
}
/// The instruction is lowered when when type indexes 0, 1, and 2 are all in
/// their respective lists.
LegalizeRuleSet &lowerForCartesianProduct(std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1,
std::initializer_list<LLT> Types2) {
using namespace LegalityPredicates;
return actionForCartesianProduct(LegalizeAction::Lower, Types0, Types1,
Types2);
}
/// The instruction is emitted as a library call.
LegalizeRuleSet &libcall() {
using namespace LegalizeMutations;
// We have no choice but conservatively assume that predicate-less lowering
// properly handles all type indices by design:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Libcall, always);
}
/// Like legalIf, but for the Libcall action.
LegalizeRuleSet &libcallIf(LegalityPredicate Predicate) {
// We have no choice but conservatively assume that a libcall with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Libcall, Predicate);
}
LegalizeRuleSet &libcallFor(std::initializer_list<LLT> Types) {
return actionFor(LegalizeAction::Libcall, Types);
}
LegalizeRuleSet &
libcallFor(std::initializer_list<std::pair<LLT, LLT>> Types) {
return actionFor(LegalizeAction::Libcall, Types);
}
LegalizeRuleSet &
libcallForCartesianProduct(std::initializer_list<LLT> Types) {
return actionForCartesianProduct(LegalizeAction::Libcall, Types);
}
LegalizeRuleSet &
libcallForCartesianProduct(std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1) {
return actionForCartesianProduct(LegalizeAction::Libcall, Types0, Types1);
}
/// Widen the scalar to the one selected by the mutation if the predicate is
/// true.
LegalizeRuleSet &widenScalarIf(LegalityPredicate Predicate,
LegalizeMutation Mutation) {
// We have no choice but conservatively assume that an action with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::WidenScalar, Predicate, Mutation);
}
/// Narrow the scalar to the one selected by the mutation if the predicate is
/// true.
LegalizeRuleSet &narrowScalarIf(LegalityPredicate Predicate,
LegalizeMutation Mutation) {
// We have no choice but conservatively assume that an action with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::NarrowScalar, Predicate, Mutation);
}
/// Narrow the scalar, specified in mutation, when type indexes 0 and 1 is any
/// type pair in the given list.
LegalizeRuleSet &
narrowScalarFor(std::initializer_list<std::pair<LLT, LLT>> Types,
LegalizeMutation Mutation) {
return actionFor(LegalizeAction::NarrowScalar, Types, Mutation);
}
/// Add more elements to reach the type selected by the mutation if the
/// predicate is true.
LegalizeRuleSet &moreElementsIf(LegalityPredicate Predicate,
LegalizeMutation Mutation) {
// We have no choice but conservatively assume that an action with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::MoreElements, Predicate, Mutation);
}
/// Remove elements to reach the type selected by the mutation if the
/// predicate is true.
LegalizeRuleSet &fewerElementsIf(LegalityPredicate Predicate,
LegalizeMutation Mutation) {
// We have no choice but conservatively assume that an action with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::FewerElements, Predicate, Mutation);
}
/// The instruction is unsupported.
LegalizeRuleSet &unsupported() {
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Unsupported, always);
}
LegalizeRuleSet &unsupportedIf(LegalityPredicate Predicate) {
return actionIf(LegalizeAction::Unsupported, Predicate);
}
LegalizeRuleSet &unsupportedFor(std::initializer_list<LLT> Types) {
return actionFor(LegalizeAction::Unsupported, Types);
}
LegalizeRuleSet &unsupportedIfMemSizeNotPow2() {
return actionIf(LegalizeAction::Unsupported,
LegalityPredicates::memSizeInBytesNotPow2(0));
}
LegalizeRuleSet &lowerIfMemSizeNotPow2() {
return actionIf(LegalizeAction::Lower,
LegalityPredicates::memSizeInBytesNotPow2(0));
}
LegalizeRuleSet &customIf(LegalityPredicate Predicate) {
// We have no choice but conservatively assume that a custom action with a
// free-form user provided Predicate properly handles all type indices:
markAllIdxsAsCovered();
return actionIf(LegalizeAction::Custom, Predicate);
}
LegalizeRuleSet &customFor(std::initializer_list<LLT> Types) {
return actionFor(LegalizeAction::Custom, Types);
}
/// The instruction is custom when type indexes 0 and 1 is any type pair in the
/// given list.
LegalizeRuleSet &customFor(std::initializer_list<std::pair<LLT, LLT>> Types) {
return actionFor(LegalizeAction::Custom, Types);
}
LegalizeRuleSet &customForCartesianProduct(std::initializer_list<LLT> Types) {
return actionForCartesianProduct(LegalizeAction::Custom, Types);
}
LegalizeRuleSet &
customForCartesianProduct(std::initializer_list<LLT> Types0,
std::initializer_list<LLT> Types1) {
return actionForCartesianProduct(LegalizeAction::Custom, Types0, Types1);
}
/// Unconditionally custom lower.
LegalizeRuleSet &custom() {
return customIf(always);
}
/// Widen the scalar to the next power of two that is at least MinSize.
/// No effect if the type is not a scalar or is a power of two.
LegalizeRuleSet &widenScalarToNextPow2(unsigned TypeIdx,
unsigned MinSize = 0) {
using namespace LegalityPredicates;
return actionIf(
LegalizeAction::WidenScalar, sizeNotPow2(typeIdx(TypeIdx)),
LegalizeMutations::widenScalarOrEltToNextPow2(TypeIdx, MinSize));
}
/// Widen the scalar or vector element type to the next power of two that is
/// at least MinSize. No effect if the scalar size is a power of two.
LegalizeRuleSet &widenScalarOrEltToNextPow2(unsigned TypeIdx,
unsigned MinSize = 0) {
using namespace LegalityPredicates;
return actionIf(
LegalizeAction::WidenScalar, scalarOrEltSizeNotPow2(typeIdx(TypeIdx)),
LegalizeMutations::widenScalarOrEltToNextPow2(TypeIdx, MinSize));
}
LegalizeRuleSet &narrowScalar(unsigned TypeIdx, LegalizeMutation Mutation) {
using namespace LegalityPredicates;
return actionIf(LegalizeAction::NarrowScalar, isScalar(typeIdx(TypeIdx)),
Mutation);
}
LegalizeRuleSet &scalarize(unsigned TypeIdx) {
using namespace LegalityPredicates;
return actionIf(LegalizeAction::FewerElements, isVector(typeIdx(TypeIdx)),
LegalizeMutations::scalarize(TypeIdx));
}
LegalizeRuleSet &scalarizeIf(LegalityPredicate Predicate, unsigned TypeIdx) {
using namespace LegalityPredicates;
return actionIf(LegalizeAction::FewerElements,
all(Predicate, isVector(typeIdx(TypeIdx))),
LegalizeMutations::scalarize(TypeIdx));
}
/// Ensure the scalar or element is at least as wide as Ty.
LegalizeRuleSet &minScalarOrElt(unsigned TypeIdx, const LLT Ty) {
using namespace LegalityPredicates;
using namespace LegalizeMutations;
return actionIf(LegalizeAction::WidenScalar,
scalarOrEltNarrowerThan(TypeIdx, Ty.getScalarSizeInBits()),
changeElementTo(typeIdx(TypeIdx), Ty));
}
/// Ensure the scalar or element is at least as wide as Ty.
LegalizeRuleSet &minScalarOrEltIf(LegalityPredicate Predicate,
unsigned TypeIdx, const LLT Ty) {
using namespace LegalityPredicates;
using namespace LegalizeMutations;
return actionIf(LegalizeAction::WidenScalar,
all(Predicate, scalarOrEltNarrowerThan(
TypeIdx, Ty.getScalarSizeInBits())),
changeElementTo(typeIdx(TypeIdx), Ty));
}
/// Ensure the scalar is at least as wide as Ty.
LegalizeRuleSet &minScalar(unsigned TypeIdx, const LLT Ty) {
using namespace LegalityPredicates;
using namespace LegalizeMutations;
return actionIf(LegalizeAction::WidenScalar,
scalarNarrowerThan(TypeIdx, Ty.getSizeInBits()),
changeTo(typeIdx(TypeIdx), Ty));
}
/// Ensure the scalar is at most as wide as Ty.
LegalizeRuleSet &maxScalarOrElt(unsigned TypeIdx, const LLT Ty) {
using namespace LegalityPredicates;
using namespace LegalizeMutations;
return actionIf(LegalizeAction::NarrowScalar,
scalarOrEltWiderThan(TypeIdx, Ty.getScalarSizeInBits()),
changeElementTo(typeIdx(TypeIdx), Ty));
}
/// Ensure the scalar is at most as wide as Ty.
LegalizeRuleSet &maxScalar(unsigned TypeIdx, const LLT Ty) {
using namespace LegalityPredicates;
using namespace LegalizeMutations;
return actionIf(LegalizeAction::NarrowScalar,
scalarWiderThan(TypeIdx, Ty.getSizeInBits()),
changeTo(typeIdx(TypeIdx), Ty));
}
/// Conditionally limit the maximum size of the scalar.
/// For example, when the maximum size of one type depends on the size of
/// another such as extracting N bits from an M bit container.
LegalizeRuleSet &maxScalarIf(LegalityPredicate Predicate, unsigned TypeIdx,
const LLT Ty) {
using namespace LegalityPredicates;
using namespace LegalizeMutations;
return actionIf(
LegalizeAction::NarrowScalar,
[=](const LegalityQuery &Query) {
const LLT QueryTy = Query.Types[TypeIdx];
return QueryTy.isScalar() &&
QueryTy.getSizeInBits() > Ty.getSizeInBits() &&
Predicate(Query);
},
changeElementTo(typeIdx(TypeIdx), Ty));
}
/// Limit the range of scalar sizes to MinTy and MaxTy.
LegalizeRuleSet &clampScalar(unsigned TypeIdx, const LLT MinTy,
const LLT MaxTy) {
assert(MinTy.isScalar() && MaxTy.isScalar() && "Expected scalar types");
return minScalar(TypeIdx, MinTy).maxScalar(TypeIdx, MaxTy);
}
/// Limit the range of scalar sizes to MinTy and MaxTy.
LegalizeRuleSet &clampScalarOrElt(unsigned TypeIdx, const LLT MinTy,
const LLT MaxTy) {
return minScalarOrElt(TypeIdx, MinTy).maxScalarOrElt(TypeIdx, MaxTy);
}
/// Widen the scalar to match the size of another.
LegalizeRuleSet &minScalarSameAs(unsigned TypeIdx, unsigned LargeTypeIdx) {
typeIdx(TypeIdx);
return widenScalarIf(
[=](const LegalityQuery &Query) {
return Query.Types[LargeTypeIdx].getScalarSizeInBits() >
Query.Types[TypeIdx].getSizeInBits();
},
LegalizeMutations::changeElementSizeTo(TypeIdx, LargeTypeIdx));
}
/// Narrow the scalar to match the size of another.
LegalizeRuleSet &maxScalarSameAs(unsigned TypeIdx, unsigned NarrowTypeIdx) {
typeIdx(TypeIdx);
return narrowScalarIf(
[=](const LegalityQuery &Query) {
return Query.Types[NarrowTypeIdx].getScalarSizeInBits() <
Query.Types[TypeIdx].getSizeInBits();
},
LegalizeMutations::changeElementSizeTo(TypeIdx, NarrowTypeIdx));
}
/// Change the type \p TypeIdx to have the same scalar size as type \p
/// SameSizeIdx.
LegalizeRuleSet &scalarSameSizeAs(unsigned TypeIdx, unsigned SameSizeIdx) {
return minScalarSameAs(TypeIdx, SameSizeIdx)
.maxScalarSameAs(TypeIdx, SameSizeIdx);
}
/// Conditionally widen the scalar or elt to match the size of another.
LegalizeRuleSet &minScalarEltSameAsIf(LegalityPredicate Predicate,
unsigned TypeIdx, unsigned LargeTypeIdx) {
typeIdx(TypeIdx);
return widenScalarIf(
[=](const LegalityQuery &Query) {
return Query.Types[LargeTypeIdx].getScalarSizeInBits() >
Query.Types[TypeIdx].getScalarSizeInBits() &&
Predicate(Query);
},
[=](const LegalityQuery &Query) {
LLT T = Query.Types[LargeTypeIdx];
return std::make_pair(TypeIdx, T);
});
}
/// Add more elements to the vector to reach the next power of two.
/// No effect if the type is not a vector or the element count is a power of
/// two.
LegalizeRuleSet &moreElementsToNextPow2(unsigned TypeIdx) {
using namespace LegalityPredicates;
return actionIf(LegalizeAction::MoreElements,
numElementsNotPow2(typeIdx(TypeIdx)),
LegalizeMutations::moreElementsToNextPow2(TypeIdx));
}
/// Limit the number of elements in EltTy vectors to at least MinElements.
LegalizeRuleSet &clampMinNumElements(unsigned TypeIdx, const LLT EltTy,
unsigned MinElements) {
// Mark the type index as covered:
typeIdx(TypeIdx);
return actionIf(
LegalizeAction::MoreElements,
[=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[TypeIdx];
return VecTy.isVector() && VecTy.getElementType() == EltTy &&
VecTy.getNumElements() < MinElements;
},
[=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[TypeIdx];
return std::make_pair(
TypeIdx, LLT::vector(MinElements, VecTy.getElementType()));
});
}
/// Limit the number of elements in EltTy vectors to at most MaxElements.
LegalizeRuleSet &clampMaxNumElements(unsigned TypeIdx, const LLT EltTy,
unsigned MaxElements) {
// Mark the type index as covered:
typeIdx(TypeIdx);
return actionIf(
LegalizeAction::FewerElements,
[=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[TypeIdx];
return VecTy.isVector() && VecTy.getElementType() == EltTy &&
VecTy.getNumElements() > MaxElements;
},
[=](const LegalityQuery &Query) {
LLT VecTy = Query.Types[TypeIdx];
LLT NewTy = LLT::scalarOrVector(MaxElements, VecTy.getElementType());
return std::make_pair(TypeIdx, NewTy);
});
}
/// Limit the number of elements for the given vectors to at least MinTy's
/// number of elements and at most MaxTy's number of elements.
///
/// No effect if the type is not a vector or does not have the same element
/// type as the constraints.
/// The element type of MinTy and MaxTy must match.
LegalizeRuleSet &clampNumElements(unsigned TypeIdx, const LLT MinTy,
const LLT MaxTy) {
assert(MinTy.getElementType() == MaxTy.getElementType() &&
"Expected element types to agree");
const LLT EltTy = MinTy.getElementType();
return clampMinNumElements(TypeIdx, EltTy, MinTy.getNumElements())
.clampMaxNumElements(TypeIdx, EltTy, MaxTy.getNumElements());
}
/// Fallback on the previous implementation. This should only be used while
/// porting a rule.
LegalizeRuleSet &fallback() {
add({always, LegalizeAction::UseLegacyRules});
return *this;
}
/// Check if there is no type index which is obviously not handled by the
/// LegalizeRuleSet in any way at all.
/// \pre Type indices of the opcode form a dense [0, \p NumTypeIdxs) set.
bool verifyTypeIdxsCoverage(unsigned NumTypeIdxs) const;
/// Check if there is no imm index which is obviously not handled by the
/// LegalizeRuleSet in any way at all.
/// \pre Type indices of the opcode form a dense [0, \p NumTypeIdxs) set.
bool verifyImmIdxsCoverage(unsigned NumImmIdxs) const;
/// Apply the ruleset to the given LegalityQuery.
LegalizeActionStep apply(const LegalityQuery &Query) const;
};
class LegalizerInfo {
public:
LegalizerInfo();
virtual ~LegalizerInfo() = default;
unsigned getOpcodeIdxForOpcode(unsigned Opcode) const;
unsigned getActionDefinitionsIdx(unsigned Opcode) const;
/// Compute any ancillary tables needed to quickly decide how an operation
/// should be handled. This must be called after all "set*Action"methods but
/// before any query is made or incorrect results may be returned.
void computeTables();
/// Perform simple self-diagnostic and assert if there is anything obviously
/// wrong with the actions set up.
void verify(const MCInstrInfo &MII) const;
static bool needsLegalizingToDifferentSize(const LegalizeAction Action) {
using namespace LegalizeActions;
switch (Action) {
case NarrowScalar:
case WidenScalar:
case FewerElements:
case MoreElements:
case Unsupported:
return true;
default:
return false;
}
}
using SizeAndAction = std::pair<uint16_t, LegalizeAction>;
using SizeAndActionsVec = std::vector<SizeAndAction>;
using SizeChangeStrategy =
std::function<SizeAndActionsVec(const SizeAndActionsVec &v)>;
/// More friendly way to set an action for common types that have an LLT
/// representation.
/// The LegalizeAction must be one for which NeedsLegalizingToDifferentSize
/// returns false.
void setAction(const InstrAspect &Aspect, LegalizeAction Action) {
assert(!needsLegalizingToDifferentSize(Action));
TablesInitialized = false;
const unsigned OpcodeIdx = Aspect.Opcode - FirstOp;
if (SpecifiedActions[OpcodeIdx].size() <= Aspect.Idx)
SpecifiedActions[OpcodeIdx].resize(Aspect.Idx + 1);
SpecifiedActions[OpcodeIdx][Aspect.Idx][Aspect.Type] = Action;
}
/// The setAction calls record the non-size-changing legalization actions
/// to take on specificly-sized types. The SizeChangeStrategy defines what
/// to do when the size of the type needs to be changed to reach a legally
/// sized type (i.e., one that was defined through a setAction call).
/// e.g.
/// setAction ({G_ADD, 0, LLT::scalar(32)}, Legal);
/// setLegalizeScalarToDifferentSizeStrategy(
/// G_ADD, 0, widenToLargerTypesAndNarrowToLargest);
/// will end up defining getAction({G_ADD, 0, T}) to return the following
/// actions for different scalar types T:
/// LLT::scalar(1)..LLT::scalar(31): {WidenScalar, 0, LLT::scalar(32)}
/// LLT::scalar(32): {Legal, 0, LLT::scalar(32)}
/// LLT::scalar(33)..: {NarrowScalar, 0, LLT::scalar(32)}
///
/// If no SizeChangeAction gets defined, through this function,
/// the default is unsupportedForDifferentSizes.
void setLegalizeScalarToDifferentSizeStrategy(const unsigned Opcode,
const unsigned TypeIdx,
SizeChangeStrategy S) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (ScalarSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
ScalarSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
ScalarSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
}
/// See also setLegalizeScalarToDifferentSizeStrategy.
/// This function allows to set the SizeChangeStrategy for vector elements.
void setLegalizeVectorElementToDifferentSizeStrategy(const unsigned Opcode,
const unsigned TypeIdx,
SizeChangeStrategy S) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (VectorElementSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
VectorElementSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
VectorElementSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
}
/// A SizeChangeStrategy for the common case where legalization for a
/// particular operation consists of only supporting a specific set of type
/// sizes. E.g.
/// setAction ({G_DIV, 0, LLT::scalar(32)}, Legal);
/// setAction ({G_DIV, 0, LLT::scalar(64)}, Legal);
/// setLegalizeScalarToDifferentSizeStrategy(
/// G_DIV, 0, unsupportedForDifferentSizes);
/// will result in getAction({G_DIV, 0, T}) to return Legal for s32 and s64,
/// and Unsupported for all other scalar types T.
static SizeAndActionsVec
unsupportedForDifferentSizes(const SizeAndActionsVec &v) {
using namespace LegalizeActions;
return increaseToLargerTypesAndDecreaseToLargest(v, Unsupported,
Unsupported);
}
/// A SizeChangeStrategy for the common case where legalization for a
/// particular operation consists of widening the type to a large legal type,
/// unless there is no such type and then instead it should be narrowed to the
/// largest legal type.
static SizeAndActionsVec
widenToLargerTypesAndNarrowToLargest(const SizeAndActionsVec &v) {
using namespace LegalizeActions;
assert(v.size() > 0 &&
"At least one size that can be legalized towards is needed"
" for this SizeChangeStrategy");
return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
NarrowScalar);
}
static SizeAndActionsVec
widenToLargerTypesUnsupportedOtherwise(const SizeAndActionsVec &v) {
using namespace LegalizeActions;
return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
Unsupported);
}
static SizeAndActionsVec
narrowToSmallerAndUnsupportedIfTooSmall(const SizeAndActionsVec &v) {
using namespace LegalizeActions;
return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
Unsupported);
}
static SizeAndActionsVec
narrowToSmallerAndWidenToSmallest(const SizeAndActionsVec &v) {
using namespace LegalizeActions;
assert(v.size() > 0 &&
"At least one size that can be legalized towards is needed"
" for this SizeChangeStrategy");
return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
WidenScalar);
}
/// A SizeChangeStrategy for the common case where legalization for a
/// particular vector operation consists of having more elements in the
/// vector, to a type that is legal. Unless there is no such type and then
/// instead it should be legalized towards the widest vector that's still
/// legal. E.g.
/// setAction({G_ADD, LLT::vector(8, 8)}, Legal);
/// setAction({G_ADD, LLT::vector(16, 8)}, Legal);
/// setAction({G_ADD, LLT::vector(2, 32)}, Legal);
/// setAction({G_ADD, LLT::vector(4, 32)}, Legal);
/// setLegalizeVectorElementToDifferentSizeStrategy(
/// G_ADD, 0, moreToWiderTypesAndLessToWidest);
/// will result in the following getAction results:
/// * getAction({G_ADD, LLT::vector(8,8)}) returns
/// (Legal, vector(8,8)).
/// * getAction({G_ADD, LLT::vector(9,8)}) returns
/// (MoreElements, vector(16,8)).
/// * getAction({G_ADD, LLT::vector(8,32)}) returns
/// (FewerElements, vector(4,32)).
static SizeAndActionsVec
moreToWiderTypesAndLessToWidest(const SizeAndActionsVec &v) {
using namespace LegalizeActions;
return increaseToLargerTypesAndDecreaseToLargest(v, MoreElements,
FewerElements);
}
/// Helper function to implement many typical SizeChangeStrategy functions.
static SizeAndActionsVec
increaseToLargerTypesAndDecreaseToLargest(const SizeAndActionsVec &v,
LegalizeAction IncreaseAction,
LegalizeAction DecreaseAction);
/// Helper function to implement many typical SizeChangeStrategy functions.
static SizeAndActionsVec
decreaseToSmallerTypesAndIncreaseToSmallest(const SizeAndActionsVec &v,
LegalizeAction DecreaseAction,
LegalizeAction IncreaseAction);
/// Get the action definitions for the given opcode. Use this to run a
/// LegalityQuery through the definitions.
const LegalizeRuleSet &getActionDefinitions(unsigned Opcode) const;
/// Get the action definition builder for the given opcode. Use this to define
/// the action definitions.
///
/// It is an error to request an opcode that has already been requested by the
/// multiple-opcode variant.
LegalizeRuleSet &getActionDefinitionsBuilder(unsigned Opcode);
/// Get the action definition builder for the given set of opcodes. Use this
/// to define the action definitions for multiple opcodes at once. The first
/// opcode given will be considered the representative opcode and will hold
/// the definitions whereas the other opcodes will be configured to refer to
/// the representative opcode. This lowers memory requirements and very
/// slightly improves performance.
///
/// It would be very easy to introduce unexpected side-effects as a result of
/// this aliasing if it were permitted to request different but intersecting
/// sets of opcodes but that is difficult to keep track of. It is therefore an
/// error to request the same opcode twice using this API, to request an
/// opcode that already has definitions, or to use the single-opcode API on an
/// opcode that has already been requested by this API.
LegalizeRuleSet &
getActionDefinitionsBuilder(std::initializer_list<unsigned> Opcodes);
void aliasActionDefinitions(unsigned OpcodeTo, unsigned OpcodeFrom);
/// Determine what action should be taken to legalize the described
/// instruction. Requires computeTables to have been called.
///
/// \returns a description of the next legalization step to perform.
LegalizeActionStep getAction(const LegalityQuery &Query) const;
/// Determine what action should be taken to legalize the given generic
/// instruction.
///
/// \returns a description of the next legalization step to perform.
LegalizeActionStep getAction(const MachineInstr &MI,
const MachineRegisterInfo &MRI) const;
bool isLegal(const LegalityQuery &Query) const {
return getAction(Query).Action == LegalizeAction::Legal;
}
bool isLegalOrCustom(const LegalityQuery &Query) const {
auto Action = getAction(Query).Action;
return Action == LegalizeAction::Legal || Action == LegalizeAction::Custom;
}
bool isLegal(const MachineInstr &MI, const MachineRegisterInfo &MRI) const;
bool isLegalOrCustom(const MachineInstr &MI,
const MachineRegisterInfo &MRI) const;
/// Called for instructions with the Custom LegalizationAction.
virtual bool legalizeCustom(LegalizerHelper &Helper,
MachineInstr &MI) const {
llvm_unreachable("must implement this if custom action is used");
}
/// \returns true if MI is either legal or has been legalized and false if not
/// legal.
/// Return true if MI is either legal or has been legalized and false
/// if not legal.
virtual bool legalizeIntrinsic(LegalizerHelper &Helper,
MachineInstr &MI) const {
return true;
}
/// Return the opcode (SEXT/ZEXT/ANYEXT) that should be performed while
/// widening a constant of type SmallTy which targets can override.
/// For eg, the DAG does (SmallTy.isByteSized() ? G_SEXT : G_ZEXT) which
/// will be the default.
virtual unsigned getExtOpcodeForWideningConstant(LLT SmallTy) const;
private:
/// Determine what action should be taken to legalize the given generic
/// 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>
getAspectAction(const InstrAspect &Aspect) const;
/// The SizeAndActionsVec is a representation mapping between all natural
/// numbers and an Action. The natural number represents the bit size of
/// the InstrAspect. For example, for a target with native support for 32-bit
/// and 64-bit additions, you'd express that as:
/// setScalarAction(G_ADD, 0,
/// {{1, WidenScalar}, // bit sizes [ 1, 31[
/// {32, Legal}, // bit sizes [32, 33[
/// {33, WidenScalar}, // bit sizes [33, 64[
/// {64, Legal}, // bit sizes [64, 65[
/// {65, NarrowScalar} // bit sizes [65, +inf[
/// });
/// It may be that only 64-bit pointers are supported on your target:
/// setPointerAction(G_PTR_ADD, 0, LLT:pointer(1),
/// {{1, Unsupported}, // bit sizes [ 1, 63[
/// {64, Legal}, // bit sizes [64, 65[
/// {65, Unsupported}, // bit sizes [65, +inf[
/// });
void setScalarAction(const unsigned Opcode, const unsigned TypeIndex,
const SizeAndActionsVec &SizeAndActions) {
const unsigned OpcodeIdx = Opcode - FirstOp;
SmallVector<SizeAndActionsVec, 1> &Actions = ScalarActions[OpcodeIdx];
setActions(TypeIndex, Actions, SizeAndActions);
}
void setPointerAction(const unsigned Opcode, const unsigned TypeIndex,
const unsigned AddressSpace,
const SizeAndActionsVec &SizeAndActions) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace) ==
AddrSpace2PointerActions[OpcodeIdx].end())
AddrSpace2PointerActions[OpcodeIdx][AddressSpace] = {{}};
SmallVector<SizeAndActionsVec, 1> &Actions =
AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace)->second;
setActions(TypeIndex, Actions, SizeAndActions);
}
/// If an operation on a given vector type (say <M x iN>) isn't explicitly
/// specified, we proceed in 2 stages. First we legalize the underlying scalar
/// (so that there's at least one legal vector with that scalar), then we
/// adjust the number of elements in the vector so that it is legal. The
/// desired action in the first step is controlled by this function.
void setScalarInVectorAction(const unsigned Opcode, const unsigned TypeIndex,
const SizeAndActionsVec &SizeAndActions) {
unsigned OpcodeIdx = Opcode - FirstOp;
SmallVector<SizeAndActionsVec, 1> &Actions =
ScalarInVectorActions[OpcodeIdx];
setActions(TypeIndex, Actions, SizeAndActions);
}
/// See also setScalarInVectorAction.
/// This function let's you specify the number of elements in a vector that
/// are legal for a legal element size.
void setVectorNumElementAction(const unsigned Opcode,
const unsigned TypeIndex,
const unsigned ElementSize,
const SizeAndActionsVec &SizeAndActions) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (NumElements2Actions[OpcodeIdx].find(ElementSize) ==
NumElements2Actions[OpcodeIdx].end())
NumElements2Actions[OpcodeIdx][ElementSize] = {{}};
SmallVector<SizeAndActionsVec, 1> &Actions =
NumElements2Actions[OpcodeIdx].find(ElementSize)->second;
setActions(TypeIndex, Actions, SizeAndActions);
}
/// A partial SizeAndActionsVec potentially doesn't cover all bit sizes,
/// i.e. it's OK if it doesn't start from size 1.
static void checkPartialSizeAndActionsVector(const SizeAndActionsVec& v) {
using namespace LegalizeActions;
#ifndef NDEBUG
// The sizes should be in increasing order
int prev_size = -1;
for(auto SizeAndAction: v) {
assert(SizeAndAction.first > prev_size);
prev_size = SizeAndAction.first;
}
// - for every Widen action, there should be a larger bitsize that
// can be legalized towards (e.g. Legal, Lower, Libcall or Custom
// action).
// - for every Narrow action, there should be a smaller bitsize that
// can be legalized towards.
int SmallestNarrowIdx = -1;
int LargestWidenIdx = -1;
int SmallestLegalizableToSameSizeIdx = -1;
int LargestLegalizableToSameSizeIdx = -1;
for(size_t i=0; i<v.size(); ++i) {
switch (v[i].second) {
case FewerElements:
case NarrowScalar:
if (SmallestNarrowIdx == -1)
SmallestNarrowIdx = i;
break;
case WidenScalar:
case MoreElements:
LargestWidenIdx = i;
break;
case Unsupported:
break;
default:
if (SmallestLegalizableToSameSizeIdx == -1)
SmallestLegalizableToSameSizeIdx = i;
LargestLegalizableToSameSizeIdx = i;
}
}
if (SmallestNarrowIdx != -1) {
assert(SmallestLegalizableToSameSizeIdx != -1);
assert(SmallestNarrowIdx > SmallestLegalizableToSameSizeIdx);
}
if (LargestWidenIdx != -1)
assert(LargestWidenIdx < LargestLegalizableToSameSizeIdx);
#endif
}
/// A full SizeAndActionsVec must cover all bit sizes, i.e. must start with
/// from size 1.
static void checkFullSizeAndActionsVector(const SizeAndActionsVec& v) {
#ifndef NDEBUG
// Data structure invariant: The first bit size must be size 1.
assert(v.size() >= 1);
assert(v[0].first == 1);
checkPartialSizeAndActionsVector(v);
#endif
}
/// Sets actions for all bit sizes on a particular generic opcode, type
/// index and scalar or pointer type.
void setActions(unsigned TypeIndex,
SmallVector<SizeAndActionsVec, 1> &Actions,
const SizeAndActionsVec &SizeAndActions) {
checkFullSizeAndActionsVector(SizeAndActions);
if (Actions.size() <= TypeIndex)
Actions.resize(TypeIndex + 1);
Actions[TypeIndex] = SizeAndActions;
}
static SizeAndAction findAction(const SizeAndActionsVec &Vec,
const uint32_t Size);
/// Returns the next action needed to get the scalar or pointer type closer
/// to being legal
/// E.g. findLegalAction({G_REM, 13}) should return
/// (WidenScalar, 32). After that, findLegalAction({G_REM, 32}) will
/// probably be called, which should return (Lower, 32).
/// This is assuming the setScalarAction on G_REM was something like:
/// setScalarAction(G_REM, 0,
/// {{1, WidenScalar}, // bit sizes [ 1, 31[
/// {32, Lower}, // bit sizes [32, 33[
/// {33, NarrowScalar} // bit sizes [65, +inf[
/// });
std::pair<LegalizeAction, LLT>
findScalarLegalAction(const InstrAspect &Aspect) const;
/// Returns the next action needed towards legalizing the vector type.
std::pair<LegalizeAction, LLT>
findVectorLegalAction(const InstrAspect &Aspect) const;
static const int FirstOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START;
static const int LastOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
// Data structures used temporarily during construction of legality data:
using TypeMap = DenseMap<LLT, LegalizeAction>;
SmallVector<TypeMap, 1> SpecifiedActions[LastOp - FirstOp + 1];
SmallVector<SizeChangeStrategy, 1>
ScalarSizeChangeStrategies[LastOp - FirstOp + 1];
SmallVector<SizeChangeStrategy, 1>
VectorElementSizeChangeStrategies[LastOp - FirstOp + 1];
bool TablesInitialized;
// Data structures used by getAction:
SmallVector<SizeAndActionsVec, 1> ScalarActions[LastOp - FirstOp + 1];
SmallVector<SizeAndActionsVec, 1> ScalarInVectorActions[LastOp - FirstOp + 1];
std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
AddrSpace2PointerActions[LastOp - FirstOp + 1];
std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
NumElements2Actions[LastOp - FirstOp + 1];
LegalizeRuleSet RulesForOpcode[LastOp - FirstOp + 1];
};
#ifndef NDEBUG
/// Checks that MIR is fully legal, returns an illegal instruction if it's not,
/// nullptr otherwise
const MachineInstr *machineFunctionIsIllegal(const MachineFunction &MF);
#endif
} // end namespace llvm.
#endif // LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H