[llvm] [InstCombine] Fold scmp/ucmp of extended operands (PR #218871)
Aayush Shrivastava via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 02:49:07 PDT 2026
https://github.com/iamaayushrivastava created https://github.com/llvm/llvm-project/pull/218871
Fixes #202467
`scmp`/`ucmp` compare their operands, and both `sext` and `zext` are monotonic with respect to the unsigned order (`sext` additionally preserves the signed order). So a common extension on both operands can be dropped and the compare done in the narrower type:
scmp (sext X), (sext Y) --> scmp X, Y
scmp (zext X), (zext Y) --> ucmp X, Y
ucmp (ext X), (ext Y) --> ucmp X, Y
Zero-extended values are non-negative, which is why a signed compare of them becomes an unsigned compare of the narrow operands.
Both operands must use the same extend opcode and source type. Since these intrinsics are not commutative, the extend is looked for on either side, and a constant operand is narrowed instead when truncating and re-extending it gives back the same constant (`getLosslessInvCast`). No one-use restriction is needed since no new cast is created.
This also adds the previously missing `Intrinsic::ucmp` case to `visitCallInst`.
>From ff4a3017f297c97405eac42da30446783ba6e937 Mon Sep 17 00:00:00 2001
From: iamaayushrivastava <iamaayushrivastava at gmail.com>
Date: Wed, 26 Aug 2026 15:13:27 +0530
Subject: [PATCH] [InstCombine] Fold scmp/ucmp of extended operands
---
.../InstCombine/InstCombineCalls.cpp | 63 ++++++-
llvm/test/Transforms/InstCombine/scmp.ll | 178 ++++++++++++++++++
llvm/test/Transforms/InstCombine/ucmp.ll | 149 +++++++++++++++
3 files changed, 389 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 8a9c99eb36f66..1402c12f2025f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1984,6 +1984,60 @@ static Value *foldSinAndCosToSinCos(IntrinsicInst *II, IRBuilderBase &B,
return IsSin ? Sin : Cos;
}
+/// Fold an scmp/ucmp intrinsic whose operands are extended from a narrower
+/// type:
+/// scmp (sext X), (sext Y) --> scmp X, Y
+/// scmp (zext X), (zext Y) --> ucmp X, Y
+/// ucmp (ext X), (ext Y) --> ucmp X, Y
+/// Both operands must use the same extend opcode and source type. A constant
+/// operand is narrowed instead, if truncating and re-extending it gives back
+/// the same constant.
+static Value *foldCmpIntrinsicOfExtended(IntrinsicInst *II,
+ InstCombiner::BuilderTy &Builder,
+ const DataLayout &DL) {
+ // scmp/ucmp are not commutative, so the extend may be on either side.
+ unsigned ExtIdx = 0;
+ Value *X;
+ if (!match(II->getArgOperand(0), m_ZExtOrSExt(m_Value(X)))) {
+ ExtIdx = 1;
+ if (!match(II->getArgOperand(1), m_ZExtOrSExt(m_Value(X))))
+ return nullptr;
+ }
+
+ auto CastOpc = static_cast<Instruction::CastOps>(
+ cast<Operator>(II->getArgOperand(ExtIdx))->getOpcode());
+ Type *NarrowTy = X->getType();
+
+ // The other operand must be the same kind of extend from the same type, or a
+ // constant that can be narrowed losslessly.
+ Value *OtherOp = II->getArgOperand(1 - ExtIdx);
+ Value *Y;
+ Constant *WideC;
+ if (match(OtherOp, m_ZExtOrSExt(m_Value(Y)))) {
+ if (cast<Operator>(OtherOp)->getOpcode() != CastOpc ||
+ Y->getType() != NarrowTy)
+ return nullptr;
+ } else if (match(OtherOp, m_ImmConstant(WideC))) {
+ Y = getLosslessInvCast(WideC, NarrowTy, CastOpc, DL);
+ if (!Y)
+ return nullptr;
+ } else {
+ return nullptr;
+ }
+
+ // Both extends preserve the unsigned order, so an unsigned compare of the
+ // narrow operands is always equivalent. The signed order is only preserved by
+ // sext; zero extended values are non-negative, so a signed compare of those
+ // is an unsigned compare of the narrow operands.
+ Intrinsic::ID NewIID =
+ II->getIntrinsicID() == Intrinsic::scmp && CastOpc == Instruction::SExt
+ ? Intrinsic::scmp
+ : Intrinsic::ucmp;
+ if (ExtIdx != 0)
+ std::swap(X, Y);
+ return Builder.CreateIntrinsic(II->getType(), NewIID, {X, Y});
+}
+
/// CallInst simplification. This mostly only handles folding of intrinsic
/// instructions. For normal calls, it allows visitCallBase to do the heavy
/// lifting.
@@ -2482,7 +2536,14 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
break;
}
- case Intrinsic::scmp: {
+ case Intrinsic::scmp:
+ case Intrinsic::ucmp: {
+ if (Value *V = foldCmpIntrinsicOfExtended(II, Builder, DL))
+ return replaceInstUsesWith(CI, V);
+
+ if (IID == Intrinsic::ucmp)
+ break;
+
Value *I0 = II->getArgOperand(0), *I1 = II->getArgOperand(1);
// scmp(X, 0) -> sext_or_trunc(X) if X is known to be one of -1, 0, 1.
diff --git a/llvm/test/Transforms/InstCombine/scmp.ll b/llvm/test/Transforms/InstCombine/scmp.ll
index 4352834fa6e00..9d55843e34815 100644
--- a/llvm/test/Transforms/InstCombine/scmp.ll
+++ b/llvm/test/Transforms/InstCombine/scmp.ll
@@ -1019,3 +1019,181 @@ define i8 @scmp_zero_of_ucmp(i32 %x, i32 %y) {
%r = call i8 @llvm.scmp.i8.i64(i64 %cmp, i64 0)
ret i8 %r
}
+
+; scmp(sext(X), sext(Y)) -> scmp(X, Y): sign extension preserves the signed
+; order, so the compare can be done in the narrower type.
+define i8 @scmp_of_sexts(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @scmp_of_sexts(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %sy = sext i32 %y to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %sx, i64 %sy)
+ ret i8 %r
+}
+
+; scmp(zext(X), zext(Y)) -> ucmp(X, Y): both operands are non-negative, so the
+; signed compare of the wide values is an unsigned compare of the narrow ones.
+define i8 @scmp_of_zexts(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @scmp_of_zexts(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %zy = zext i32 %y to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %zx, i64 %zy)
+ ret i8 %r
+}
+
+define <4 x i8> @scmp_of_sexts_vec(<4 x i16> %x, <4 x i16> %y) {
+; CHECK-LABEL: define <4 x i8> @scmp_of_sexts_vec(
+; CHECK-SAME: <4 x i16> [[X:%.*]], <4 x i16> [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call <4 x i8> @llvm.scmp.v4i8.v4i16(<4 x i16> [[X]], <4 x i16> [[Y]])
+; CHECK-NEXT: ret <4 x i8> [[R]]
+;
+ %sx = sext <4 x i16> %x to <4 x i32>
+ %sy = sext <4 x i16> %y to <4 x i32>
+ %r = call <4 x i8> @llvm.scmp.v4i8.v4i32(<4 x i32> %sx, <4 x i32> %sy)
+ ret <4 x i8> %r
+}
+
+; The extends may have other uses: no new cast is created and the compare only
+; gets narrower.
+define i8 @scmp_of_sexts_multiuse(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @scmp_of_sexts_multiuse(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SX:%.*]] = sext i32 [[X]] to i64
+; CHECK-NEXT: [[SY:%.*]] = sext i32 [[Y]] to i64
+; CHECK-NEXT: call void @use64(i64 [[SX]])
+; CHECK-NEXT: call void @use64(i64 [[SY]])
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %sy = sext i32 %y to i64
+ call void @use64(i64 %sx)
+ call void @use64(i64 %sy)
+ %r = call i8 @llvm.scmp.i8.i64(i64 %sx, i64 %sy)
+ ret i8 %r
+}
+
+; Negative test: the extends use different opcodes.
+define i8 @scmp_of_sext_zext(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @scmp_of_sext_zext(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SX:%.*]] = sext i32 [[X]] to i64
+; CHECK-NEXT: [[ZY:%.*]] = zext i32 [[Y]] to i64
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i64(i64 [[SX]], i64 [[ZY]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %zy = zext i32 %y to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %sx, i64 %zy)
+ ret i8 %r
+}
+
+; Negative test: the extends have different source types.
+define i8 @scmp_of_sexts_different_src_ty(i32 %x, i16 %y) {
+; CHECK-LABEL: define i8 @scmp_of_sexts_different_src_ty(
+; CHECK-SAME: i32 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT: [[SX:%.*]] = sext i32 [[X]] to i64
+; CHECK-NEXT: [[SY:%.*]] = sext i16 [[Y]] to i64
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i64(i64 [[SX]], i64 [[SY]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %sy = sext i16 %y to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %sx, i64 %sy)
+ ret i8 %r
+}
+
+define i8 @scmp_sext_const(i32 %x) {
+; CHECK-LABEL: define i8 @scmp_sext_const(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X]], i32 -42)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %sx, i64 -42)
+ ret i8 %r
+}
+
+define i8 @scmp_const_sext(i32 %x) {
+; CHECK-LABEL: define i8 @scmp_const_sext(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 -42, i32 [[X]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 -42, i64 %sx)
+ ret i8 %r
+}
+
+define i8 @scmp_zext_const(i32 %x) {
+; CHECK-LABEL: define i8 @scmp_zext_const(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 42)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %zx, i64 42)
+ ret i8 %r
+}
+
+define <2 x i8> @scmp_sext_const_vec(<2 x i16> %x) {
+; CHECK-LABEL: define <2 x i8> @scmp_sext_const_vec(
+; CHECK-SAME: <2 x i16> [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.scmp.v2i8.v2i16(<2 x i16> [[X]], <2 x i16> <i16 -7, i16 3>)
+; CHECK-NEXT: ret <2 x i8> [[R]]
+;
+ %sx = sext <2 x i16> %x to <2 x i32>
+ %r = call <2 x i8> @llvm.scmp.v2i8.v2i32(<2 x i32> %sx, <2 x i32> <i32 -7, i32 3>)
+ ret <2 x i8> %r
+}
+
+; The constant is not representable in the narrow type, so the extend is not
+; dropped; the compare is resolved by range analysis instead.
+define i8 @scmp_sext_const_not_narrowable(i8 %x) {
+; CHECK-LABEL: define i8 @scmp_sext_const_not_narrowable(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -1
+;
+ %sx = sext i8 %x to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %sx, i64 1000)
+ ret i8 %r
+}
+
+; Negative test: one of the constant lanes is not representable in the narrow
+; type, so the whole constant cannot be narrowed.
+define <2 x i8> @scmp_sext_const_vec_not_narrowable(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i8> @scmp_sext_const_vec_not_narrowable(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT: [[SX:%.*]] = sext <2 x i8> [[X]] to <2 x i16>
+; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.scmp.v2i8.v2i16(<2 x i16> [[SX]], <2 x i16> <i16 5, i16 300>)
+; CHECK-NEXT: ret <2 x i8> [[R]]
+;
+ %sx = sext <2 x i8> %x to <2 x i16>
+ %r = call <2 x i8> @llvm.scmp.v2i8.v2i16(<2 x i16> %sx, <2 x i16> <i16 5, i16 300>)
+ ret <2 x i8> %r
+}
+
+; Real-world motivating case (Rust `<[u8; 32]>::cmp`): the memcmp result is
+; sign extended only to feed scmp against zero.
+define i8 @scmp_zero_of_sext_memcmp(ptr %x, ptr %y) {
+; CHECK-LABEL: define i8 @scmp_zero_of_sext_memcmp(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
+; CHECK-NEXT: [[CMP:%.*]] = call i32 @memcmp(ptr noundef nonnull dereferenceable(32) [[X]], ptr noundef nonnull dereferenceable(32) [[Y]], i64 32)
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[CMP]], i32 0)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp = call i32 @memcmp(ptr %x, ptr %y, i64 32)
+ %s = sext i32 %cmp to i64
+ %r = call i8 @llvm.scmp.i8.i64(i64 %s, i64 0)
+ ret i8 %r
+}
+
+declare void @use64(i64 %value)
+declare i32 @memcmp(ptr, ptr, i64)
diff --git a/llvm/test/Transforms/InstCombine/ucmp.ll b/llvm/test/Transforms/InstCombine/ucmp.ll
index 3ccfb4f6fd6e6..1f53617d9cf45 100644
--- a/llvm/test/Transforms/InstCombine/ucmp.ll
+++ b/llvm/test/Transforms/InstCombine/ucmp.ll
@@ -622,3 +622,152 @@ define i8 @trunc_ucmp_multiuse(i32 %x, i32 %y) {
%tr = trunc i32 %cmp to i8
ret i8 %tr
}
+
+; ucmp(zext(X), zext(Y)) -> ucmp(X, Y): zero extension preserves the unsigned
+; order, so the compare can be done in the narrower type.
+define i8 @ucmp_of_zexts(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @ucmp_of_zexts(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %zy = zext i32 %y to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %zx, i64 %zy)
+ ret i8 %r
+}
+
+; ucmp(sext(X), sext(Y)) -> ucmp(X, Y): sign extension is monotonic with
+; respect to the unsigned order too.
+define i8 @ucmp_of_sexts(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @ucmp_of_sexts(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %sy = sext i32 %y to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %sx, i64 %sy)
+ ret i8 %r
+}
+
+define <4 x i8> @ucmp_of_zexts_vec(<4 x i16> %x, <4 x i16> %y) {
+; CHECK-LABEL: define <4 x i8> @ucmp_of_zexts_vec(
+; CHECK-SAME: <4 x i16> [[X:%.*]], <4 x i16> [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call <4 x i8> @llvm.ucmp.v4i8.v4i16(<4 x i16> [[X]], <4 x i16> [[Y]])
+; CHECK-NEXT: ret <4 x i8> [[R]]
+;
+ %zx = zext <4 x i16> %x to <4 x i32>
+ %zy = zext <4 x i16> %y to <4 x i32>
+ %r = call <4 x i8> @llvm.ucmp.v4i8.v4i32(<4 x i32> %zx, <4 x i32> %zy)
+ ret <4 x i8> %r
+}
+
+define i8 @ucmp_of_zexts_multiuse(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @ucmp_of_zexts_multiuse(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ZX:%.*]] = zext i32 [[X]] to i64
+; CHECK-NEXT: [[ZY:%.*]] = zext i32 [[Y]] to i64
+; CHECK-NEXT: call void @use64(i64 [[ZX]])
+; CHECK-NEXT: call void @use64(i64 [[ZY]])
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %zy = zext i32 %y to i64
+ call void @use64(i64 %zx)
+ call void @use64(i64 %zy)
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %zx, i64 %zy)
+ ret i8 %r
+}
+
+; Negative test: the extends use different opcodes.
+define i8 @ucmp_of_zext_sext(i32 %x, i32 %y) {
+; CHECK-LABEL: define i8 @ucmp_of_zext_sext(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ZX:%.*]] = zext i32 [[X]] to i64
+; CHECK-NEXT: [[SY:%.*]] = sext i32 [[Y]] to i64
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i64(i64 [[ZX]], i64 [[SY]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %sy = sext i32 %y to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %zx, i64 %sy)
+ ret i8 %r
+}
+
+; Negative test: the extends have different source types.
+define i8 @ucmp_of_zexts_different_src_ty(i32 %x, i16 %y) {
+; CHECK-LABEL: define i8 @ucmp_of_zexts_different_src_ty(
+; CHECK-SAME: i32 [[X:%.*]], i16 [[Y:%.*]]) {
+; CHECK-NEXT: [[ZX:%.*]] = zext i32 [[X]] to i64
+; CHECK-NEXT: [[ZY:%.*]] = zext i16 [[Y]] to i64
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i64(i64 [[ZX]], i64 [[ZY]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %zy = zext i16 %y to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %zx, i64 %zy)
+ ret i8 %r
+}
+
+define i8 @ucmp_zext_const(i32 %x) {
+; CHECK-LABEL: define i8 @ucmp_zext_const(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 42)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %zx, i64 42)
+ ret i8 %r
+}
+
+define i8 @ucmp_const_zext(i32 %x) {
+; CHECK-LABEL: define i8 @ucmp_const_zext(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 42, i32 [[X]])
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %zx = zext i32 %x to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 42, i64 %zx)
+ ret i8 %r
+}
+
+define i8 @ucmp_sext_const(i32 %x) {
+; CHECK-LABEL: define i8 @ucmp_sext_const(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 -42)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %sx = sext i32 %x to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %sx, i64 -42)
+ ret i8 %r
+}
+
+; The constant is not representable in the narrow type, so the extend is not
+; dropped; the compare is resolved by range analysis instead.
+define i8 @ucmp_zext_const_not_narrowable(i8 %x) {
+; CHECK-LABEL: define i8 @ucmp_zext_const_not_narrowable(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -1
+;
+ %zx = zext i8 %x to i64
+ %r = call i8 @llvm.ucmp.i8.i64(i64 %zx, i64 1000)
+ ret i8 %r
+}
+
+; Negative test: one of the constant lanes is not representable in the narrow
+; type, so the whole constant cannot be narrowed.
+define <2 x i8> @ucmp_zext_const_vec_not_narrowable(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i8> @ucmp_zext_const_vec_not_narrowable(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT: [[ZX:%.*]] = zext <2 x i8> [[X]] to <2 x i16>
+; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.ucmp.v2i8.v2i16(<2 x i16> [[ZX]], <2 x i16> <i16 5, i16 300>)
+; CHECK-NEXT: ret <2 x i8> [[R]]
+;
+ %zx = zext <2 x i8> %x to <2 x i16>
+ %r = call <2 x i8> @llvm.ucmp.v2i8.v2i16(<2 x i16> %zx, <2 x i16> <i16 5, i16 300>)
+ ret <2 x i8> %r
+}
+
+declare void @use64(i64 %value)
More information about the llvm-commits
mailing list