[llvm] [SLP]Recognize interchangeable cmp predicates with boundary constants (PR #218237)
Alexey Bataev via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 06:05:20 PDT 2026
https://github.com/alexey-bataev created https://github.com/llvm/llvm-project/pull/218237
Treat boundary comparisons canonicalized to eq/ne (e.g. x <u 1 became
x == 0) as interchangeable with the rest of the bundle by adjusting
the compared constant, emitting a single vector compare.
Fixes #190505
>From 9742b7e2f71a66b0a55d3bd947d4f7dc6018b2b5 Mon Sep 17 00:00:00 2001
From: Alexey Bataev <a.bataev at outlook.com>
Date: Sun, 23 Aug 2026 06:05:04 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
=?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
---
.../Transforms/Vectorize/SLPVectorizer.cpp | 57 ++++-
.../SLPCompatibilityAnalysis.cpp | 222 ++++++++++++++++++
.../SLPVectorizer/SLPCompatibilityAnalysis.h | 52 ++++
.../X86/interchangeable-cmp-predicates.ll | 78 ++----
4 files changed, 351 insertions(+), 58 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 8d49f8b9fda9d..192c3cf7e5195 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -10379,7 +10379,8 @@ BoUpSLP::TreeEntry::EntryState BoUpSLP::getScalarsVectorizationState(
if (isa<PoisonValue>(V))
continue;
auto *Cmp = cast<CmpInst>(V);
- if ((Cmp->getPredicate() != P0 && Cmp->getPredicate() != SwapP0) ||
+ if ((Cmp->getPredicate() != P0 && Cmp->getPredicate() != SwapP0 &&
+ !CmpSamePredicateHelper::canConvertTo(Cmp, P0)) ||
Cmp->getOperand(0)->getType() != ComparedTy) {
LLVM_DEBUG(dbgs() << "SLP: Gathering cmp with different predicate.\n");
return TreeEntry::NeedToGather;
@@ -12705,6 +12706,30 @@ void BoUpSLP::buildTreeRec(ArrayRef<Value *> VLRef, unsigned Depth,
if (S.isAltShuffle() && TrySplitNode(S))
return;
+ // Cmp nodes with interchangeable lanes (e.g. x == 0 mixed with x <u C)
+ // try the split by the original predicates, like the alternate nodes.
+ if (S.getOpcode() == Instruction::ICmp && !S.isAltShuffle()) {
+ auto SameOrSwapped = [](const ICmpInst *CI, CmpInst::Predicate P) {
+ return CI->getPredicate() == P ||
+ CI->getPredicate() == CmpInst::getSwappedPredicate(P);
+ };
+ ICmpInst *MainI = cast<ICmpInst>(*find_if(VL, IsaPred<ICmpInst>));
+ auto *AltIt = find_if(VL, [&](Value *V) {
+ auto *CI = dyn_cast<ICmpInst>(V);
+ return CI && !SameOrSwapped(CI, MainI->getPredicate());
+ });
+ ICmpInst *AltI = AltIt == VL.end() ? nullptr : cast<ICmpInst>(*AltIt);
+ if (AltI &&
+ all_of(VL,
+ [&](Value *V) {
+ auto *CI = dyn_cast<ICmpInst>(V);
+ return !CI || SameOrSwapped(CI, MainI->getPredicate()) ||
+ SameOrSwapped(CI, AltI->getPredicate());
+ }) &&
+ TrySplitNode(InstructionsState(MainI, AltI)))
+ return;
+ }
+
// Check that every instruction appears once in this bundle.
if (!tryToFindDuplicates(VL, ReuseShuffleIndices, *TTI, *TLI, S, UserTreeIdx,
*this, /*BuildGatherOnly=*/false)) {
@@ -13219,11 +13244,14 @@ void BoUpSLP::buildTreeRec(ArrayRef<Value *> VLRef, unsigned Depth,
Operands.back() = Ops.getVL(1);
} else {
// Collect operands - commute if it uses the swapped predicate.
+ // Lanes interchangeable with P0 (e.g. x == 0 in an x <u C bundle)
+ // already have their operands adjusted, no need to commute them.
for (auto [Idx, V] : enumerate(VL)) {
if (isa<PoisonValue>(V))
continue;
auto *Cmp = cast<CmpInst>(V);
- if (Cmp->getPredicate() != P0)
+ if (Cmp->getPredicate() != P0 &&
+ !CmpSamePredicateHelper::canConvertTo(Cmp, P0))
std::swap(Operands.front()[Idx], Operands.back()[Idx]);
}
}
@@ -23504,8 +23532,29 @@ Value *BoUpSLP::vectorizeTree(TreeEntry *E) {
return CI && CI->getValue() == CI->getBitWidth() - 1;
}))
I->setHasNoSignedWrap(/*b=*/false);
- if (auto *ICmp = dyn_cast<ICmpInst>(I); ICmp && It == MinBWs.end())
- ICmp->setSameSign(/*B=*/false);
+ // Keep the intersected samesign unless the compared operands were
+ // narrowed (the sign relation may change in the narrower type) or a
+ // converted lane's adjusted constant flips the sign.
+ if (auto *ICmp = dyn_cast<ICmpInst>(I); ICmp && It == MinBWs.end()) {
+ bool Narrowed = MinBWs.contains(getOperandEntry(E, 0)) ||
+ MinBWs.contains(getOperandEntry(E, 1));
+ bool SignFlip = !Narrowed && [&] {
+ CmpInst::Predicate P0 = cast<CmpInst>(E->getMainOp())->getPredicate();
+ return any_of(E->Scalars, [&](Value *Scalar) {
+ auto *LaneCI = dyn_cast<ICmpInst>(Scalar);
+ if (!LaneCI || LaneCI->getPredicate() == P0)
+ return false;
+ auto *OrigC = dyn_cast<ConstantInt>(LaneCI->getOperand(1));
+ if (!OrigC)
+ return false;
+ ConstantInt *AdjC =
+ CmpSamePredicateHelper::getAdjustedConstant(LaneCI, P0);
+ return AdjC && AdjC->isNegative() != OrigC->isNegative();
+ });
+ }();
+ if (Narrowed || SignFlip)
+ ICmp->setSameSign(/*B=*/false);
+ }
return I;
};
switch (ShuffleOrOp) {
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.cpp
index 63c0a921a7917..ba840bd10acb6 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.cpp
@@ -15,6 +15,7 @@
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SmallVectorExtras.h"
+#include "llvm/ADT/bit.h"
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/InstrTypes.h"
@@ -30,6 +31,7 @@
#include <algorithm>
#include <array>
#include <cassert>
+#include <optional>
#include <utility>
using namespace llvm;
@@ -324,6 +326,192 @@ bool BinOpSameOpcodeHelper::add(const Instruction *I) {
AltOp.trySet(OpcodeInMaskForm, InterchangeableMask));
}
+/// If the comparison (Pred, X, C) is a single-element or single-complement
+/// range check, returns its boundary family: false + K for the singleton
+/// {K} (eq forms), true + K for the complement of {K} (ne forms).
+static std::optional<std::pair<bool, APInt>>
+getCmpBoundaryFamily(CmpInst::Predicate Pred, const APInt &C) {
+ const unsigned BW = C.getBitWidth();
+ const APInt MaxU = APInt::getMaxValue(BW);
+ const APInt MinS = APInt::getSignedMinValue(BW);
+ const APInt MaxS = APInt::getSignedMaxValue(BW);
+ switch (Pred) {
+ case CmpInst::ICMP_EQ:
+ return std::make_pair(false, C);
+ case CmpInst::ICMP_NE:
+ return std::make_pair(true, C);
+ case CmpInst::ICMP_ULT:
+ if (C.isOne())
+ return std::make_pair(false, APInt(BW, 0));
+ if (C == MaxU)
+ return std::make_pair(true, C);
+ break;
+ case CmpInst::ICMP_ULE:
+ if (C.isZero())
+ return std::make_pair(false, C);
+ if (C == MaxU - 1)
+ return std::make_pair(true, MaxU);
+ break;
+ case CmpInst::ICMP_UGT:
+ if (C.isZero())
+ return std::make_pair(true, C);
+ if (C == MaxU - 1)
+ return std::make_pair(false, MaxU);
+ break;
+ case CmpInst::ICMP_UGE:
+ if (C.isOne())
+ return std::make_pair(true, APInt(BW, 0));
+ if (C == MaxU)
+ return std::make_pair(false, C);
+ break;
+ case CmpInst::ICMP_SLT:
+ if (C == MinS + 1)
+ return std::make_pair(false, MinS);
+ if (C == MaxS)
+ return std::make_pair(true, C);
+ break;
+ case CmpInst::ICMP_SLE:
+ if (C == MinS)
+ return std::make_pair(false, C);
+ if (C == MaxS - 1)
+ return std::make_pair(true, MaxS);
+ break;
+ case CmpInst::ICMP_SGT:
+ if (C == MinS)
+ return std::make_pair(true, C);
+ if (C == MaxS - 1)
+ return std::make_pair(false, MaxS);
+ break;
+ case CmpInst::ICMP_SGE:
+ if (C == MinS + 1)
+ return std::make_pair(true, MinS);
+ if (C == MaxS)
+ return std::make_pair(false, C);
+ break;
+ default:
+ break;
+ }
+ return std::nullopt;
+}
+
+CmpSamePredicateHelper::MaskType
+CmpSamePredicateHelper::getFormsMask(CmpInst::Predicate Pred, const APInt &C) {
+ MaskType M = getBit(Pred);
+ std::optional<std::pair<bool, APInt>> Family = getCmpBoundaryFamily(Pred, C);
+ if (!Family)
+ return M;
+ const auto &[IsComplement, K] = *Family;
+ if (!IsComplement) {
+ M |= getBit(CmpInst::ICMP_EQ);
+ if (K.isZero())
+ M |= getBit(CmpInst::ICMP_ULT) | getBit(CmpInst::ICMP_ULE);
+ if (K.isMaxValue())
+ M |= getBit(CmpInst::ICMP_UGT) | getBit(CmpInst::ICMP_UGE);
+ if (K.isMinSignedValue())
+ M |= getBit(CmpInst::ICMP_SLT) | getBit(CmpInst::ICMP_SLE);
+ if (K.isMaxSignedValue())
+ M |= getBit(CmpInst::ICMP_SGT) | getBit(CmpInst::ICMP_SGE);
+ return M;
+ }
+ M |= getBit(CmpInst::ICMP_NE);
+ if (K.isZero())
+ M |= getBit(CmpInst::ICMP_UGT) | getBit(CmpInst::ICMP_UGE);
+ if (K.isMaxValue())
+ M |= getBit(CmpInst::ICMP_ULT) | getBit(CmpInst::ICMP_ULE);
+ if (K.isMinSignedValue())
+ M |= getBit(CmpInst::ICMP_SGT) | getBit(CmpInst::ICMP_SGE);
+ if (K.isMaxSignedValue())
+ M |= getBit(CmpInst::ICMP_SLT) | getBit(CmpInst::ICMP_SLE);
+ return M;
+}
+
+APInt CmpSamePredicateHelper::getFamilyConstant(bool IsComplement,
+ const APInt &K,
+ CmpInst::Predicate Pred) {
+ switch (Pred) {
+ case CmpInst::ICMP_EQ:
+ case CmpInst::ICMP_NE:
+ return K;
+ case CmpInst::ICMP_ULT:
+ case CmpInst::ICMP_SLT:
+ return IsComplement ? K : K + 1;
+ case CmpInst::ICMP_ULE:
+ case CmpInst::ICMP_SLE:
+ return IsComplement ? K - 1 : K;
+ case CmpInst::ICMP_UGT:
+ case CmpInst::ICMP_SGT:
+ return IsComplement ? K : K - 1;
+ case CmpInst::ICMP_UGE:
+ case CmpInst::ICMP_SGE:
+ return IsComplement ? K + 1 : K;
+ default:
+ llvm_unreachable("Unexpected predicate.");
+ }
+}
+
+bool CmpSamePredicateHelper::add(const ICmpInst *CI) {
+ MaskType LaneMask = getBit(CI->getPredicate());
+ if (auto *C = dyn_cast<ConstantInt>(CI->getOperand(1)))
+ LaneMask = getFormsMask(CI->getPredicate(), C->getValue());
+ SeenBefore |= getBit(CI->getPredicate());
+ Mask &= LaneMask;
+ return Mask != 0;
+}
+
+CmpInst::Predicate
+CmpSamePredicateHelper::getPredicate(const ICmpInst *Preferred) const {
+ MaskType Candidate = Mask & SeenBefore;
+ if (!Candidate)
+ return CmpInst::BAD_ICMP_PREDICATE;
+ if (Candidate & getBit(Preferred->getPredicate()))
+ return Preferred->getPredicate();
+ return static_cast<CmpInst::Predicate>(CmpInst::ICMP_EQ +
+ countr_zero(Candidate));
+}
+
+CmpInst::Predicate
+CmpSamePredicateHelper::getSharedPredicate(ArrayRef<Value *> VL,
+ const ICmpInst *Preferred) {
+ CmpSamePredicateHelper Helper;
+ if (!all_of(VL, [&](Value *V) {
+ auto *CI = dyn_cast<ICmpInst>(V);
+ return isa<PoisonValue>(V) || (CI && Helper.add(CI));
+ }))
+ return CmpInst::BAD_ICMP_PREDICATE;
+ return Helper.getPredicate(Preferred);
+}
+
+bool CmpSamePredicateHelper::canConvertTo(const CmpInst *CI,
+ CmpInst::Predicate Pred) {
+ if (!CmpInst::isIntPredicate(Pred))
+ return false;
+ auto *ICI = dyn_cast<ICmpInst>(CI);
+ if (!ICI)
+ return false;
+ if (ICI->getPredicate() == Pred)
+ return true;
+ auto *C = dyn_cast<ConstantInt>(ICI->getOperand(1));
+ if (!C)
+ return false;
+ return getFormsMask(ICI->getPredicate(), C->getValue()) & getBit(Pred);
+}
+
+ConstantInt *
+CmpSamePredicateHelper::getAdjustedConstant(const CmpInst *CI,
+ CmpInst::Predicate Pred) {
+ if (!canConvertTo(CI, Pred))
+ return nullptr;
+ auto *ICI = cast<ICmpInst>(CI);
+ if (ICI->getPredicate() == Pred)
+ return nullptr;
+ auto *C = cast<ConstantInt>(ICI->getOperand(1));
+ std::optional<std::pair<bool, APInt>> Family =
+ getCmpBoundaryFamily(ICI->getPredicate(), C->getValue());
+ assert(Family && "Expected a boundary family for a convertible compare.");
+ return ConstantInt::get(
+ CI->getContext(), getFamilyConstant(Family->first, Family->second, Pred));
+}
+
bool InstructionsState::isSameOperation(const Instruction *I,
const Instruction *Op) {
if (I->getOpcode() != Op->getOpcode())
@@ -610,6 +798,12 @@ InstructionsState getSameOpcode(ArrayRef<Value *> VL,
// not alternate.
return UniqueNonSwappedPreds.size() > 2 && UniquePreds.size() == 2;
}();
+ // Find the predicate the whole bundle can share, if any, treating
+ // boundary comparisons canonicalized to eq/ne as interchangeable.
+ CmpInst::Predicate InterchangeablePred = CmpInst::BAD_ICMP_PREDICATE;
+ if (IsCmpOp && isa<ICmpInst>(MainOp))
+ InterchangeablePred =
+ CmpSamePredicateHelper::getSharedPredicate(VL, cast<ICmpInst>(MainOp));
// Check for one alternate opcode from another BinaryOperator.
// TODO - generalize to support all operators (types, calls etc.).
Intrinsic::ID BaseID = 0;
@@ -675,6 +869,8 @@ InstructionsState getSameOpcode(ArrayRef<Value *> VL,
if (isCmpSameOrSwapped(BaseInst, Inst, TLI))
continue;
+ if (CmpSamePredicateHelper::canConvertTo(Inst, InterchangeablePred))
+ continue;
auto *AltInst = cast<CmpInst>(AltOp);
if (MainOp != AltOp) {
if (isCmpSameOrSwapped(AltInst, Inst, TLI))
@@ -761,6 +957,20 @@ InstructionsState getSameOpcode(ArrayRef<Value *> VL,
if (It != VL.end())
MainOp = AltOp = cast<Instruction>(*It);
}
+ if (IsCmpOp && InterchangeablePred != CmpInst::BAD_ICMP_PREDICATE &&
+ InterchangeablePred != BasePred) {
+ // Every lane is convertible to the shared predicate, so the alternate
+ // operation is never set for such bundles.
+ if (MainOp != AltOp)
+ return InstructionsState::invalid();
+ auto *It = find_if(VL, [&](Value *V) {
+ auto *CI = dyn_cast<ICmpInst>(V);
+ return CI && CI->getPredicate() == InterchangeablePred;
+ });
+ assert(It != VL.end() &&
+ "Expected an instruction with the shared predicate.");
+ MainOp = AltOp = cast<Instruction>(*It);
+ }
assert((MainOp == AltOp || !allSameOpcode(VL)) &&
"Incorrect implementation of allSameOpcode.");
InstructionsState S(MainOp, AltOp);
@@ -784,6 +994,18 @@ convertTo(Instruction *I, const InstructionsState &S) {
// Use args() to skip the trailing callee operand in CallInst::operands().
if (auto *CI = dyn_cast<CallInst>(I))
return std::make_pair(SelectedOp, SmallVector<Value *>(CI->args()));
+ // A comparison lane interchangeable with the main operation (e.g. x == 0
+ // in an x <u C bundle) is emitted with the main predicate and the
+ // adjusted constant.
+ if (auto *MainCI = dyn_cast<ICmpInst>(SelectedOp);
+ MainCI && !S.isAltShuffle()) {
+ auto *CI = cast<ICmpInst>(I);
+ if (CI->getPredicate() != MainCI->getPredicate())
+ if (ConstantInt *C = CmpSamePredicateHelper::getAdjustedConstant(
+ CI, MainCI->getPredicate()))
+ return std::make_pair(SelectedOp,
+ SmallVector<Value *>{CI->getOperand(0), C});
+ }
return std::make_pair(SelectedOp, SmallVector<Value *>(I->operands()));
}
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.h b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.h
index 62c8fe985c4a7..f938a030e1c5d 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.h
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPCompatibilityAnalysis.h
@@ -23,12 +23,15 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/IVDescriptors.h"
#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
#include <cstdint>
#include <utility>
namespace llvm {
+class APInt;
class Constant;
+class ConstantInt;
class TargetLibraryInfo;
class Value;
} // namespace llvm
@@ -130,6 +133,55 @@ class BinOpSameOpcodeHelper {
}
};
+/// Helper class that determines whether a list of integer comparisons can
+/// share a single predicate. InstCombine canonicalizes single-element and
+/// single-complement range comparisons to eq/ne at the type boundaries
+/// (e.g. x <u 1 becomes x == 0); such lanes are interchangeable with the
+/// rest of the list by adjusting the compared constant.
+class CmpSamePredicateHelper {
+ using MaskType = std::uint16_t;
+ /// Bit i represents predicate ICMP_EQ + i.
+ static constexpr unsigned NumPreds = CmpInst::ICMP_SLE - CmpInst::ICMP_EQ + 1;
+ static constexpr MaskType AllPreds = (1 << NumPreds) - 1;
+ /// Intersection of the per-lane convertible predicate sets.
+ MaskType Mask = AllPreds;
+ /// Predicates present in the list natively. The shared predicate must be
+ /// one of them: the main op must be an actual instruction with this
+ /// predicate.
+ MaskType SeenBefore = 0;
+
+ static constexpr MaskType getBit(CmpInst::Predicate P) {
+ return static_cast<MaskType>(1) << (P - CmpInst::ICMP_EQ);
+ }
+ /// Returns the mask of the predicates that can express the comparison
+ /// (Pred, X, C) with an adjusted constant C, including Pred itself.
+ static MaskType getFormsMask(CmpInst::Predicate Pred, const APInt &C);
+ /// Returns the constant of the (Pred, X, C') form equivalent to the
+ /// boundary family (IsComplement, K). Pred must be in the family mask.
+ static APInt getFamilyConstant(bool IsComplement, const APInt &K,
+ CmpInst::Predicate Pred);
+
+public:
+ /// Intersects the convertible predicate set of \p CI with the running
+ /// set. Returns false when the intersection becomes empty.
+ bool add(const ICmpInst *CI);
+ /// Returns the shared predicate, preferring the predicate of
+ /// \p Preferred when the whole list can use it, or BAD_ICMP_PREDICATE
+ /// when the list cannot share a natively present predicate.
+ CmpInst::Predicate getPredicate(const ICmpInst *Preferred) const;
+ /// Returns the predicate the whole list can share, or BAD_ICMP_PREDICATE
+ /// when it cannot share a natively present predicate.
+ static CmpInst::Predicate getSharedPredicate(ArrayRef<Value *> VL,
+ const ICmpInst *Preferred);
+ /// Checks if the comparison \p CI can be expressed with the predicate
+ /// \p Pred by adjusting its constant operand.
+ static bool canConvertTo(const CmpInst *CI, CmpInst::Predicate Pred);
+ /// Returns the adjusted constant operand expressing \p CI with the
+ /// predicate \p Pred, or nullptr if not convertible.
+ static ConstantInt *getAdjustedConstant(const CmpInst *CI,
+ CmpInst::Predicate Pred);
+};
+
/// Main data required for vectorization of instructions.
class InstructionsState {
/// MainOp and AltOp are primarily determined by getSameOpcode. Currently,
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/interchangeable-cmp-predicates.ll b/llvm/test/Transforms/SLPVectorizer/X86/interchangeable-cmp-predicates.ll
index 51156bc66d2c3..53fb104458c81 100644
--- a/llvm/test/Transforms/SLPVectorizer/X86/interchangeable-cmp-predicates.ll
+++ b/llvm/test/Transforms/SLPVectorizer/X86/interchangeable-cmp-predicates.ll
@@ -8,12 +8,9 @@
define <8 x i1> @eq_ult(i16 %x) {
; CHECK-LABEL: define <8 x i1> @eq_ult(
; CHECK-SAME: i16 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> <i16 0, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison>, i16 [[X]], i64 1
-; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> <i32 0, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
-; CHECK-NEXT: [[TMP6:%.*]] = insertelement <8 x i16> <i16 poison, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7, i16 8>, i16 [[X]], i64 0
-; CHECK-NEXT: [[TMP4:%.*]] = icmp eq <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP5:%.*]] = icmp ult <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i1> [[TMP4]], <8 x i1> [[TMP5]], <8 x i32> <i32 0, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> poison, i16 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult <8 x i16> [[TMP2]], <i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7, i16 8>
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
;
%c1 = icmp eq i16 %x, 0
@@ -38,12 +35,9 @@ define <8 x i1> @eq_ult(i16 %x) {
define <8 x i1> @ne_ugt(i16 %x) {
; CHECK-LABEL: define <8 x i1> @ne_ugt(
; CHECK-SAME: i16 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> <i16 0, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison>, i16 [[X]], i64 1
-; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> <i32 0, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
-; CHECK-NEXT: [[TMP6:%.*]] = insertelement <8 x i16> <i16 poison, i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7>, i16 [[X]], i64 0
-; CHECK-NEXT: [[TMP4:%.*]] = icmp ne <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP5:%.*]] = icmp ugt <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i1> [[TMP4]], <8 x i1> [[TMP5]], <8 x i32> <i32 0, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> poison, i16 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ugt <8 x i16> [[TMP2]], <i16 0, i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7>
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
;
%c1 = icmp ne i16 %x, 0
@@ -68,12 +62,9 @@ define <8 x i1> @ne_ugt(i16 %x) {
define <8 x i1> @eq_slt_signed_mins(i16 %x) {
; CHECK-LABEL: define <8 x i1> @eq_slt_signed_mins(
; CHECK-SAME: i16 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> <i16 -32768, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison>, i16 [[X]], i64 1
-; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> <i32 0, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
-; CHECK-NEXT: [[TMP6:%.*]] = insertelement <8 x i16> <i16 poison, i16 -32766, i16 -32765, i16 -32764, i16 -32763, i16 -32762, i16 -32761, i16 -32760>, i16 [[X]], i64 0
-; CHECK-NEXT: [[TMP4:%.*]] = icmp eq <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP5:%.*]] = icmp slt <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i1> [[TMP4]], <8 x i1> [[TMP5]], <8 x i32> <i32 0, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> poison, i16 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP3:%.*]] = icmp slt <8 x i16> [[TMP2]], <i16 -32767, i16 -32766, i16 -32765, i16 -32764, i16 -32763, i16 -32762, i16 -32761, i16 -32760>
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
;
%c1 = icmp eq i16 %x, -32768
@@ -98,12 +89,9 @@ define <8 x i1> @eq_slt_signed_mins(i16 %x) {
define <8 x i1> @ne_sle_signed_maxs(i16 %x) {
; CHECK-LABEL: define <8 x i1> @ne_sle_signed_maxs(
; CHECK-SAME: i16 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> <i16 32767, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison>, i16 [[X]], i64 1
-; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> <i32 0, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
-; CHECK-NEXT: [[TMP6:%.*]] = insertelement <8 x i16> <i16 poison, i16 32765, i16 32764, i16 32763, i16 32762, i16 32761, i16 32760, i16 32759>, i16 [[X]], i64 0
-; CHECK-NEXT: [[TMP4:%.*]] = icmp ne <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP5:%.*]] = icmp sle <8 x i16> [[TMP2]], [[TMP6]]
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i1> [[TMP4]], <8 x i1> [[TMP5]], <8 x i32> <i32 0, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> poison, i16 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP3:%.*]] = icmp sle <8 x i16> [[TMP2]], <i16 32766, i16 32765, i16 32764, i16 32763, i16 32762, i16 32761, i16 32760, i16 32759>
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
;
%c1 = icmp ne i16 %x, 32767
@@ -130,9 +118,7 @@ define <8 x i1> @ule_ult(i16 %x) {
; CHECK-SAME: i16 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i16> poison, i16 [[X]], i64 0
; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> zeroinitializer
-; CHECK-NEXT: [[TMP5:%.*]] = icmp ule <8 x i16> [[TMP2]], <i16 0, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7, i16 8>
-; CHECK-NEXT: [[TMP4:%.*]] = icmp ult <8 x i16> [[TMP2]], <i16 0, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7, i16 8>
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i1> [[TMP5]], <8 x i1> [[TMP4]], <8 x i32> <i32 0, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult <8 x i16> [[TMP2]], <i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7, i16 8>
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
;
%c1 = icmp ule i16 %x, 0
@@ -221,19 +207,9 @@ define <8 x i1> @neg_eq_ne(i16 %x) {
define <8 x i1> @samesign_lanes(i8 %x) {
; CHECK-LABEL: define <8 x i1> @samesign_lanes(
; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT: [[C1:%.*]] = icmp samesign eq i8 [[X]], 0
-; CHECK-NEXT: [[C2:%.*]] = icmp samesign ult i8 [[X]], 2
-; CHECK-NEXT: [[C3:%.*]] = icmp samesign ult i8 [[X]], 3
-; CHECK-NEXT: [[C4:%.*]] = icmp samesign ult i8 [[X]], 4
-; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i8> poison, i8 [[X]], i64 0
-; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <4 x i8> [[TMP1]], <4 x i8> poison, <4 x i32> zeroinitializer
-; CHECK-NEXT: [[TMP5:%.*]] = icmp ult <4 x i8> [[TMP2]], <i8 5, i8 6, i8 7, i8 8>
-; CHECK-NEXT: [[I1:%.*]] = insertelement <8 x i1> poison, i1 [[C1]], i32 0
-; CHECK-NEXT: [[I2:%.*]] = insertelement <8 x i1> [[I1]], i1 [[C2]], i32 1
-; CHECK-NEXT: [[I3:%.*]] = insertelement <8 x i1> [[I2]], i1 [[C3]], i32 2
-; CHECK-NEXT: [[I4:%.*]] = insertelement <8 x i1> [[I3]], i1 [[C4]], i32 3
-; CHECK-NEXT: [[TMP4:%.*]] = shufflevector <4 x i1> [[TMP5]], <4 x i1> poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison>
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i1> [[I4]], <8 x i1> [[TMP4]], <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 8, i32 9, i32 10, i32 11>
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <8 x i8> poison, i8 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <8 x i8> [[TMP1]], <8 x i8> poison, <8 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP3:%.*]] = icmp samesign ult <8 x i8> [[TMP2]], <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8>
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
;
%c1 = icmp samesign eq i8 %x, 0
@@ -259,14 +235,11 @@ define <8 x i1> @samesign_lanes(i8 %x) {
define <4 x i1> @samesign_i1_signflip(i1 %x, i1 %y, i1 %z, i1 %w) {
; CHECK-LABEL: define <4 x i1> @samesign_i1_signflip(
; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]], i1 [[Z:%.*]], i1 [[W:%.*]]) {
-; CHECK-NEXT: [[C1:%.*]] = icmp samesign eq i1 [[X]], false
-; CHECK-NEXT: [[C2:%.*]] = icmp samesign ult i1 [[Y]], true
-; CHECK-NEXT: [[C3:%.*]] = icmp samesign ult i1 [[Z]], true
-; CHECK-NEXT: [[C4:%.*]] = icmp samesign ult i1 [[W]], true
-; CHECK-NEXT: [[I1:%.*]] = insertelement <4 x i1> poison, i1 [[C1]], i32 0
-; CHECK-NEXT: [[I2:%.*]] = insertelement <4 x i1> [[I1]], i1 [[C2]], i32 1
-; CHECK-NEXT: [[I3:%.*]] = insertelement <4 x i1> [[I2]], i1 [[C3]], i32 2
-; CHECK-NEXT: [[TMP5:%.*]] = insertelement <4 x i1> [[I3]], i1 [[C4]], i32 3
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i1> poison, i1 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i1> [[TMP1]], i1 [[Y]], i64 1
+; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i1> [[TMP2]], i1 [[Z]], i64 2
+; CHECK-NEXT: [[TMP4:%.*]] = insertelement <4 x i1> [[TMP3]], i1 [[W]], i64 3
+; CHECK-NEXT: [[TMP5:%.*]] = icmp eq <4 x i1> [[TMP4]], zeroinitializer
; CHECK-NEXT: ret <4 x i1> [[TMP5]]
;
%c1 = icmp samesign eq i1 %x, 0
@@ -313,12 +286,9 @@ define void @selects(ptr noalias %out, ptr addrspace(11) %a, ptr addrspace(11) %
; CHECK-LABEL: define void @selects(
; CHECK-SAME: ptr noalias [[OUT:%.*]], ptr addrspace(11) [[A:%.*]], ptr addrspace(11) [[B:%.*]], i64 [[N:%.*]]) {
; CHECK-NEXT: [[X:%.*]] = trunc i64 [[N]] to i16
-; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i16> <i16 0, i16 poison, i16 poison, i16 poison>, i16 [[X]], i64 1
-; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <4 x i16> [[TMP1]], <4 x i16> poison, <4 x i32> <i32 0, i32 1, i32 1, i32 1>
-; CHECK-NEXT: [[TMP7:%.*]] = insertelement <4 x i16> <i16 poison, i16 2, i16 3, i16 4>, i16 [[X]], i64 0
-; CHECK-NEXT: [[TMP8:%.*]] = icmp eq <4 x i16> [[TMP2]], [[TMP7]]
-; CHECK-NEXT: [[TMP9:%.*]] = icmp ult <4 x i16> [[TMP2]], [[TMP7]]
-; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <4 x i1> [[TMP8]], <4 x i1> [[TMP9]], <4 x i32> <i32 0, i32 5, i32 6, i32 7>
+; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i16> poison, i16 [[X]], i64 0
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <4 x i16> [[TMP1]], <4 x i16> poison, <4 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult <4 x i16> [[TMP2]], <i16 1, i16 2, i16 3, i16 4>
; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i16>, ptr addrspace(11) [[A]], align 2
; CHECK-NEXT: [[TMP5:%.*]] = load <4 x i16>, ptr addrspace(11) [[B]], align 2
; CHECK-NEXT: [[TMP6:%.*]] = select <4 x i1> [[TMP3]], <4 x i16> [[TMP5]], <4 x i16> [[TMP4]]
More information about the llvm-commits
mailing list