Skip to content

[PatternMatching] Add generic API for matching constants using custom conditions #85676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions llvm/include/llvm/IR/PatternMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,39 @@ template <typename Predicate> struct apf_pred_ty : public Predicate {
//
///////////////////////////////////////////////////////////////////////////////

template <typename APTy> struct custom_checkfn {
function_ref<bool(const APTy &)> CheckFn;
bool isValue(const APTy &C) { return CheckFn(C); }
};

/// Match an integer or vector where CheckFn(ele) for each element is true.
/// For vectors, poison elements are assumed to match.
inline cst_pred_ty<custom_checkfn<APInt>>
m_CheckedInt(function_ref<bool(const APInt &)> CheckFn) {
return cst_pred_ty<custom_checkfn<APInt>>{CheckFn};
}

inline api_pred_ty<custom_checkfn<APInt>>
m_CheckedInt(const APInt *&V, function_ref<bool(const APInt &)> CheckFn) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this again just now, what's the point of these methods returning a const APInt *? In that case, isn't this limited to splats anyway and you could just as well use m_APInt + a check and save you the lambda?

I think this should be providing the result as a Constant *, which would support non-splat values matching the predicate...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this... yeah think you are right. Should be a constant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting the norm case seems to be to always export these as APInt, if you looks at something like m_Negative/m_NonNegative its the same deal.

Any objection to me replacing all of these?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming there are not a lot of uses of them, replacing them should be fine. Something to be careful about is that the direct replacement here would be m_APIntAllowPoison, not m_APInt.

If there are many uses, it may make sense to retain them as a convenience (I don't think m_CheckedInt with APInt makes sense as a convenience though, it's strictly more complicated than just writing out the check).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See: #91377 decided not to do the other. Didn't seem like much upside and made code less compact.

api_pred_ty<custom_checkfn<APInt>> P(V);
P.CheckFn = CheckFn;
return P;
}

/// Match a float or vector where CheckFn(ele) for each element is true.
/// For vectors, poison elements are assumed to match.
inline cstfp_pred_ty<custom_checkfn<APFloat>>
m_CheckedFp(function_ref<bool(const APFloat &)> CheckFn) {
return cstfp_pred_ty<custom_checkfn<APFloat>>{CheckFn};
}

inline apf_pred_ty<custom_checkfn<APFloat>>
m_CheckedFp(const APFloat *&V, function_ref<bool(const APFloat &)> CheckFn) {
apf_pred_ty<custom_checkfn<APFloat>> P(V);
P.CheckFn = CheckFn;
return P;
}

struct is_any_apint {
bool isValue(const APInt &C) { return true; }
};
Expand Down
16 changes: 6 additions & 10 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7123,34 +7123,30 @@ Instruction *InstCombinerImpl::foldICmpCommutative(ICmpInst::Predicate Pred,
return replaceInstUsesWith(CxtI, V);

// Folding (X / Y) pred X => X swap(pred) 0 for constant Y other than 0 or 1
auto CheckUGT1 = [](const APInt &Divisor) { return Divisor.ugt(1); };
{
const APInt *Divisor;
if (match(Op0, m_UDiv(m_Specific(Op1), m_APInt(Divisor))) &&
Divisor->ugt(1)) {
if (match(Op0, m_UDiv(m_Specific(Op1), m_CheckedInt(CheckUGT1)))) {
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
Constant::getNullValue(Op1->getType()));
}

if (!ICmpInst::isUnsigned(Pred) &&
match(Op0, m_SDiv(m_Specific(Op1), m_APInt(Divisor))) &&
Divisor->ugt(1)) {
match(Op0, m_SDiv(m_Specific(Op1), m_CheckedInt(CheckUGT1)))) {
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
Constant::getNullValue(Op1->getType()));
}
}

