#include <cassert>
#include <tuple>
#include <utility>
+#include <unordered_map>
namespace llvm {
}
}
+ typedef std::pair<uint16_t, LegalizeAction> SizeAndAction;
+ typedef std::vector<SizeAndAction> SizeAndActionsVec;
+ 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;
- unsigned Opcode = Aspect.Opcode - FirstOp;
- if (Actions[Opcode].size() <= Aspect.Idx)
- Actions[Opcode].resize(Aspect.Idx + 1);
- Actions[Aspect.Opcode - FirstOp][Aspect.Idx][Aspect.Type] = Action;
+ 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;
}
- /// 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(unsigned Opcode, LLT ScalarTy,
- LegalizeAction Action) {
- assert(!ScalarTy.isVector());
- ScalarInVectorActions[std::make_pair(Opcode, ScalarTy)] = 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) {
+ 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) {
+ 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) {
+ return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
+ Unsupported);
+ }
+
+ static SizeAndActionsVec
+ narrowToSmallerAndUnsupportedIfTooSmall(const SizeAndActionsVec &v) {
+ return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
+ Unsupported);
+ }
+
+ static SizeAndActionsVec
+ narrowToSmallerAndWidenToSmallest(const SizeAndActionsVec &v) {
+ 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) {
+ 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);
+
/// Determine what action should be taken to legalize the given generic
/// instruction opcode, type-index and type. Requires computeTables to have
/// been called.
std::tuple<LegalizeAction, unsigned, LLT>
getAction(const MachineInstr &MI, const MachineRegisterInfo &MRI) const;
- /// Iterate the given function (typically something like doubling the width)
- /// on Ty until we find a legal type for this operation.
- Optional<LLT> findLegalizableSize(const InstrAspect &Aspect,
- function_ref<LLT(LLT)> NextType) const {
- if (Aspect.Idx >= Actions[Aspect.Opcode - FirstOp].size())
- return None;
-
- LegalizeAction Action;
- const TypeMap &Map = Actions[Aspect.Opcode - FirstOp][Aspect.Idx];
- LLT Ty = Aspect.Type;
- do {
- Ty = NextType(Ty);
- auto ActionIt = Map.find(Ty);
- if (ActionIt == Map.end()) {
- auto DefaultIt = DefaultActions.find(Aspect.Opcode);
- if (DefaultIt == DefaultActions.end())
- return None;
- Action = DefaultIt->second;
- } else
- Action = ActionIt->second;
- } while (needsLegalizingToDifferentSize(Action));
- return Ty;
- }
-
- /// Find what type it's actually OK to perform the given operation on, given
- /// the general approach we've decided to take.
- Optional<LLT> findLegalType(const InstrAspect &Aspect, LegalizeAction Action) const;
-
- std::pair<LegalizeAction, LLT> findLegalAction(const InstrAspect &Aspect,
- LegalizeAction Action) const {
- auto LegalType = findLegalType(Aspect, Action);
- if (!LegalType)
- return std::make_pair(LegalizeAction::Unsupported, LLT());
- return std::make_pair(Action, *LegalType);
- }
-
- /// Find the specified \p Aspect in the primary (explicitly set) Actions
- /// table. Returns either the action the target requested or NotFound if there
- /// was no setAction call.
- LegalizeAction findInActions(const InstrAspect &Aspect) const {
- if (Aspect.Opcode < FirstOp || Aspect.Opcode > LastOp)
- return NotFound;
- if (Aspect.Idx >= Actions[Aspect.Opcode - FirstOp].size())
- return NotFound;
- const TypeMap &Map = Actions[Aspect.Opcode - FirstOp][Aspect.Idx];
- auto ActionIt = Map.find(Aspect.Type);
- if (ActionIt == Map.end())
- return NotFound;
-
- return ActionIt->second;
- }
-
bool isLegal(const MachineInstr &MI, const MachineRegisterInfo &MRI) const;
virtual bool legalizeCustom(MachineInstr &MI,
MachineIRBuilder &MIRBuilder) const;
private:
- static const int FirstOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START;
- static const int LastOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
+ /// 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_GEP, 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) {
+#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
+ }
- using TypeMap = DenseMap<LLT, LegalizeAction>;
- using SIVActionMap = DenseMap<std::pair<unsigned, LLT>, LegalizeAction>;
+ /// 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;
+ }
- SmallVector<TypeMap, 1> Actions[LastOp - FirstOp + 1];
- SIVActionMap ScalarInVectorActions;
- DenseMap<std::pair<unsigned, LLT>, uint16_t> MaxLegalVectorElts;
- DenseMap<unsigned, LegalizeAction> DefaultActions;
+ 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;
- bool TablesInitialized = false;
+ // Data structures used temporarily during construction of legality data:
+ typedef DenseMap<LLT, LegalizeAction> TypeMap;
+ 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];
};
-} // end namespace llvm
+} // end namespace llvm.
#endif // LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H
return scalar(getScalarSizeInBits());
}
- /// Get a low-level type with half the size of the original, by halving the
- /// size of the scalar type involved. For example `s32` will become `s16`,
- /// `<2 x s32>` will become `<2 x s16>`.
- LLT halfScalarSize() const {
- assert(!IsPointer && getScalarSizeInBits() > 1 &&
- getScalarSizeInBits() % 2 == 0 && "cannot half size of this type");
- return LLT{/*isPointer=*/false, IsVector ? true : false,
- IsVector ? getNumElements() : (uint16_t)0,
- getScalarSizeInBits() / 2, /*AddressSpace=*/0};
- }
-
- /// Get a low-level type with twice the size of the original, by doubling the
- /// size of the scalar type involved. For example `s32` will become `s64`,
- /// `<2 x s32>` will become `<2 x s64>`.
- LLT doubleScalarSize() const {
- assert(!IsPointer && "cannot change size of this type");
- return LLT{/*isPointer=*/false, IsVector ? true : false,
- IsVector ? getNumElements() : (uint16_t)0,
- getScalarSizeInBits() * 2, /*AddressSpace=*/0};
- }
-
- /// Get a low-level type with half the size of the original, by halving the
- /// number of vector elements of the scalar type involved. The source must be
- /// a vector type with an even number of elements. For example `<4 x s32>`
- /// will become `<2 x s32>`, `<2 x s32>` will become `s32`.
- LLT halfElements() const {
- assert(isVector() && getNumElements() % 2 == 0 && "cannot half odd vector");
- if (getNumElements() == 2)
- return scalar(getScalarSizeInBits());
-
- return LLT{/*isPointer=*/false, /*isVector=*/true,
- (uint16_t)(getNumElements() / 2), getScalarSizeInBits(),
- /*AddressSpace=*/0};
- }
-
- /// Get a low-level type with twice the size of the original, by doubling the
- /// number of vector elements of the scalar type involved. The source must be
- /// a vector type. For example `<2 x s32>` will become `<4 x s32>`. Doubling
- /// the number of elements in sN produces <2 x sN>.
- LLT doubleElements() const {
- return LLT{IsPointer ? true : false, /*isVector=*/true,
- (uint16_t)(getNumElements() * 2), getScalarSizeInBits(),
- IsPointer ? getAddressSpace() : 0};
- }
-
void print(raw_ostream &OS) const;
bool operator==(const LLT &RHS) const {
MIRBuilder.setInstr(MI);
+ int64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ int64_t NarrowSize = NarrowTy.getSizeInBits();
+
switch (MI.getOpcode()) {
default:
return UnableToLegalize;
case TargetOpcode::G_IMPLICIT_DEF: {
- int NumParts = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() /
- NarrowTy.getSizeInBits();
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
+ return UnableToLegalize;
+ int NumParts = SizeOp0 / NarrowSize;
SmallVector<unsigned, 2> DstRegs;
for (int i = 0; i < NumParts; ++i) {
return Legalized;
}
case TargetOpcode::G_ADD: {
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
+ return UnableToLegalize;
// Expand in terms of carry-setting/consuming G_ADDE instructions.
- int NumParts = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() /
- NarrowTy.getSizeInBits();
+ int NumParts = SizeOp0 / NarrowTy.getSizeInBits();
SmallVector<unsigned, 2> Src1Regs, Src2Regs, DstRegs;
extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs);
if (TypeIdx != 1)
return UnableToLegalize;
- int64_t NarrowSize = NarrowTy.getSizeInBits();
- int NumParts =
- MRI.getType(MI.getOperand(1).getReg()).getSizeInBits() / NarrowSize;
+ int64_t SizeOp1 = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits();
+ // FIXME: add support for when SizeOp1 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp1 % NarrowSize != 0)
+ return UnableToLegalize;
+ int NumParts = SizeOp1 / NarrowSize;
SmallVector<unsigned, 2> SrcRegs, DstRegs;
SmallVector<uint64_t, 2> Indexes;
return Legalized;
}
case TargetOpcode::G_INSERT: {
- if (TypeIdx != 0)
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
return UnableToLegalize;
- int64_t NarrowSize = NarrowTy.getSizeInBits();
- int NumParts =
- MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() / NarrowSize;
+ int NumParts = SizeOp0 / NarrowSize;
SmallVector<unsigned, 2> SrcRegs, DstRegs;
SmallVector<uint64_t, 2> Indexes;
return Legalized;
}
case TargetOpcode::G_LOAD: {
- unsigned NarrowSize = NarrowTy.getSizeInBits();
- int NumParts =
- MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() / NarrowSize;
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
+ return UnableToLegalize;
+ int NumParts = SizeOp0 / NarrowSize;
LLT OffsetTy = LLT::scalar(
MRI.getType(MI.getOperand(1).getReg()).getScalarSizeInBits());
return Legalized;
}
case TargetOpcode::G_STORE: {
- unsigned NarrowSize = NarrowTy.getSizeInBits();
- int NumParts =
- MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() / NarrowSize;
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
+ return UnableToLegalize;
+ int NumParts = SizeOp0 / NarrowSize;
LLT OffsetTy = LLT::scalar(
MRI.getType(MI.getOperand(1).getReg()).getScalarSizeInBits());
return Legalized;
}
case TargetOpcode::G_CONSTANT: {
- unsigned NarrowSize = NarrowTy.getSizeInBits();
- int NumParts =
- MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() / NarrowSize;
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
+ return UnableToLegalize;
+ int NumParts = SizeOp0 / NarrowSize;
const APInt &Cst = MI.getOperand(1).getCImm()->getValue();
LLVMContext &Ctx = MIRBuilder.getMF().getFunction()->getContext();
// ...
// AN = BinOp<Ty/N> BN, CN
// A = G_MERGE_VALUES A1, ..., AN
- unsigned NarrowSize = NarrowTy.getSizeInBits();
- int NumParts =
- MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() / NarrowSize;
+
+ // FIXME: add support for when SizeOp0 isn't an exact multiple of
+ // NarrowSize.
+ if (SizeOp0 % NarrowSize != 0)
+ return UnableToLegalize;
+ int NumParts = SizeOp0 / NarrowSize;
// List the registers where the destination will be scattered.
SmallVector<unsigned, 2> DstRegs;
case TargetOpcode::G_ADD: {
unsigned NarrowSize = NarrowTy.getSizeInBits();
unsigned DstReg = MI.getOperand(0).getReg();
- int NumParts = MRI.getType(DstReg).getSizeInBits() / NarrowSize;
+ unsigned Size = MRI.getType(DstReg).getSizeInBits();
+ int NumParts = Size / NarrowSize;
+ // FIXME: Don't know how to handle the situation where the small vectors
+ // aren't all the same size yet.
+ if (Size % NarrowSize != 0)
+ return UnableToLegalize;
MIRBuilder.setInstr(MI);
#include "llvm/Support/MathExtras.h"
#include "llvm/Target/TargetOpcodes.h"
#include <algorithm>
-#include <cassert>
-#include <tuple>
-#include <utility>
-
+#include <map>
using namespace llvm;
-LegalizerInfo::LegalizerInfo() {
- DefaultActions[TargetOpcode::G_IMPLICIT_DEF] = NarrowScalar;
-
- // FIXME: these two can be legalized to the fundamental load/store Jakob
- // proposed. Once loads & stores are supported.
- DefaultActions[TargetOpcode::G_ANYEXT] = Legal;
- DefaultActions[TargetOpcode::G_TRUNC] = Legal;
+LegalizerInfo::LegalizerInfo() : TablesInitialized(false) {
+ // Set defaults.
+ // FIXME: these two (G_ANYEXT and G_TRUNC?) can be legalized to the
+ // fundamental load/store Jakob proposed. Once loads & stores are supported.
+ setScalarAction(TargetOpcode::G_ANYEXT, 1, {{1, Legal}});
+ setScalarAction(TargetOpcode::G_ZEXT, 1, {{1, Legal}});
+ setScalarAction(TargetOpcode::G_SEXT, 1, {{1, Legal}});
+ setScalarAction(TargetOpcode::G_TRUNC, 0, {{1, Legal}});
+ setScalarAction(TargetOpcode::G_TRUNC, 1, {{1, Legal}});
- DefaultActions[TargetOpcode::G_INTRINSIC] = Legal;
- DefaultActions[TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS] = Legal;
+ setScalarAction(TargetOpcode::G_INTRINSIC, 0, {{1, Legal}});
+ setScalarAction(TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS, 0, {{1, Legal}});
- DefaultActions[TargetOpcode::G_ADD] = NarrowScalar;
- DefaultActions[TargetOpcode::G_LOAD] = NarrowScalar;
- DefaultActions[TargetOpcode::G_STORE] = NarrowScalar;
- DefaultActions[TargetOpcode::G_OR] = NarrowScalar;
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_IMPLICIT_DEF, 0, narrowToSmallerAndUnsupportedIfTooSmall);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_ADD, 0, widenToLargerTypesAndNarrowToLargest);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_OR, 0, widenToLargerTypesAndNarrowToLargest);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_LOAD, 0, narrowToSmallerAndUnsupportedIfTooSmall);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_STORE, 0, narrowToSmallerAndUnsupportedIfTooSmall);
- DefaultActions[TargetOpcode::G_BRCOND] = WidenScalar;
- DefaultActions[TargetOpcode::G_INSERT] = NarrowScalar;
- DefaultActions[TargetOpcode::G_EXTRACT] = NarrowScalar;
- DefaultActions[TargetOpcode::G_FNEG] = Lower;
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_BRCOND, 0, widenToLargerTypesUnsupportedOtherwise);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_INSERT, 0, narrowToSmallerAndUnsupportedIfTooSmall);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_EXTRACT, 0, narrowToSmallerAndUnsupportedIfTooSmall);
+ setLegalizeScalarToDifferentSizeStrategy(
+ TargetOpcode::G_EXTRACT, 1, narrowToSmallerAndUnsupportedIfTooSmall);
+ setScalarAction(TargetOpcode::G_FNEG, 0, {{1, Lower}});
}
void LegalizerInfo::computeTables() {
- for (unsigned Opcode = 0; Opcode <= LastOp - FirstOp; ++Opcode) {
- for (unsigned Idx = 0, End = Actions[Opcode].size(); Idx != End; ++Idx) {
- for (auto &Action : Actions[Opcode][Idx]) {
- LLT Ty = Action.first;
- if (!Ty.isVector())
- continue;
-
- auto &Entry = MaxLegalVectorElts[std::make_pair(Opcode + FirstOp,
- Ty.getElementType())];
- Entry = std::max(Entry, Ty.getNumElements());
+ assert(TablesInitialized == false);
+
+ for (unsigned OpcodeIdx = 0; OpcodeIdx <= LastOp - FirstOp; ++OpcodeIdx) {
+ const unsigned Opcode = FirstOp + OpcodeIdx;
+ for (unsigned TypeIdx = 0; TypeIdx != SpecifiedActions[OpcodeIdx].size();
+ ++TypeIdx) {
+ // 0. Collect information specified through the setAction API, i.e.
+ // for specific bit sizes.
+ // For scalar types:
+ SizeAndActionsVec ScalarSpecifiedActions;
+ // For pointer types:
+ std::map<uint16_t, SizeAndActionsVec> AddressSpace2SpecifiedActions;
+ // For vector types:
+ std::map<uint16_t, SizeAndActionsVec> ElemSize2SpecifiedActions;
+ for (auto LLT2Action : SpecifiedActions[OpcodeIdx][TypeIdx]) {
+ const LLT Type = LLT2Action.first;
+ const LegalizeAction Action = LLT2Action.second;
+
+ auto SizeAction = std::make_pair(Type.getSizeInBits(), Action);
+ if (Type.isPointer())
+ AddressSpace2SpecifiedActions[Type.getAddressSpace()].push_back(
+ SizeAction);
+ else if (Type.isVector())
+ ElemSize2SpecifiedActions[Type.getElementType().getSizeInBits()]
+ .push_back(SizeAction);
+ else
+ ScalarSpecifiedActions.push_back(SizeAction);
+ }
+
+ // 1. Handle scalar types
+ {
+ // Decide how to handle bit sizes for which no explicit specification
+ // was given.
+ SizeChangeStrategy S = &unsupportedForDifferentSizes;
+ if (TypeIdx < ScalarSizeChangeStrategies[OpcodeIdx].size() &&
+ ScalarSizeChangeStrategies[OpcodeIdx][TypeIdx] != nullptr)
+ S = ScalarSizeChangeStrategies[OpcodeIdx][TypeIdx];
+ std::sort(ScalarSpecifiedActions.begin(), ScalarSpecifiedActions.end());
+ checkPartialSizeAndActionsVector(ScalarSpecifiedActions);
+ setScalarAction(Opcode, TypeIdx, S(ScalarSpecifiedActions));
}
+
+ // 2. Handle pointer types
+ for (auto PointerSpecifiedActions : AddressSpace2SpecifiedActions) {
+ std::sort(PointerSpecifiedActions.second.begin(),
+ PointerSpecifiedActions.second.end());
+ checkPartialSizeAndActionsVector(PointerSpecifiedActions.second);
+ // For pointer types, we assume that there isn't a meaningfull way
+ // to change the number of bits used in the pointer.
+ setPointerAction(
+ Opcode, TypeIdx, PointerSpecifiedActions.first,
+ unsupportedForDifferentSizes(PointerSpecifiedActions.second));
+ }
+
+ // 3. Handle vector types
+ SizeAndActionsVec ElementSizesSeen;
+ for (auto VectorSpecifiedActions : ElemSize2SpecifiedActions) {
+ std::sort(VectorSpecifiedActions.second.begin(),
+ VectorSpecifiedActions.second.end());
+ const uint16_t ElementSize = VectorSpecifiedActions.first;
+ ElementSizesSeen.push_back({ElementSize, Legal});
+ checkPartialSizeAndActionsVector(VectorSpecifiedActions.second);
+ // For vector types, we assume that the best way to adapt the number
+ // of elements is to the next larger number of elements type for which
+ // the vector type is legal, unless there is no such type. In that case,
+ // legalize towards a vector type with a smaller number of elements.
+ SizeAndActionsVec NumElementsActions;
+ for (SizeAndAction BitsizeAndAction : VectorSpecifiedActions.second) {
+ assert(BitsizeAndAction.first % ElementSize == 0);
+ const uint16_t NumElements = BitsizeAndAction.first / ElementSize;
+ NumElementsActions.push_back({NumElements, BitsizeAndAction.second});
+ }
+ setVectorNumElementAction(
+ Opcode, TypeIdx, ElementSize,
+ moreToWiderTypesAndLessToWidest(NumElementsActions));
+ }
+ std::sort(ElementSizesSeen.begin(), ElementSizesSeen.end());
+ SizeChangeStrategy VectorElementSizeChangeStrategy =
+ &unsupportedForDifferentSizes;
+ if (TypeIdx < VectorElementSizeChangeStrategies[OpcodeIdx].size() &&
+ VectorElementSizeChangeStrategies[OpcodeIdx][TypeIdx] != nullptr)
+ VectorElementSizeChangeStrategy =
+ VectorElementSizeChangeStrategies[OpcodeIdx][TypeIdx];
+ setScalarInVectorAction(
+ Opcode, TypeIdx, VectorElementSizeChangeStrategy(ElementSizesSeen));
}
}
Aspect.Opcode == TargetOpcode::G_UNMERGE_VALUES)
return std::make_pair(Legal, Aspect.Type);
- LLT Ty = Aspect.Type;
- LegalizeAction Action = findInActions(Aspect);
- // LegalizerHelper is not able to handle non-power-of-2 types right now, so do
- // not try to legalize them unless they are marked as Legal or Custom.
- // FIXME: This is a temporary hack until the general non-power-of-2
- // legalization works.
- if (!isPowerOf2_64(Ty.getSizeInBits()) &&
- !(Action == Legal || Action == Custom))
- return std::make_pair(Unsupported, LLT());
-
- if (Action != NotFound)
- return findLegalAction(Aspect, Action);
-
- unsigned Opcode = Aspect.Opcode;
- if (!Ty.isVector()) {
- auto DefaultAction = DefaultActions.find(Aspect.Opcode);
- if (DefaultAction != DefaultActions.end() && DefaultAction->second == Legal)
- return std::make_pair(Legal, Ty);
-
- if (DefaultAction != DefaultActions.end() && DefaultAction->second == Lower)
- return std::make_pair(Lower, Ty);
-
- if (DefaultAction == DefaultActions.end() ||
- DefaultAction->second != NarrowScalar)
- return std::make_pair(Unsupported, LLT());
- return findLegalAction(Aspect, NarrowScalar);
- }
-
- LLT EltTy = Ty.getElementType();
- int NumElts = Ty.getNumElements();
-
- auto ScalarAction = ScalarInVectorActions.find(std::make_pair(Opcode, EltTy));
- if (ScalarAction != ScalarInVectorActions.end() &&
- ScalarAction->second != Legal)
- 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(Aspect, MoreElements);
-
- if (MaxLegalElts == 0) {
- // Scalarize if there's no legal vector type, which is just a special case
- // of FewerElements.
- return std::make_pair(FewerElements, EltTy);
- }
-
- return findLegalAction(Aspect, FewerElements);
+ if (Aspect.Type.isScalar() || Aspect.Type.isPointer())
+ return findScalarLegalAction(Aspect);
+ assert(Aspect.Type.isVector());
+ return findVectorLegalAction(Aspect);
}
std::tuple<LegalizerInfo::LegalizeAction, unsigned, LLT>
LegalizerInfo::getAction(const MachineInstr &MI,
const MachineRegisterInfo &MRI) const {
SmallBitVector SeenTypes(8);
- const MCInstrDesc &MCID = MI.getDesc();
- const MCOperandInfo *OpInfo = MCID.OpInfo;
- for (unsigned i = 0, e = MCID.getNumOperands(); i != e; ++i) {
+ const MCOperandInfo *OpInfo = MI.getDesc().OpInfo;
+ // FIXME: probably we'll need to cache the results here somehow?
+ for (unsigned i = 0; i < MI.getDesc().getNumOperands(); ++i) {
if (!OpInfo[i].isGenericType())
continue;
- // We don't want to repeatedly check the same operand index, that
- // could get expensive.
+ // We must only record actions once for each TypeIdx; otherwise we'd
+ // try to legalize operands multiple times down the line.
unsigned TypeIdx = OpInfo[i].getGenericTypeIndex();
if (SeenTypes[TypeIdx])
continue;
return std::get<0>(getAction(MI, MRI)) == Legal;
}
-Optional<LLT> LegalizerInfo::findLegalType(const InstrAspect &Aspect,
- LegalizeAction Action) const {
- switch(Action) {
- default:
- llvm_unreachable("Cannot find legal type");
+bool LegalizerInfo::legalizeCustom(MachineInstr &MI, MachineRegisterInfo &MRI,
+ MachineIRBuilder &MIRBuilder) const {
+ return false;
+}
+
+LegalizerInfo::SizeAndActionsVec
+LegalizerInfo::increaseToLargerTypesAndDecreaseToLargest(
+ const SizeAndActionsVec &v, LegalizeAction IncreaseAction,
+ LegalizeAction DecreaseAction) {
+ SizeAndActionsVec result;
+ unsigned LargestSizeSoFar = 0;
+ if (v.size() >= 1 && v[0].first != 1)
+ result.push_back({1, IncreaseAction});
+ for (size_t i = 0; i < v.size(); ++i) {
+ result.push_back(v[i]);
+ LargestSizeSoFar = v[i].first;
+ if (i + 1 < v.size() && v[i + 1].first != v[i].first + 1) {
+ result.push_back({LargestSizeSoFar + 1, IncreaseAction});
+ LargestSizeSoFar = v[i].first + 1;
+ }
+ }
+ result.push_back({LargestSizeSoFar + 1, DecreaseAction});
+ return result;
+}
+
+LegalizerInfo::SizeAndActionsVec
+LegalizerInfo::decreaseToSmallerTypesAndIncreaseToSmallest(
+ const SizeAndActionsVec &v, LegalizeAction DecreaseAction,
+ LegalizeAction IncreaseAction) {
+ SizeAndActionsVec result;
+ if (v.size() == 0 || v[0].first != 1)
+ result.push_back({1, IncreaseAction});
+ for (size_t i = 0; i < v.size(); ++i) {
+ result.push_back(v[i]);
+ if (i + 1 == v.size() || v[i + 1].first != v[i].first + 1) {
+ result.push_back({v[i].first + 1, DecreaseAction});
+ }
+ }
+ return result;
+}
+
+LegalizerInfo::SizeAndAction
+LegalizerInfo::findAction(const SizeAndActionsVec &Vec, const uint32_t Size) {
+ assert(Size >= 1);
+ // Find the last element in Vec that has a bitsize equal to or smaller than
+ // the requested bit size.
+ // That is the element just before the first element that is bigger than Size.
+ auto VecIt = std::upper_bound(
+ Vec.begin(), Vec.end(), Size,
+ [](const uint32_t Size, const SizeAndAction lhs) -> bool {
+ return Size < lhs.first;
+ });
+ assert(VecIt != Vec.begin() && "Does Vec not start with size 1?");
+ --VecIt;
+ int VecIdx = VecIt - Vec.begin();
+
+ LegalizeAction Action = Vec[VecIdx].second;
+ switch (Action) {
case Legal:
case Lower:
case Libcall:
case Custom:
- return Aspect.Type;
+ return {Size, Action};
+ case FewerElements:
+ // FIXME: is this special case still needed and correct?
+ // Special case for scalarization:
+ if (Vec == SizeAndActionsVec({{1, FewerElements}}))
+ return {1, FewerElements};
case NarrowScalar: {
- return findLegalizableSize(
- Aspect, [&](LLT Ty) -> LLT { return Ty.halfScalarSize(); });
- }
- case WidenScalar: {
- return findLegalizableSize(Aspect, [&](LLT Ty) -> LLT {
- return Ty.getSizeInBits() < 8 ? LLT::scalar(8) : Ty.doubleScalarSize();
- });
- }
- case FewerElements: {
- return findLegalizableSize(
- Aspect, [&](LLT Ty) -> LLT { return Ty.halfElements(); });
+ // The following needs to be a loop, as for now, we do allow needing to
+ // go over "Unsupported" bit sizes before finding a legalizable bit size.
+ // e.g. (s8, WidenScalar), (s9, Unsupported), (s32, Legal). if Size==8,
+ // we need to iterate over s9, and then to s32 to return (s32, Legal).
+ // If we want to get rid of the below loop, we should have stronger asserts
+ // when building the SizeAndActionsVecs, probably not allowing
+ // "Unsupported" unless at the ends of the vector.
+ for (int i = VecIdx - 1; i >= 0; --i)
+ if (!needsLegalizingToDifferentSize(Vec[i].second) &&
+ Vec[i].second != Unsupported)
+ return {Vec[i].first, Action};
+ llvm_unreachable("");
}
+ case WidenScalar:
case MoreElements: {
- return findLegalizableSize(
- Aspect, [&](LLT Ty) -> LLT { return Ty.doubleElements(); });
+ // See above, the following needs to be a loop, at least for now.
+ for (std::size_t i = VecIdx + 1; i < Vec.size(); ++i)
+ if (!needsLegalizingToDifferentSize(Vec[i].second) &&
+ Vec[i].second != Unsupported)
+ return {Vec[i].first, Action};
+ llvm_unreachable("");
}
+ case Unsupported:
+ return {Size, Unsupported};
+ case NotFound:
+ llvm_unreachable("NotFound");
}
}
-bool LegalizerInfo::legalizeCustom(MachineInstr &MI,
- MachineRegisterInfo &MRI,
- MachineIRBuilder &MIRBuilder) const {
- return false;
+std::pair<LegalizerInfo::LegalizeAction, LLT>
+LegalizerInfo::findScalarLegalAction(const InstrAspect &Aspect) const {
+ assert(Aspect.Type.isScalar() || Aspect.Type.isPointer());
+ if (Aspect.Opcode < FirstOp || Aspect.Opcode > LastOp)
+ return {NotFound, LLT()};
+ const unsigned OpcodeIdx = Aspect.Opcode - FirstOp;
+ if (Aspect.Type.isPointer() &&
+ AddrSpace2PointerActions[OpcodeIdx].find(Aspect.Type.getAddressSpace()) ==
+ AddrSpace2PointerActions[OpcodeIdx].end()) {
+ return {NotFound, LLT()};
+ }
+ const SmallVector<SizeAndActionsVec, 1> &Actions =
+ Aspect.Type.isPointer()
+ ? AddrSpace2PointerActions[OpcodeIdx]
+ .find(Aspect.Type.getAddressSpace())
+ ->second
+ : ScalarActions[OpcodeIdx];
+ if (Aspect.Idx >= Actions.size())
+ return {NotFound, LLT()};
+ const SizeAndActionsVec &Vec = Actions[Aspect.Idx];
+ // FIXME: speed up this search, e.g. by using a results cache for repeated
+ // queries?
+ auto SizeAndAction = findAction(Vec, Aspect.Type.getSizeInBits());
+ return {SizeAndAction.second,
+ Aspect.Type.isScalar() ? LLT::scalar(SizeAndAction.first)
+ : LLT::pointer(Aspect.Type.getAddressSpace(),
+ SizeAndAction.first)};
+}
+
+std::pair<LegalizerInfo::LegalizeAction, LLT>
+LegalizerInfo::findVectorLegalAction(const InstrAspect &Aspect) const {
+ assert(Aspect.Type.isVector());
+ // First legalize the vector element size, then legalize the number of
+ // lanes in the vector.
+ if (Aspect.Opcode < FirstOp || Aspect.Opcode > LastOp)
+ return {NotFound, Aspect.Type};
+ const unsigned OpcodeIdx = Aspect.Opcode - FirstOp;
+ const unsigned TypeIdx = Aspect.Idx;
+ if (TypeIdx >= ScalarInVectorActions[OpcodeIdx].size())
+ return {NotFound, Aspect.Type};
+ const SizeAndActionsVec &ElemSizeVec =
+ ScalarInVectorActions[OpcodeIdx][TypeIdx];
+
+ LLT IntermediateType;
+ auto ElementSizeAndAction =
+ findAction(ElemSizeVec, Aspect.Type.getScalarSizeInBits());
+ IntermediateType =
+ LLT::vector(Aspect.Type.getNumElements(), ElementSizeAndAction.first);
+ if (ElementSizeAndAction.second != Legal)
+ return {ElementSizeAndAction.second, IntermediateType};
+
+ auto i = NumElements2Actions[OpcodeIdx].find(
+ IntermediateType.getScalarSizeInBits());
+ if (i == NumElements2Actions[OpcodeIdx].end()) {
+ return {NotFound, IntermediateType};
+ }
+ const SizeAndActionsVec &NumElementsVec = (*i).second[TypeIdx];
+ auto NumElementsAndAction =
+ findAction(NumElementsVec, IntermediateType.getNumElements());
+ return {NumElementsAndAction.second,
+ LLT::vector(NumElementsAndAction.first,
+ IntermediateType.getScalarSizeInBits())};
}
assert(isScalar() && "unexpected type");
OS << "s" << getScalarSizeInBits();
} else
- llvm_unreachable("trying to print an invalid type");
+ OS << "LLT_invalid";
}
const constexpr LLT::BitFieldInfo LLT::ScalarSizeFieldInfo;
using namespace llvm;
+/// FIXME: The following static functions are SizeChangeStrategy functions
+/// that are meant to temporarily mimic the behaviour of the old legalization
+/// based on doubling/halving non-legal types as closely as possible. This is
+/// not entirly possible as only legalizing the types that are exactly a power
+/// of 2 times the size of the legal types would require specifying all those
+/// sizes explicitly.
+/// In practice, not specifying those isn't a problem, and the below functions
+/// should disappear quickly as we add support for legalizing non-power-of-2
+/// sized types further.
+static void
+addAndInterleaveWithUnsupported(LegalizerInfo::SizeAndActionsVec &result,
+ const LegalizerInfo::SizeAndActionsVec &v) {
+ for (unsigned i = 0; i < v.size(); ++i) {
+ result.push_back(v[i]);
+ if (i + 1 < v[i].first && i + 1 < v.size() &&
+ v[i + 1].first != v[i].first + 1)
+ result.push_back({v[i].first + 1, LegalizerInfo::Unsupported});
+ }
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1_narrow_128_ToLargest(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 2);
+ LegalizerInfo::SizeAndActionsVec result = {{1, LegalizerInfo::WidenScalar},
+ {2, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ assert(Largest + 1 < 128);
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ result.push_back({128, LegalizerInfo::NarrowScalar});
+ result.push_back({129, LegalizerInfo::Unsupported});
+ return result;
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_16(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 17);
+ LegalizerInfo::SizeAndActionsVec result = {{1, LegalizerInfo::Unsupported},
+ {16, LegalizerInfo::WidenScalar},
+ {17, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1_8(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 9);
+ LegalizerInfo::SizeAndActionsVec result = {
+ {1, LegalizerInfo::WidenScalar}, {2, LegalizerInfo::Unsupported},
+ {8, LegalizerInfo::WidenScalar}, {9, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1_8_16(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 17);
+ LegalizerInfo::SizeAndActionsVec result = {
+ {1, LegalizerInfo::WidenScalar}, {2, LegalizerInfo::Unsupported},
+ {8, LegalizerInfo::WidenScalar}, {9, LegalizerInfo::Unsupported},
+ {16, LegalizerInfo::WidenScalar}, {17, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1_8_16_narrowToLargest(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 17);
+ LegalizerInfo::SizeAndActionsVec result = {
+ {1, LegalizerInfo::WidenScalar}, {2, LegalizerInfo::Unsupported},
+ {8, LegalizerInfo::WidenScalar}, {9, LegalizerInfo::Unsupported},
+ {16, LegalizerInfo::WidenScalar}, {17, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::NarrowScalar});
+ return result;
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1_8_16_32(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 33);
+ LegalizerInfo::SizeAndActionsVec result = {
+ {1, LegalizerInfo::WidenScalar}, {2, LegalizerInfo::Unsupported},
+ {8, LegalizerInfo::WidenScalar}, {9, LegalizerInfo::Unsupported},
+ {16, LegalizerInfo::WidenScalar}, {17, LegalizerInfo::Unsupported},
+ {32, LegalizerInfo::WidenScalar}, {33, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
AArch64LegalizerInfo::AArch64LegalizerInfo() {
using namespace TargetOpcode;
const LLT p0 = LLT::pointer(0, 64);
for (auto Ty : {s16, s32, s64, p0})
setAction({G_PHI, Ty}, Legal);
- for (auto Ty : {s1, s8})
- setAction({G_PHI, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(G_PHI, 0, widen_1_8);
for (auto Ty : { s32, s64 })
setAction({G_BSWAP, Ty}, Legal);
for (auto Ty : {s32, s64, v2s32, v4s32, v2s64})
setAction({BinOp, Ty}, Legal);
- for (auto Ty : {s1, s8, s16})
- setAction({BinOp, Ty}, WidenScalar);
+ if (BinOp != G_ADD)
+ setLegalizeScalarToDifferentSizeStrategy(BinOp, 0,
+ widen_1_8_16_narrowToLargest);
}
setAction({G_GEP, p0}, Legal);
setAction({G_GEP, 1, s64}, Legal);
- for (auto Ty : {s1, s8, s16, s32})
- setAction({G_GEP, 1, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(G_GEP, 1, widen_1_8_16_32);
setAction({G_PTR_MASK, p0}, Legal);
for (auto Ty : {s32, s64})
setAction({BinOp, Ty}, Legal);
- for (auto Ty : {s1, s8, s16})
- setAction({BinOp, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(BinOp, 0, widen_1_8_16);
}
for (unsigned BinOp : {G_SREM, G_UREM})
for (auto Ty : { s1, s8, s16, s32, s64 })
setAction({BinOp, Ty}, Lower);
- for (unsigned Op : {G_SMULO, G_UMULO})
- setAction({Op, s64}, Lower);
+ for (unsigned Op : {G_SMULO, G_UMULO}) {
+ setAction({Op, 0, s64}, Lower);
+ setAction({Op, 1, s1}, Legal);
+ }
for (unsigned Op : {G_UADDE, G_USUBE, G_SADDO, G_SSUBO, G_SMULH, G_UMULH}) {
for (auto Ty : { s32, s64 })
setAction({G_INSERT, Ty}, Legal);
setAction({G_INSERT, 1, Ty}, Legal);
}
+ setLegalizeScalarToDifferentSizeStrategy(G_INSERT, 0,
+ widen_1_8_16_narrowToLargest);
for (auto Ty : {s1, s8, s16}) {
- setAction({G_INSERT, Ty}, WidenScalar);
setAction({G_INSERT, 1, Ty}, Legal);
// FIXME: Can't widen the sources because that violates the constraints on
// G_INSERT (It seems entirely reasonable that inputs shouldn't overlap).
for (auto Ty : {s8, s16, s32, s64, p0, v2s32})
setAction({MemOp, Ty}, Legal);
- setAction({MemOp, s1}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(MemOp, 0,
+ widen_1_narrow_128_ToLargest);
// And everything's fine in addrspace 0.
setAction({MemOp, 1, p0}, Legal);
setAction({G_CONSTANT, p0}, Legal);
- for (auto Ty : {s1, s8, s16})
- setAction({TargetOpcode::G_CONSTANT, Ty}, WidenScalar);
-
- setAction({TargetOpcode::G_FCONSTANT, s16}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(G_CONSTANT, 0, widen_1_8_16);
+ setLegalizeScalarToDifferentSizeStrategy(G_FCONSTANT, 0, widen_16);
setAction({G_ICMP, 1, s32}, Legal);
setAction({G_ICMP, 1, s64}, Legal);
setAction({G_ICMP, 1, p0}, Legal);
- for (auto Ty : {s1, s8, s16}) {
- setAction({G_ICMP, Ty}, WidenScalar);
- setAction({G_FCMP, Ty}, WidenScalar);
- setAction({G_ICMP, 1, Ty}, WidenScalar);
- }
+ setLegalizeScalarToDifferentSizeStrategy(G_ICMP, 0, widen_1_8_16);
+ setLegalizeScalarToDifferentSizeStrategy(G_FCMP, 0, widen_1_8_16);
+ setLegalizeScalarToDifferentSizeStrategy(G_ICMP, 1, widen_1_8_16);
setAction({G_ICMP, s32}, Legal);
setAction({G_FCMP, s32}, Legal);
setAction({G_ANYEXT, Ty}, Legal);
}
- for (auto Ty : { s1, s8, s16, s32 }) {
- setAction({G_ZEXT, 1, Ty}, Legal);
- setAction({G_SEXT, 1, Ty}, Legal);
- setAction({G_ANYEXT, 1, Ty}, Legal);
- }
-
// FP conversions
for (auto Ty : { s16, s32 }) {
setAction({G_FPTRUNC, Ty}, Legal);
setAction({G_FPEXT, Ty}, Legal);
}
- for (auto Ty : { s1, s8, s16, s32 })
- setAction({G_TRUNC, Ty}, Legal);
-
- for (auto Ty : { s8, s16, s32, s64 })
- setAction({G_TRUNC, 1, Ty}, Legal);
-
// Conversions
for (auto Ty : { s32, s64 }) {
setAction({G_FPTOSI, 0, Ty}, Legal);
setAction({G_SITOFP, 1, Ty}, Legal);
setAction({G_UITOFP, 1, Ty}, Legal);
}
- for (auto Ty : { s1, s8, s16 }) {
- setAction({G_FPTOSI, 0, Ty}, WidenScalar);
- setAction({G_FPTOUI, 0, Ty}, WidenScalar);
- setAction({G_SITOFP, 1, Ty}, WidenScalar);
- setAction({G_UITOFP, 1, Ty}, WidenScalar);
- }
+ setLegalizeScalarToDifferentSizeStrategy(G_FPTOSI, 0, widen_1_8_16);
+ setLegalizeScalarToDifferentSizeStrategy(G_FPTOUI, 0, widen_1_8_16);
+ setLegalizeScalarToDifferentSizeStrategy(G_SITOFP, 1, widen_1_8_16);
+ setLegalizeScalarToDifferentSizeStrategy(G_UITOFP, 1, widen_1_8_16);
for (auto Ty : { s32, s64 }) {
setAction({G_FPTOSI, 1, Ty}, Legal);
setAction({G_BRINDIRECT, p0}, Legal);
// Select
- for (auto Ty : {s1, s8, s16})
- setAction({G_SELECT, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(G_SELECT, 0, widen_1_8_16);
for (auto Ty : {s32, s64, p0})
setAction({G_SELECT, Ty}, Legal);
using namespace llvm;
+/// FIXME: The following static functions are SizeChangeStrategy functions
+/// that are meant to temporarily mimic the behaviour of the old legalization
+/// based on doubling/halving non-legal types as closely as possible. This is
+/// not entirly possible as only legalizing the types that are exactly a power
+/// of 2 times the size of the legal types would require specifying all those
+/// sizes explicitly.
+/// In practice, not specifying those isn't a problem, and the below functions
+/// should disappear quickly as we add support for legalizing non-power-of-2
+/// sized types further.
+static void
+addAndInterleaveWithUnsupported(LegalizerInfo::SizeAndActionsVec &result,
+ const LegalizerInfo::SizeAndActionsVec &v) {
+ for (unsigned i = 0; i < v.size(); ++i) {
+ result.push_back(v[i]);
+ if (i + 1 < v[i].first && i + 1 < v.size() &&
+ v[i + 1].first != v[i].first + 1)
+ result.push_back({v[i].first + 1, LegalizerInfo::Unsupported});
+ }
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_8_16(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 17);
+ LegalizerInfo::SizeAndActionsVec result = {
+ {1, LegalizerInfo::Unsupported},
+ {8, LegalizerInfo::WidenScalar}, {9, LegalizerInfo::Unsupported},
+ {16, LegalizerInfo::WidenScalar}, {17, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1_8_16(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 17);
+ LegalizerInfo::SizeAndActionsVec result = {
+ {1, LegalizerInfo::WidenScalar}, {2, LegalizerInfo::Unsupported},
+ {8, LegalizerInfo::WidenScalar}, {9, LegalizerInfo::Unsupported},
+ {16, LegalizerInfo::WidenScalar}, {17, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
static bool AEABI(const ARMSubtarget &ST) {
return ST.isTargetAEABI() || ST.isTargetGNUAEABI() || ST.isTargetMuslAEABI();
}
}
for (unsigned Op : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR}) {
- for (auto Ty : {s1, s8, s16})
- setAction({Op, Ty}, WidenScalar);
+ if (Op != G_ADD)
+ setLegalizeScalarToDifferentSizeStrategy(
+ Op, 0, widenToLargerTypesUnsupportedOtherwise);
setAction({Op, s32}, Legal);
}
for (unsigned Op : {G_SDIV, G_UDIV}) {
- for (auto Ty : {s8, s16})
- setAction({Op, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(Op, 0,
+ widenToLargerTypesUnsupportedOtherwise);
if (ST.hasDivideInARMMode())
setAction({Op, s32}, Legal);
else
}
for (unsigned Op : {G_SREM, G_UREM}) {
- for (auto Ty : {s8, s16})
- setAction({Op, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(Op, 0, widen_8_16);
if (ST.hasDivideInARMMode())
setAction({Op, s32}, Lower);
else if (AEABI(ST))
setAction({Op, s32}, Libcall);
}
- for (unsigned Op : {G_SEXT, G_ZEXT}) {
+ for (unsigned Op : {G_SEXT, G_ZEXT, G_ANYEXT}) {
setAction({Op, s32}, Legal);
- for (auto Ty : {s1, s8, s16})
- setAction({Op, 1, Ty}, Legal);
}
for (unsigned Op : {G_ASHR, G_LSHR, G_SHL})
setAction({G_BRCOND, s1}, Legal);
setAction({G_CONSTANT, s32}, Legal);
- for (auto Ty : {s1, s8, s16})
- setAction({G_CONSTANT, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(G_CONSTANT, 0, widen_1_8_16);
setAction({G_ICMP, s1}, Legal);
- for (auto Ty : {s8, s16})
- setAction({G_ICMP, 1, Ty}, WidenScalar);
+ setLegalizeScalarToDifferentSizeStrategy(G_ICMP, 1,
+ widenToLargerTypesUnsupportedOtherwise);
for (auto Ty : {s32, p0})
setAction({G_ICMP, 1, Ty}, Legal);
using namespace llvm;
using namespace TargetOpcode;
+/// FIXME: The following static functions are SizeChangeStrategy functions
+/// that are meant to temporarily mimic the behaviour of the old legalization
+/// based on doubling/halving non-legal types as closely as possible. This is
+/// not entirly possible as only legalizing the types that are exactly a power
+/// of 2 times the size of the legal types would require specifying all those
+/// sizes explicitly.
+/// In practice, not specifying those isn't a problem, and the below functions
+/// should disappear quickly as we add support for legalizing non-power-of-2
+/// sized types further.
+static void
+addAndInterleaveWithUnsupported(LegalizerInfo::SizeAndActionsVec &result,
+ const LegalizerInfo::SizeAndActionsVec &v) {
+ for (unsigned i = 0; i < v.size(); ++i) {
+ result.push_back(v[i]);
+ if (i + 1 < v[i].first && i + 1 < v.size() &&
+ v[i + 1].first != v[i].first + 1)
+ result.push_back({v[i].first + 1, LegalizerInfo::Unsupported});
+ }
+}
+
+static LegalizerInfo::SizeAndActionsVec
+widen_1(const LegalizerInfo::SizeAndActionsVec &v) {
+ assert(v.size() >= 1);
+ assert(v[0].first > 1);
+ LegalizerInfo::SizeAndActionsVec result = {{1, LegalizerInfo::WidenScalar},
+ {2, LegalizerInfo::Unsupported}};
+ addAndInterleaveWithUnsupported(result, v);
+ auto Largest = result.back().first;
+ result.push_back({Largest + 1, LegalizerInfo::Unsupported});
+ return result;
+}
+
X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
const X86TargetMachine &TM)
: Subtarget(STI), TM(TM) {
setLegalizerInfoAVX512DQ();
setLegalizerInfoAVX512BW();
+ setLegalizeScalarToDifferentSizeStrategy(G_PHI, 0, widen_1);
+ for (unsigned BinOp : {G_SUB, G_MUL, G_AND, G_OR, G_XOR})
+ setLegalizeScalarToDifferentSizeStrategy(BinOp, 0, widen_1);
+ for (unsigned MemOp : {G_LOAD, G_STORE})
+ setLegalizeScalarToDifferentSizeStrategy(MemOp, 0,
+ narrowToSmallerAndWidenToSmallest);
+ setLegalizeScalarToDifferentSizeStrategy(
+ G_GEP, 1, widenToLargerTypesUnsupportedOtherwise);
+ setLegalizeScalarToDifferentSizeStrategy(
+ G_CONSTANT, 0, widenToLargerTypesAndNarrowToLargest);
+
computeTables();
}
const LLT s8 = LLT::scalar(8);
const LLT s16 = LLT::scalar(16);
const LLT s32 = LLT::scalar(32);
- const LLT s64 = LLT::scalar(64);
for (auto Ty : {p0, s1, s8, s16, s32})
setAction({G_IMPLICIT_DEF, Ty}, Legal);
for (auto Ty : {s8, s16, s32, p0})
setAction({G_PHI, Ty}, Legal);
- setAction({G_PHI, s1}, WidenScalar);
-
- for (unsigned BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR}) {
+ for (unsigned BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR})
for (auto Ty : {s8, s16, s32})
setAction({BinOp, Ty}, Legal);
- setAction({BinOp, s1}, WidenScalar);
- }
-
for (unsigned Op : {G_UADDE}) {
setAction({Op, s32}, Legal);
setAction({Op, 1, s1}, Legal);
for (auto Ty : {s8, s16, s32, p0})
setAction({MemOp, Ty}, Legal);
- setAction({MemOp, s1}, WidenScalar);
// And everything's fine in addrspace 0.
setAction({MemOp, 1, p0}, Legal);
}
setAction({G_GEP, p0}, Legal);
setAction({G_GEP, 1, s32}, Legal);
- for (auto Ty : {s1, s8, s16})
- setAction({G_GEP, 1, Ty}, WidenScalar);
-
// Control-flow
setAction({G_BRCOND, s1}, Legal);
for (auto Ty : {s8, s16, s32, p0})
setAction({TargetOpcode::G_CONSTANT, Ty}, Legal);
- setAction({TargetOpcode::G_CONSTANT, s1}, WidenScalar);
- setAction({TargetOpcode::G_CONSTANT, s64}, NarrowScalar);
-
// Extensions
for (auto Ty : {s8, s16, s32}) {
setAction({G_ZEXT, Ty}, Legal);
setAction({G_ANYEXT, Ty}, Legal);
}
- for (auto Ty : {s1, s8, s16}) {
- setAction({G_ZEXT, 1, Ty}, Legal);
- setAction({G_SEXT, 1, Ty}, Legal);
- setAction({G_ANYEXT, 1, Ty}, Legal);
- }
-
// Comparison
setAction({G_ICMP, s1}, Legal);
if (!Subtarget.is64Bit())
return;
- const LLT s32 = LLT::scalar(32);
const LLT s64 = LLT::scalar(64);
setAction({G_IMPLICIT_DEF, s64}, Legal);
// Extensions
for (unsigned extOp : {G_ZEXT, G_SEXT, G_ANYEXT}) {
setAction({extOp, s64}, Legal);
- setAction({extOp, 1, s32}, Legal);
}
// Comparison
%vec = load <2 x i16*>, <2 x i16*>* undef
br label %block
}
+
+; FALLBACK-WITH-REPORT-ERR-G_IMPLICIT_DEF-LEGALIZABLE: (FIXME: this is what is expected once we can legalize non-pow-of-2 G_IMPLICIT_DEF) remark: <unknown>:0:0: unable to legalize instruction: %vreg1<def>(s96) = G_INSERT %vreg2, %vreg0, 0; (in function: nonpow2_insertvalue_narrowing
+; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: unable to legalize instruction: %vreg2<def>(s96) = G_IMPLICIT_DEF; (in function: nonpow2_insertvalue_narrowing
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_insertvalue_narrowing
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_insertvalue_narrowing:
+%struct96 = type { float, float, float }
+define void @nonpow2_insertvalue_narrowing(float %a) {
+ %dummy = insertvalue %struct96 undef, float %a, 0
+ ret void
+}
+
+; FALLBACK-WITH-REPORT-ERR remark: <unknown>:0:0: unable to legalize instruction: %vreg3<def>(s96) = G_ADD %vreg2, %vreg2; (in function: nonpow2_add_narrowing
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_add_narrowing
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_add_narrowing:
+define void @nonpow2_add_narrowing() {
+ %a = add i128 undef, undef
+ %b = trunc i128 %a to i96
+ %dummy = add i96 %b, %b
+ ret void
+}
+
+; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: unable to legalize instruction: %vreg3<def>(s96) = G_OR %vreg2, %vreg2; (in function: nonpow2_or_narrowing
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_or_narrowing
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_or_narrowing:
+define void @nonpow2_or_narrowing() {
+ %a = add i128 undef, undef
+ %b = trunc i128 %a to i96
+ %dummy = or i96 %b, %b
+ ret void
+}
+
+; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: unable to legalize instruction: %vreg0<def>(s96) = G_LOAD %vreg1; mem:LD12[undef](align=16) (in function: nonpow2_load_narrowing
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_load_narrowing
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_load_narrowing:
+define void @nonpow2_load_narrowing() {
+ %dummy = load i96, i96* undef
+ ret void
+}
+
+; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: unable to legalize instruction: G_STORE %vreg3, %vreg0; mem:ST12[%c](align=16) (in function: nonpow2_store_narrowing
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_store_narrowing
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_store_narrowing:
+define void @nonpow2_store_narrowing(i96* %c) {
+ %a = add i128 undef, undef
+ %b = trunc i128 %a to i96
+ store i96 %b, i96* %c
+ ret void
+}
+
+; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: unable to legalize instruction: %vreg0<def>(s96) = G_CONSTANT 0; (in function: nonpow2_constant_narrowing
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_constant_narrowing
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_constant_narrowing:
+define void @nonpow2_constant_narrowing() {
+ store i96 0, i96* undef
+ ret void
+}
+
+; Currently can't handle vector lengths that aren't an exact multiple of
+; natively supported vector lengths. Test that the fall-back works for those.
+; FALLBACK-WITH-REPORT-ERR-G_IMPLICIT_DEF-LEGALIZABLE: (FIXME: this is what is expected once we can legalize non-pow-of-2 G_IMPLICIT_DEF) remark: <unknown>:0:0: unable to legalize instruction: %vreg1<def>(<7 x s64>) = G_ADD %vreg0, %vreg0; (in function: nonpow2_vector_add_fewerelements
+; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: unable to legalize instruction: %vreg0<def>(<7 x s64>) = G_IMPLICIT_DEF; (in function: nonpow2_vector_add_fewerelements
+; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_vector_add_fewerelements
+; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_vector_add_fewerelements:
+define void @nonpow2_vector_add_fewerelements() {
+ %dummy = add <7 x i64> undef, undef
+ ret void
+}
entry:
ret void
}
+ define void @test_scalar_add_big_nonpow2() {
+ entry:
+ ret void
+ }
define void @test_scalar_add_small() {
entry:
ret void
entry:
ret void
}
+ define void @test_vector_add_nonpow2() {
+ entry:
+ ret void
+ }
...
---
%x1 = COPY %8
...
+---
+name: test_scalar_add_big_nonpow2
+registers:
+ - { id: 0, class: _ }
+ - { id: 1, class: _ }
+ - { id: 2, class: _ }
+ - { id: 3, class: _ }
+ - { id: 4, class: _ }
+ - { id: 5, class: _ }
+ - { id: 6, class: _ }
+ - { id: 7, class: _ }
+ - { id: 8, class: _ }
+ - { id: 9, class: _ }
+body: |
+ bb.0.entry:
+ liveins: %x0, %x1, %x2, %x3
+ ; CHECK-LABEL: name: test_scalar_add_big_nonpow2
+ ; CHECK-NOT: G_MERGE_VALUES
+ ; CHECK-NOT: G_UNMERGE_VALUES
+ ; CHECK-DAG: [[CARRY0_32:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+ ; CHECK-DAG: [[CARRY0:%[0-9]+]]:_(s1) = G_TRUNC [[CARRY0_32]]
+ ; CHECK: [[RES_LO:%[0-9]+]]:_(s64), [[CARRY1:%[0-9]+]]:_(s1) = G_UADDE %0, %1, [[CARRY0]]
+ ; CHECK: [[RES_MI:%[0-9]+]]:_(s64), [[CARRY2:%[0-9]+]]:_(s1) = G_UADDE %1, %2, [[CARRY1]]
+ ; CHECK: [[RES_HI:%[0-9]+]]:_(s64), {{%.*}}(s1) = G_UADDE %2, %3, [[CARRY2]]
+ ; CHECK-NOT: G_MERGE_VALUES
+ ; CHECK-NOT: G_UNMERGE_VALUES
+ ; CHECK: %x0 = COPY [[RES_LO]]
+ ; CHECK: %x1 = COPY [[RES_MI]]
+ ; CHECK: %x2 = COPY [[RES_HI]]
+
+ %0(s64) = COPY %x0
+ %1(s64) = COPY %x1
+ %2(s64) = COPY %x2
+ %3(s64) = COPY %x3
+ %4(s192) = G_MERGE_VALUES %0, %1, %2
+ %5(s192) = G_MERGE_VALUES %1, %2, %3
+ %6(s192) = G_ADD %4, %5
+ %7(s64), %8(s64), %9(s64) = G_UNMERGE_VALUES %6
+ %x0 = COPY %7
+ %x1 = COPY %8
+ %x2 = COPY %9
+...
+
---
name: test_scalar_add_small
registers:
%q0 = COPY %7
%q1 = COPY %8
...
+---
+name: test_vector_add_nonpow2
+registers:
+ - { id: 0, class: _ }
+ - { id: 1, class: _ }
+ - { id: 2, class: _ }
+ - { id: 3, class: _ }
+ - { id: 4, class: _ }
+ - { id: 5, class: _ }
+ - { id: 6, class: _ }
+ - { id: 7, class: _ }
+ - { id: 8, class: _ }
+ - { id: 9, class: _ }
+body: |
+ bb.0.entry:
+ liveins: %q0, %q1, %q2, %q3
+ ; CHECK-LABEL: name: test_vector_add_nonpow2
+ ; CHECK-NOT: G_EXTRACT
+ ; CHECK-NOT: G_SEQUENCE
+ ; CHECK: [[RES_LO:%[0-9]+]]:_(<2 x s64>) = G_ADD %0, %1
+ ; CHECK: [[RES_MI:%[0-9]+]]:_(<2 x s64>) = G_ADD %1, %2
+ ; CHECK: [[RES_HI:%[0-9]+]]:_(<2 x s64>) = G_ADD %2, %3
+ ; CHECK-NOT: G_EXTRACT
+ ; CHECK-NOT: G_SEQUENCE
+ ; CHECK: %q0 = COPY [[RES_LO]]
+ ; CHECK: %q1 = COPY [[RES_MI]]
+ ; CHECK: %q2 = COPY [[RES_HI]]
+
+ %0(<2 x s64>) = COPY %q0
+ %1(<2 x s64>) = COPY %q1
+ %2(<2 x s64>) = COPY %q2
+ %3(<2 x s64>) = COPY %q3
+ %4(<6 x s64>) = G_MERGE_VALUES %0, %1, %2
+ %5(<6 x s64>) = G_MERGE_VALUES %1, %2, %3
+ %6(<6 x s64>) = G_ADD %4, %5
+ %7(<2 x s64>), %8(<2 x s64>), %9(<2 x s64>) = G_UNMERGE_VALUES %6
+ %q0 = COPY %7
+ %q1 = COPY %8
+ %q2 = COPY %9
+...
define void @test_inserts_4() { ret void }
define void @test_inserts_5() { ret void }
define void @test_inserts_6() { ret void }
+ define void @test_inserts_nonpow2() { ret void }
...
---
%4:_(s128) = G_INSERT %3, %2, 32
RET_ReallyLR
...
+
+---
+name: test_inserts_nonpow2
+body: |
+ bb.0:
+ liveins: %x0, %x1, %x2
+
+
+ ; CHECK-LABEL: name: test_inserts_nonpow2
+ ; CHECK: %5:_(s192) = G_MERGE_VALUES %3(s64), %1(s64), %2(s64)
+ %0:_(s64) = COPY %x0
+ %1:_(s64) = COPY %x1
+ %2:_(s64) = COPY %x2
+ %3:_(s64) = COPY %x3
+ %4:_(s192) = G_MERGE_VALUES %0, %1, %2
+ %5:_(s192) = G_INSERT %4, %3, 0
+ RET_ReallyLR
+...
- { id: 1, class: gprb }
- { id: 2, class: gprb }
- { id: 3, class: gprb }
+ - { id: 4, class: gprb }
body: |
bb.0:
- liveins: %r0, %r1
+ liveins: %r0, %r1, %r2
%0(p0) = COPY %r0
; CHECK: [[VREGX:%[0-9]+]]:gpr = COPY %r0
%1(p0) = COPY %r1
; CHECK: [[VREGY:%[0-9]+]]:gpr = COPY %r1
- %2(s1) = G_TRUNC %1(p0)
- ; CHECK: [[VREGC:%[0-9]+]]:gpr = COPY [[VREGY]]
+ %2(s32) = COPY %r2
+ ; CHECK: [[VREGC:%[0-9]+]]:gpr = COPY %r2
- %3(p0) = G_SELECT %2(s1), %0, %1
- ; CHECK: CMPri [[VREGC]], 0, 14, _, implicit-def %cpsr
+ %3(s1) = G_TRUNC %2(s32)
+ ; CHECK: [[VREGD:%[0-9]+]]:gpr = COPY [[VREGC]]
+
+ %4(p0) = G_SELECT %3(s1), %0, %1
+ ; CHECK: CMPri [[VREGD]], 0, 14, _, implicit-def %cpsr
; CHECK: [[RES:%[0-9]+]]:gpr = MOVCCr [[VREGX]], [[VREGY]], 0, %cpsr
- %r0 = COPY %3(p0)
+ %r0 = COPY %4(p0)
; CHECK: %r0 = COPY [[RES]]
BX_RET 14, _, implicit %r0
using namespace TargetOpcode;
LegalizerInfo L;
// Typical RISCy set of operations based on AArch64.
- L.setAction({G_ADD, LLT::scalar(8)}, LegalizerInfo::WidenScalar);
- L.setAction({G_ADD, LLT::scalar(16)}, LegalizerInfo::WidenScalar);
- L.setAction({G_ADD, LLT::scalar(32)}, LegalizerInfo::Legal);
- L.setAction({G_ADD, LLT::scalar(64)}, LegalizerInfo::Legal);
+ for (auto Op : {G_ADD, G_SUB}) {
+ for (unsigned Size : {32, 64})
+ L.setAction({Op, 0, LLT::scalar(Size)}, LegalizerInfo::Legal);
+ L.setLegalizeScalarToDifferentSizeStrategy(
+ Op, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest);
+ }
+
L.computeTables();
- // Check we infer the correct types and actually do what we're told.
- ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(8)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(16)}),
- std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(32)}),
- std::make_pair(LegalizerInfo::Legal, LLT::scalar(32)));
- ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(64)}),
- std::make_pair(LegalizerInfo::Legal, LLT::scalar(64)));
-
- // Make sure the default for over-sized types applies.
- ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(128)}),
- std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64)));
+ for (auto &opcode : {G_ADD, G_SUB}) {
+ // Check we infer the correct types and actually do what we're told.
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(8)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(16)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(32)}),
+ std::make_pair(LegalizerInfo::Legal, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(64)}),
+ std::make_pair(LegalizerInfo::Legal, LLT::scalar(64)));
+
+ // Make sure the default for over-sized types applies.
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(128)}),
+ std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64)));
+ // Make sure we also handle unusual sizes
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(1)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(31)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(33)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(63)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(64)));
+ ASSERT_EQ(L.getAction({opcode, LLT::scalar(65)}),
+ std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64)));
+ }
}
TEST(LegalizerInfoTest, VectorRISC) {
using namespace TargetOpcode;
LegalizerInfo L;
// Typical RISCy set of operations based on ARM.
- L.setScalarInVectorAction(G_ADD, LLT::scalar(8), LegalizerInfo::Legal);
- L.setScalarInVectorAction(G_ADD, LLT::scalar(16), LegalizerInfo::Legal);
- L.setScalarInVectorAction(G_ADD, LLT::scalar(32), LegalizerInfo::Legal);
-
L.setAction({G_ADD, LLT::vector(8, 8)}, LegalizerInfo::Legal);
L.setAction({G_ADD, LLT::vector(16, 8)}, LegalizerInfo::Legal);
L.setAction({G_ADD, LLT::vector(4, 16)}, LegalizerInfo::Legal);
L.setAction({G_ADD, LLT::vector(8, 16)}, LegalizerInfo::Legal);
L.setAction({G_ADD, LLT::vector(2, 32)}, LegalizerInfo::Legal);
L.setAction({G_ADD, LLT::vector(4, 32)}, LegalizerInfo::Legal);
+
+ L.setLegalizeVectorElementToDifferentSizeStrategy(
+ G_ADD, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
+
+ L.setAction({G_ADD, 0, LLT::scalar(32)}, LegalizerInfo::Legal);
+
L.computeTables();
// Check we infer the correct types and actually do what we're told for some
// simple cases.
- ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}),
- std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8)));
ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 8)}),
std::make_pair(LegalizerInfo::Legal, LLT::vector(8, 8)));
- ASSERT_EQ(
- L.getAction({G_ADD, LLT::vector(8, 32)}),
- std::make_pair(LegalizerInfo::FewerElements, LLT::vector(4, 32)));
+ ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 7)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::vector(8, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}),
+ std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 32)}),
+ std::make_pair(LegalizerInfo::FewerElements, LLT::vector(4, 32)));
+ // Check a few non-power-of-2 sizes:
+ ASSERT_EQ(L.getAction({G_ADD, LLT::vector(3, 3)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::vector(3, 8)));
+ ASSERT_EQ(L.getAction({G_ADD, LLT::vector(3, 8)}),
+ std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8)));
}
TEST(LegalizerInfoTest, MultipleTypes) {
using namespace TargetOpcode;
LegalizerInfo L;
LLT p0 = LLT::pointer(0, 64);
- LLT s32 = LLT::scalar(32);
LLT s64 = LLT::scalar(64);
// Typical RISCy set of operations based on AArch64.
L.setAction({G_PTRTOINT, 0, s64}, LegalizerInfo::Legal);
L.setAction({G_PTRTOINT, 1, p0}, LegalizerInfo::Legal);
- L.setAction({G_PTRTOINT, 0, s32}, LegalizerInfo::WidenScalar);
+ L.setLegalizeScalarToDifferentSizeStrategy(
+ G_PTRTOINT, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest);
+
L.computeTables();
// Check we infer the correct types and actually do what we're told.
std::make_pair(LegalizerInfo::Legal, s64));
ASSERT_EQ(L.getAction({G_PTRTOINT, 1, p0}),
std::make_pair(LegalizerInfo::Legal, p0));
+ // Make sure we also handle unusual sizes
+ ASSERT_EQ(L.getAction({G_PTRTOINT, 0, LLT::scalar(65)}),
+ std::make_pair(LegalizerInfo::NarrowScalar, s64));
+ ASSERT_EQ(L.getAction({G_PTRTOINT, 1, LLT::pointer(0, 32)}),
+ std::make_pair(LegalizerInfo::Unsupported, LLT::pointer(0, 32)));
}
TEST(LegalizerInfoTest, MultipleSteps) {
using namespace TargetOpcode;
LegalizerInfo L;
- LLT s16 = LLT::scalar(16);
LLT s32 = LLT::scalar(32);
LLT s64 = LLT::scalar(64);
- L.setAction({G_UREM, 0, s16}, LegalizerInfo::WidenScalar);
+ L.setLegalizeScalarToDifferentSizeStrategy(
+ G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
L.setAction({G_UREM, 0, s32}, LegalizerInfo::Lower);
L.setAction({G_UREM, 0, s64}, LegalizerInfo::Lower);
ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(32)}),
std::make_pair(LegalizerInfo::Lower, LLT::scalar(32)));
}
+
+TEST(LegalizerInfoTest, SizeChangeStrategy) {
+ using namespace TargetOpcode;
+ LegalizerInfo L;
+ for (unsigned Size : {1, 8, 16, 32})
+ L.setAction({G_UREM, 0, LLT::scalar(Size)}, LegalizerInfo::Legal);
+
+ L.setLegalizeScalarToDifferentSizeStrategy(
+ G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
+ L.computeTables();
+
+ // Check we infer the correct types and actually do what we're told.
+ for (unsigned Size : {1, 8, 16, 32}) {
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(Size)}),
+ std::make_pair(LegalizerInfo::Legal, LLT::scalar(Size)));
+ }
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(2)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(8)));
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(7)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(8)));
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(9)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(16)));
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(17)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(31)}),
+ std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32)));
+ ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(33)}),
+ std::make_pair(LegalizerInfo::Unsupported, LLT::scalar(33)));
+}
}
for (unsigned S : {1U, 17U, 32U, 64U, 0xfffffU}) {
const LLT Ty = LLT::scalar(S);
- const LLT HalfTy = (S % 2) == 0 ? Ty.halfScalarSize() : Ty;
- const LLT DoubleTy = Ty.doubleScalarSize();
// Test kind.
- for (const LLT TestTy : {Ty, HalfTy, DoubleTy}) {
- ASSERT_TRUE(TestTy.isValid());
- ASSERT_TRUE(TestTy.isScalar());
+ ASSERT_TRUE(Ty.isValid());
+ ASSERT_TRUE(Ty.isScalar());
- ASSERT_FALSE(TestTy.isPointer());
- ASSERT_FALSE(TestTy.isVector());
- }
+ ASSERT_FALSE(Ty.isPointer());
+ ASSERT_FALSE(Ty.isVector());
// Test sizes.
EXPECT_EQ(S, Ty.getSizeInBits());
EXPECT_EQ(S, Ty.getScalarSizeInBits());
- EXPECT_EQ(S*2, DoubleTy.getSizeInBits());
- EXPECT_EQ(S*2, DoubleTy.getScalarSizeInBits());
-
- if ((S % 2) == 0) {
- EXPECT_EQ(S/2, HalfTy.getSizeInBits());
- EXPECT_EQ(S/2, HalfTy.getScalarSizeInBits());
- }
-
// Test equality operators.
EXPECT_TRUE(Ty == Ty);
EXPECT_FALSE(Ty != Ty);
- EXPECT_NE(Ty, DoubleTy);
-
// Test Type->LLT conversion.
Type *IRTy = IntegerType::get(C, S);
EXPECT_EQ(Ty, getLLTForType(*IRTy, DL));
// Test getElementType().
EXPECT_EQ(STy, VTy.getElementType());
- const LLT HalfSzTy = ((S % 2) == 0) ? VTy.halfScalarSize() : VTy;
- const LLT DoubleSzTy = VTy.doubleScalarSize();
-
- // halfElements requires an even number of elements.
- const LLT HalfEltIfEvenTy = ((Elts % 2) == 0) ? VTy.halfElements() : VTy;
- const LLT DoubleEltTy = VTy.doubleElements();
-
// Test kind.
- for (const LLT TestTy : {VTy, HalfSzTy, DoubleSzTy, DoubleEltTy}) {
- ASSERT_TRUE(TestTy.isValid());
- ASSERT_TRUE(TestTy.isVector());
-
- ASSERT_FALSE(TestTy.isScalar());
- ASSERT_FALSE(TestTy.isPointer());
- }
-
- // Test halving elements to a scalar.
- {
- ASSERT_TRUE(HalfEltIfEvenTy.isValid());
- ASSERT_FALSE(HalfEltIfEvenTy.isPointer());
- if (Elts > 2) {
- ASSERT_TRUE(HalfEltIfEvenTy.isVector());
- } else {
- ASSERT_FALSE(HalfEltIfEvenTy.isVector());
- EXPECT_EQ(STy, HalfEltIfEvenTy);
- }
- }
+ ASSERT_TRUE(VTy.isValid());
+ ASSERT_TRUE(VTy.isVector());
+ ASSERT_FALSE(VTy.isScalar());
+ ASSERT_FALSE(VTy.isPointer());
// Test sizes.
EXPECT_EQ(S * Elts, VTy.getSizeInBits());
EXPECT_EQ(S, VTy.getScalarSizeInBits());
EXPECT_EQ(Elts, VTy.getNumElements());
- if ((S % 2) == 0) {
- EXPECT_EQ((S / 2) * Elts, HalfSzTy.getSizeInBits());
- EXPECT_EQ(S / 2, HalfSzTy.getScalarSizeInBits());
- EXPECT_EQ(Elts, HalfSzTy.getNumElements());
- }
-
- EXPECT_EQ((S * 2) * Elts, DoubleSzTy.getSizeInBits());
- EXPECT_EQ(S * 2, DoubleSzTy.getScalarSizeInBits());
- EXPECT_EQ(Elts, DoubleSzTy.getNumElements());
-
- if ((Elts % 2) == 0) {
- EXPECT_EQ(S * (Elts / 2), HalfEltIfEvenTy.getSizeInBits());
- EXPECT_EQ(S, HalfEltIfEvenTy.getScalarSizeInBits());
- if (Elts > 2) {
- EXPECT_EQ(Elts / 2, HalfEltIfEvenTy.getNumElements());
- }
- }
-
- EXPECT_EQ(S * (Elts * 2), DoubleEltTy.getSizeInBits());
- EXPECT_EQ(S, DoubleEltTy.getScalarSizeInBits());
- EXPECT_EQ(Elts * 2, DoubleEltTy.getNumElements());
-
// Test equality operators.
EXPECT_TRUE(VTy == VTy);
EXPECT_FALSE(VTy != VTy);
// Test inequality operators on..
// ..different kind.
EXPECT_NE(VTy, STy);
- // ..different #elts.
- EXPECT_NE(VTy, DoubleEltTy);
- // ..different scalar size.
- EXPECT_NE(VTy, DoubleSzTy);
// Test Type->LLT conversion.
Type *IRSTy = IntegerType::get(C, S);