Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace llvm { @@ -120,27 +121,144 @@ } } + typedef std::pair SizeAndAction; + typedef std::vector SizeAndActionsVec; + using SizeChangeStrategy = + std::function; + /// 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 ) 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. @@ -158,58 +276,6 @@ std::tuple 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 findLegalizableSize(const InstrAspect &Aspect, - function_ref 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 findLegalType(const InstrAspect &Aspect, LegalizeAction Action) const; - - std::pair 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, @@ -217,20 +283,181 @@ 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 &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 &Actions = + AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace)->second; + setActions(TypeIndex, Actions, SizeAndActions); + } - using TypeMap = DenseMap; - using SIVActionMap = DenseMap, LegalizeAction>; + /// If an operation on a given vector type (say ) 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 &Actions = + ScalarInVectorActions[OpcodeIdx]; + setActions(TypeIndex, Actions, SizeAndActions); + } - SmallVector Actions[LastOp - FirstOp + 1]; - SIVActionMap ScalarInVectorActions; - DenseMap, uint16_t> MaxLegalVectorElts; - DenseMap DefaultActions; + /// 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 &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 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 &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 + findScalarLegalAction(const InstrAspect &Aspect) const; + + /// Returns the next action needed towards legalizing the vector type. + std::pair + 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 TypeMap; + SmallVector SpecifiedActions[LastOp - FirstOp + 1]; + SmallVector + ScalarSizeChangeStrategies[LastOp - FirstOp + 1]; + SmallVector + VectorElementSizeChangeStrategies[LastOp - FirstOp + 1]; + bool TablesInitialized; + + // Data structures used by getAction: + SmallVector ScalarActions[LastOp - FirstOp + 1]; + SmallVector ScalarInVectorActions[LastOp - FirstOp + 1]; + std::unordered_map> + AddrSpace2PointerActions[LastOp - FirstOp + 1]; + std::unordered_map> + NumElements2Actions[LastOp - FirstOp + 1]; }; -} // end namespace llvm +} // end namespace llvm. #endif // LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H Index: llvm/trunk/include/llvm/Support/LowLevelTypeImpl.h =================================================================== --- llvm/trunk/include/llvm/Support/LowLevelTypeImpl.h +++ llvm/trunk/include/llvm/Support/LowLevelTypeImpl.h @@ -137,51 +137,6 @@ 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 { Index: llvm/trunk/lib/CodeGen/GlobalISel/LegalizerHelper.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -173,12 +173,18 @@ 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 DstRegs; for (int i = 0; i < NumParts; ++i) { @@ -191,9 +197,12 @@ 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 Src1Regs, Src2Regs, DstRegs; extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); @@ -221,9 +230,12 @@ 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 SrcRegs, DstRegs; SmallVector Indexes; @@ -270,12 +282,12 @@ 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 SrcRegs, DstRegs; SmallVector Indexes; @@ -330,9 +342,11 @@ 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()); @@ -357,9 +371,11 @@ 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()); @@ -381,9 +397,11 @@ 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(); @@ -410,9 +428,12 @@ // ... // AN = BinOp 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 DstRegs; @@ -854,7 +875,12 @@ 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); Index: llvm/trunk/lib/CodeGen/GlobalISel/LegalizerInfo.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/LegalizerInfo.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/LegalizerInfo.cpp @@ -28,46 +28,130 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Target/TargetOpcodes.h" #include -#include -#include -#include - +#include 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; - - DefaultActions[TargetOpcode::G_INTRINSIC] = Legal; - DefaultActions[TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS] = Legal; - - DefaultActions[TargetOpcode::G_ADD] = NarrowScalar; - DefaultActions[TargetOpcode::G_LOAD] = NarrowScalar; - DefaultActions[TargetOpcode::G_STORE] = NarrowScalar; - DefaultActions[TargetOpcode::G_OR] = NarrowScalar; - - DefaultActions[TargetOpcode::G_BRCOND] = WidenScalar; - DefaultActions[TargetOpcode::G_INSERT] = NarrowScalar; - DefaultActions[TargetOpcode::G_EXTRACT] = NarrowScalar; - DefaultActions[TargetOpcode::G_FNEG] = Lower; +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}}); + + setScalarAction(TargetOpcode::G_INTRINSIC, 0, {{1, Legal}}); + setScalarAction(TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS, 0, {{1, Legal}}); + + 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); + + 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 AddressSpace2SpecifiedActions; + // For vector types: + std::map 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)); } } @@ -90,69 +174,24 @@ 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::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; @@ -172,38 +211,164 @@ return std::get<0>(getAction(MI, MRI)) == Legal; } -Optional 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::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 &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::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())}; } Index: llvm/trunk/lib/Support/LowLevelType.cpp =================================================================== --- llvm/trunk/lib/Support/LowLevelType.cpp +++ llvm/trunk/lib/Support/LowLevelType.cpp @@ -43,7 +43,7 @@ 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; Index: llvm/trunk/lib/Target/AArch64/AArch64LegalizerInfo.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ llvm/trunk/lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -23,6 +23,110 @@ 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); @@ -42,8 +146,7 @@ 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); @@ -54,15 +157,15 @@ 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); @@ -70,16 +173,17 @@ 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 }) @@ -101,8 +205,9 @@ 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). @@ -118,7 +223,8 @@ 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); @@ -132,20 +238,16 @@ 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); @@ -159,12 +261,6 @@ 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); @@ -176,12 +272,6 @@ 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); @@ -189,12 +279,10 @@ 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); @@ -209,8 +297,7 @@ 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); Index: llvm/trunk/lib/Target/ARM/ARMLegalizerInfo.cpp =================================================================== --- llvm/trunk/lib/Target/ARM/ARMLegalizerInfo.cpp +++ llvm/trunk/lib/Target/ARM/ARMLegalizerInfo.cpp @@ -24,6 +24,54 @@ 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(); } @@ -49,14 +97,15 @@ } 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 @@ -64,8 +113,7 @@ } 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)) @@ -74,10 +122,8 @@ 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}) @@ -93,12 +139,11 @@ 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); Index: llvm/trunk/lib/Target/X86/X86LegalizerInfo.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86LegalizerInfo.cpp +++ llvm/trunk/lib/Target/X86/X86LegalizerInfo.cpp @@ -22,6 +22,38 @@ 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) { @@ -37,6 +69,17 @@ 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(); } @@ -47,7 +90,6 @@ 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); @@ -55,15 +97,10 @@ 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); @@ -73,7 +110,6 @@ 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); } @@ -85,9 +121,6 @@ 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); @@ -95,9 +128,6 @@ 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); @@ -105,12 +135,6 @@ 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); @@ -123,7 +147,6 @@ if (!Subtarget.is64Bit()) return; - const LLT s32 = LLT::scalar(32); const LLT s64 = LLT::scalar(64); setAction({G_IMPLICIT_DEF, s64}, Legal); @@ -145,7 +168,6 @@ // Extensions for (unsigned extOp : {G_ZEXT, G_SEXT, G_ANYEXT}) { setAction({extOp, s64}, Legal); - setAction({extOp, 1, s32}, Legal); } // Comparison Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-fallback.ll =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-fallback.ll +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-fallback.ll @@ -167,3 +167,70 @@ %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: :0:0: unable to legalize instruction: %vreg1(s96) = G_INSERT %vreg2, %vreg0, 0; (in function: nonpow2_insertvalue_narrowing +; FALLBACK-WITH-REPORT-ERR: remark: :0:0: unable to legalize instruction: %vreg2(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: :0:0: unable to legalize instruction: %vreg3(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: :0:0: unable to legalize instruction: %vreg3(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: :0:0: unable to legalize instruction: %vreg0(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: :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: :0:0: unable to legalize instruction: %vreg0(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: :0:0: unable to legalize instruction: %vreg1(<7 x s64>) = G_ADD %vreg0, %vreg0; (in function: nonpow2_vector_add_fewerelements +; FALLBACK-WITH-REPORT-ERR: remark: :0:0: unable to legalize instruction: %vreg0(<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 +} Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/legalize-add.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/legalize-add.mir +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/legalize-add.mir @@ -8,6 +8,10 @@ entry: ret void } + define void @test_scalar_add_big_nonpow2() { + entry: + ret void + } define void @test_scalar_add_small() { entry: ret void @@ -16,6 +20,10 @@ entry: ret void } + define void @test_vector_add_nonpow2() { + entry: + ret void + } ... --- @@ -58,6 +66,49 @@ ... --- +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: - { id: 0, class: _ } @@ -124,3 +175,43 @@ %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 +... Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/legalize-inserts.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/legalize-inserts.mir +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/legalize-inserts.mir @@ -9,6 +9,7 @@ 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 } ... --- @@ -141,3 +142,21 @@ %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 +... Index: llvm/trunk/test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir =================================================================== --- llvm/trunk/test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir +++ llvm/trunk/test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir @@ -970,9 +970,10 @@ - { 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 @@ -980,14 +981,17 @@ %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 Index: llvm/trunk/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp =================================================================== --- llvm/trunk/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp +++ llvm/trunk/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp @@ -49,66 +49,91 @@ 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. @@ -116,16 +141,21 @@ 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); @@ -136,4 +166,33 @@ 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))); +} } Index: llvm/trunk/unittests/CodeGen/LowLevelTypeTest.cpp =================================================================== --- llvm/trunk/unittests/CodeGen/LowLevelTypeTest.cpp +++ llvm/trunk/unittests/CodeGen/LowLevelTypeTest.cpp @@ -36,36 +36,22 @@ 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)); @@ -90,62 +76,18 @@ // 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); @@ -153,10 +95,6 @@ // 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);