// Another case of this fold is (X >> Y) pred X => X swap(pred) 0 if Y != 0
auto CheckNE0 = [](const APInt &Shift) { return !Shift.isZero(); };
{
const APInt *Shift;
if (match(Op0, m_LShr(m_Specific(Op1), m_APInt(Shift))) &&
!Shift->isZero()) {
if (match(Op0, m_LShr(m_Specific(Op1), m_CheckedInt(CheckNE0)))) {
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
Constant::getNullValue(Op1->getType()));
}

if ((Pred == CmpInst::ICMP_SLT || Pred == CmpInst::ICMP_SGE) &&
match(Op0, m_AShr(m_Specific(Op1), m_APInt(Shift))) &&
!Shift->isZero()) {
match(Op0, m_AShr(m_Specific(Op1), m_CheckedInt(CheckNE0)))) {
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
Constant::getNullValue(Op1->getType()));
}
Expand Down
59 changes: 51 additions & 8 deletions llvm/test/Transforms/InstCombine/icmp-div-constant.ll
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ define i32 @icmp_div(i16 %a, i16 %c) {
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i16 [[A:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[THEN:%.*]], label [[EXIT:%.*]]
; CHECK: then:
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i16 [[C:%.*]], 0
; CHECK-NEXT: [[TMP0:%.*]] = sext i1 [[CMP]] to i32
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i16 [[C:%.*]], 0
; CHECK-NEXT: [[TMP0:%.*]] = sext i1 [[CMP_NOT]] to i32
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ -1, [[ENTRY:%.*]] ], [ [[TMP0]], [[THEN]] ]
Expand Down Expand Up @@ -173,8 +173,8 @@ define i32 @icmp_div3(i16 %a, i16 %c) {
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i16 [[A:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[THEN:%.*]], label [[EXIT:%.*]]
; CHECK: then:
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i16 [[C:%.*]], 0
; CHECK-NEXT: [[TMP0:%.*]] = sext i1 [[CMP]] to i32
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i16 [[C:%.*]], 0
; CHECK-NEXT: [[TMP0:%.*]] = sext i1 [[CMP_NOT]] to i32
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ -1, [[ENTRY:%.*]] ], [ [[TMP0]], [[THEN]] ]
Expand Down Expand Up @@ -381,8 +381,8 @@ define i1 @sdiv_eq_smin_use(i32 %x, i32 %y) {

define i1 @sdiv_x_by_const_cmp_x(i32 %x) {
; CHECK-LABEL: @sdiv_x_by_const_cmp_x(
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[R]]
;
%v = sdiv i32 %x, 13
%r = icmp eq i32 %v, %x
Expand All @@ -399,12 +399,33 @@ define i1 @udiv_x_by_const_cmp_x(i32 %x) {
ret i1 %2
}

define <2 x i1> @udiv_x_by_const_cmp_x_non_splat(<2 x i32> %x) {
; CHECK-LABEL: @udiv_x_by_const_cmp_x_non_splat(
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt <2 x i32> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[TMP1]]
;
%1 = udiv <2 x i32> %x, <i32 123, i32 -123>
%2 = icmp slt <2 x i32> %1, %x
ret <2 x i1> %2
}


define <2 x i1> @sdiv_x_by_const_cmp_x_non_splat(<2 x i32> %x) {
; CHECK-LABEL: @sdiv_x_by_const_cmp_x_non_splat(
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <2 x i32> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[TMP1]]
;
%1 = sdiv <2 x i32> %x, <i32 2, i32 3>
%2 = icmp eq <2 x i32> %1, %x
ret <2 x i1> %2
}

; Same as above but with right shift instead of division (C != 0)

define i1 @lshr_x_by_const_cmp_x(i32 %x) {
; CHECK-LABEL: @lshr_x_by_const_cmp_x(
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[R]]
;
%v = lshr i32 %x, 1
%r = icmp eq i32 %v, %x
Expand All @@ -421,6 +442,28 @@ define <4 x i1> @lshr_by_const_cmp_sle_value(<4 x i32> %x) {
ret <4 x i1> %r
}

define <4 x i1> @lshr_by_const_cmp_sle_value_non_splat(<4 x i32> %x) {
; CHECK-LABEL: @lshr_by_const_cmp_sle_value_non_splat(
; CHECK-NEXT: [[R:%.*]] = icmp sgt <4 x i32> [[X:%.*]], <i32 -1, i32 -1, i32 -1, i32 -1>
; CHECK-NEXT: ret <4 x i1> [[R]]
;
%v = lshr <4 x i32> %x, <i32 3, i32 3, i32 3, i32 5>
%r = icmp sle <4 x i32> %v, %x
ret <4 x i1> %r
}


define <4 x i1> @ashr_by_const_cmp_sge_value_non_splat(<4 x i32> %x) {
; CHECK-LABEL: @ashr_by_const_cmp_sge_value_non_splat(
; CHECK-NEXT: [[R:%.*]] = icmp slt <4 x i32> [[X:%.*]], <i32 1, i32 1, i32 1, i32 1>
; CHECK-NEXT: ret <4 x i1> [[R]]
;
%v = ashr <4 x i32> %x, <i32 1, i32 2, i32 3, i32 4>
%r = icmp sge <4 x i32> %v, %x
ret <4 x i1> %r
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a negative non-splat test that does not pass the check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already had non-splat negative tests (i.e udiv_x_by_const_cmp_eq_value_neg). In retrospect probably should have had those tests be scalars as they didn't really check the old logic, but we can use them here.



define i1 @lshr_by_const_cmp_sge_value(i32 %x) {
; CHECK-LABEL: @lshr_by_const_cmp_sge_value(
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 1
Expand Down
177 changes: 177 additions & 0 deletions llvm/unittests/IR/PatternMatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,134 @@ TEST_F(PatternMatchTest, BitCast) {
EXPECT_FALSE(m_ElementWiseBitCast(m_Value()).match(NXV2I64ToNXV4I32));
}

TEST_F(PatternMatchTest, CheckedInt) {
Type *I8Ty = IRB.getInt8Ty();
const APInt *Res = nullptr;

auto CheckUgt1 = [](const APInt &C) { return C.ugt(1); };
auto CheckTrue = [](const APInt &) { return true; };
auto CheckFalse = [](const APInt &) { return false; };
auto CheckNonZero = [](const APInt &C) { return !C.isZero(); };
auto CheckPow2 = [](const APInt &C) { return C.isPowerOf2(); };

auto DoScalarCheck = [&](int8_t Val) {
APInt APVal(8, Val);
Constant *C = ConstantInt::get(I8Ty, Val);

Res = nullptr;
EXPECT_TRUE(m_CheckedInt(CheckTrue).match(C));
EXPECT_TRUE(m_CheckedInt(Res, CheckTrue).match(C));
EXPECT_EQ(*Res, APVal);

Res = nullptr;
EXPECT_FALSE(m_CheckedInt(CheckFalse).match(C));
EXPECT_FALSE(m_CheckedInt(Res, CheckFalse).match(C));

Res = nullptr;
EXPECT_EQ(CheckUgt1(APVal), m_CheckedInt(CheckUgt1).match(C));
EXPECT_EQ(CheckUgt1(APVal), m_CheckedInt(Res, CheckUgt1).match(C));
if (CheckUgt1(APVal)) {
EXPECT_NE(Res, nullptr);
EXPECT_EQ(*Res, APVal);
}

Res = nullptr;
EXPECT_EQ(CheckNonZero(APVal), m_CheckedInt(CheckNonZero).match(C));
EXPECT_EQ(CheckNonZero(APVal), m_CheckedInt(Res, CheckNonZero).match(C));
if (CheckNonZero(APVal)) {
EXPECT_NE(Res, nullptr);
EXPECT_EQ(*Res, APVal);
}

Res = nullptr;
EXPECT_EQ(CheckPow2(APVal), m_CheckedInt(CheckPow2).match(C));
EXPECT_EQ(CheckPow2(APVal), m_CheckedInt(Res, CheckPow2).match(C));
if (CheckPow2(APVal)) {
EXPECT_NE(Res, nullptr);
EXPECT_EQ(*Res, APVal);
}

};

DoScalarCheck(0);
DoScalarCheck(1);
DoScalarCheck(2);
DoScalarCheck(3);

EXPECT_FALSE(m_CheckedInt(CheckTrue).match(UndefValue::get(I8Ty)));
EXPECT_FALSE(m_CheckedInt(Res, CheckTrue).match(UndefValue::get(I8Ty)));
EXPECT_EQ(Res, nullptr);

EXPECT_FALSE(m_CheckedInt(CheckFalse).match(UndefValue::get(I8Ty)));
EXPECT_FALSE(m_CheckedInt(Res, CheckFalse).match(UndefValue::get(I8Ty)));
EXPECT_EQ(Res, nullptr);

EXPECT_FALSE(m_CheckedInt(CheckTrue).match(PoisonValue::get(I8Ty)));
EXPECT_FALSE(m_CheckedInt(Res, CheckTrue).match(PoisonValue::get(I8Ty)));
EXPECT_EQ(Res, nullptr);

EXPECT_FALSE(m_CheckedInt(CheckFalse).match(PoisonValue::get(I8Ty)));
EXPECT_FALSE(m_CheckedInt(Res, CheckFalse).match(PoisonValue::get(I8Ty)));
EXPECT_EQ(Res, nullptr);

auto DoVecCheckImpl = [&](ArrayRef<std::optional<int8_t>> Vals,
function_ref<bool(const APInt &)> CheckFn,
bool UndefAsPoison) {
SmallVector<Constant *> VecElems;
std::optional<bool> Okay;
bool AllSame = true;
bool HasUndef = false;
std::optional<APInt> First;
for (const std::optional<int8_t> &Val : Vals) {
if (!Val.has_value()) {
VecElems.push_back(UndefAsPoison ? PoisonValue::get(I8Ty)
: UndefValue::get(I8Ty));
HasUndef = true;
} else {
if (!Okay.has_value())
Okay = true;
APInt APVal(8, *Val);
if (!First.has_value())
First = APVal;
else
AllSame &= First->eq(APVal);
Okay = *Okay && CheckFn(APVal);
VecElems.push_back(ConstantInt::get(I8Ty, *Val));
}
}

Constant *C = ConstantVector::get(VecElems);
EXPECT_EQ(!(HasUndef && !UndefAsPoison) && Okay.value_or(false),
m_CheckedInt(CheckFn).match(C));

Res = nullptr;
bool Expec =
!(HasUndef && !UndefAsPoison) && AllSame && Okay.value_or(false);
EXPECT_EQ(Expec, m_CheckedInt(Res, CheckFn).match(C));
if (Expec) {
EXPECT_NE(Res, nullptr);
EXPECT_EQ(*Res, *First);
}
};
auto DoVecCheck = [&](ArrayRef<std::optional<int8_t>> Vals) {
DoVecCheckImpl(Vals, CheckTrue, /*UndefAsPoison=*/false);
DoVecCheckImpl(Vals, CheckFalse, /*UndefAsPoison=*/false);
DoVecCheckImpl(Vals, CheckTrue, /*UndefAsPoison=*/true);
DoVecCheckImpl(Vals, CheckFalse, /*UndefAsPoison=*/true);
DoVecCheckImpl(Vals, CheckUgt1, /*UndefAsPoison=*/false);
DoVecCheckImpl(Vals, CheckNonZero, /*UndefAsPoison=*/false);
DoVecCheckImpl(Vals, CheckPow2, /*UndefAsPoison=*/false);
};

DoVecCheck({0, 1});
DoVecCheck({1, 1});
DoVecCheck({1, 2});
DoVecCheck({1, std::nullopt});
DoVecCheck({1, std::nullopt, 1});
DoVecCheck({1, std::nullopt, 2});
DoVecCheck({std::nullopt, std::nullopt, std::nullopt});
}

TEST_F(PatternMatchTest, Power2) {
Value *C128 = IRB.getInt32(128);
Value *CNeg128 = ConstantExpr::getNeg(cast<Constant>(C128));
Expand Down Expand Up @@ -1397,21 +1525,58 @@ TEST_F(PatternMatchTest, VectorUndefFloat) {
EXPECT_FALSE(match(VectorInfPoison, m_Finite()));
EXPECT_FALSE(match(VectorNaNPoison, m_Finite()));

auto CheckTrue = [](const APFloat &) { return true; };
EXPECT_FALSE(match(VectorZeroUndef, m_CheckedFp(CheckTrue)));
EXPECT_TRUE(match(VectorZeroPoison, m_CheckedFp(CheckTrue)));
EXPECT_TRUE(match(ScalarPosInf, m_CheckedFp(CheckTrue)));
EXPECT_TRUE(match(ScalarNegInf, m_CheckedFp(CheckTrue)));
EXPECT_TRUE(match(ScalarNaN, m_CheckedFp(CheckTrue)));
EXPECT_FALSE(match(VectorInfUndef, m_CheckedFp(CheckTrue)));
EXPECT_TRUE(match(VectorInfPoison, m_CheckedFp(CheckTrue)));
EXPECT_FALSE(match(VectorNaNUndef, m_CheckedFp(CheckTrue)));
EXPECT_TRUE(match(VectorNaNPoison, m_CheckedFp(CheckTrue)));

auto CheckFalse = [](const APFloat &) { return false; };
EXPECT_FALSE(match(VectorZeroUndef, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(VectorZeroPoison, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(ScalarPosInf, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(ScalarNegInf, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(ScalarNaN, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(VectorInfUndef, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(VectorInfPoison, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(VectorNaNUndef, m_CheckedFp(CheckFalse)));
EXPECT_FALSE(match(VectorNaNPoison, m_CheckedFp(CheckFalse)));

auto CheckNonNaN = [](const APFloat &C) { return !C.isNaN(); };
EXPECT_FALSE(match(VectorZeroUndef, m_CheckedFp(CheckNonNaN)));
EXPECT_TRUE(match(VectorZeroPoison, m_CheckedFp(CheckNonNaN)));
EXPECT_TRUE(match(ScalarPosInf, m_CheckedFp(CheckNonNaN)));
EXPECT_TRUE(match(ScalarNegInf, m_CheckedFp(CheckNonNaN)));
EXPECT_FALSE(match(ScalarNaN, m_CheckedFp(CheckNonNaN)));
EXPECT_FALSE(match(VectorInfUndef, m_CheckedFp(CheckNonNaN)));
EXPECT_TRUE(match(VectorInfPoison, m_CheckedFp(CheckNonNaN)));
EXPECT_FALSE(match(VectorNaNUndef, m_CheckedFp(CheckNonNaN)));
EXPECT_FALSE(match(VectorNaNPoison, m_CheckedFp(CheckNonNaN)));

const APFloat *C;
// Regardless of whether poison is allowed,
// a fully undef/poison constant does not match.
EXPECT_FALSE(match(ScalarUndef, m_APFloat(C)));
EXPECT_FALSE(match(ScalarUndef, m_APFloatForbidPoison(C)));
EXPECT_FALSE(match(ScalarUndef, m_APFloatAllowPoison(C)));
EXPECT_FALSE(match(ScalarUndef, m_CheckedFp(C, CheckTrue)));
EXPECT_FALSE(match(VectorUndef, m_APFloat(C)));
EXPECT_FALSE(match(VectorUndef, m_APFloatForbidPoison(C)));
EXPECT_FALSE(match(VectorUndef, m_APFloatAllowPoison(C)));
EXPECT_FALSE(match(VectorUndef, m_CheckedFp(C, CheckTrue)));
EXPECT_FALSE(match(ScalarPoison, m_APFloat(C)));
EXPECT_FALSE(match(ScalarPoison, m_APFloatForbidPoison(C)));
EXPECT_FALSE(match(ScalarPoison, m_APFloatAllowPoison(C)));
EXPECT_FALSE(match(ScalarPoison, m_CheckedFp(C, CheckTrue)));
EXPECT_FALSE(match(VectorPoison, m_APFloat(C)));
EXPECT_FALSE(match(VectorPoison, m_APFloatForbidPoison(C)));
EXPECT_FALSE(match(VectorPoison, m_APFloatAllowPoison(C)));
EXPECT_FALSE(match(VectorPoison, m_CheckedFp(C, CheckTrue)));

// We can always match simple constants and simple splats.
C = nullptr;
Expand All @@ -1432,6 +1597,12 @@ TEST_F(PatternMatchTest, VectorUndefFloat) {
C = nullptr;
EXPECT_TRUE(match(VectorZero, m_APFloatAllowPoison(C)));
EXPECT_TRUE(C->isZero());
C = nullptr;
EXPECT_TRUE(match(VectorZero, m_CheckedFp(C, CheckTrue)));
EXPECT_TRUE(C->isZero());
C = nullptr;
EXPECT_TRUE(match(VectorZero, m_CheckedFp(C, CheckNonNaN)));
EXPECT_TRUE(C->isZero());

// Splats with undef are never allowed.
// Whether splats with poison can be matched depends on the matcher.
Expand All @@ -1456,6 +1627,12 @@ TEST_F(PatternMatchTest, VectorUndefFloat) {
C = nullptr;
EXPECT_TRUE(match(VectorZeroPoison, m_Finite(C)));
EXPECT_TRUE(C->isZero());
C = nullptr;
EXPECT_TRUE(match(VectorZeroPoison, m_CheckedFp(C, CheckTrue)));
EXPECT_TRUE(C->isZero());
C = nullptr;
EXPECT_TRUE(match(VectorZeroPoison, m_CheckedFp(C, CheckNonNaN)));
EXPECT_TRUE(C->isZero());
}

TEST_F(PatternMatchTest, FloatingPointFNeg) {
Expand Down
Loading