[llvm] [InstCombine] Handle sext/zext of i1 "selects" in canonicalizeClampLike (PR #227067)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 01:53:26 PDT 2026
https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/227067
>From a92e34dc04c82b9f35af2889c6b2a0072e69bb32 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 28 Sep 2026 18:28:38 +0100
Subject: [PATCH 1/2] Precommit test
---
.../InstCombine/truncating-saturate.ll | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/truncating-saturate.ll b/llvm/test/Transforms/InstCombine/truncating-saturate.ll
index 9bebb3dcce94b..e3a1eac7bcf6f 100644
--- a/llvm/test/Transforms/InstCombine/truncating-saturate.ll
+++ b/llvm/test/Transforms/InstCombine/truncating-saturate.ll
@@ -911,3 +911,37 @@ entry:
%trunc = trunc i32 %cond to i8
ret i8 %trunc
}
+
+define i8 @trunc_sext_i1(i32 %x) {
+; CHECK-LABEL: @trunc_sext_i1(
+; CHECK-NEXT: [[LT:%.*]] = icmp ult i32 [[X:%.*]], 256
+; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[X]], -1
+; CHECK-NEXT: [[S:%.*]] = sext i1 [[C]] to i8
+; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X]] to i8
+; CHECK-NEXT: [[R:%.*]] = select i1 [[LT]], i8 [[T]], i8 [[S]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lt = icmp ult i32 %x, 256
+ %c = icmp sgt i32 %x, -1
+ %s = sext i1 %c to i8
+ %t = trunc nuw i32 %x to i8
+ %r = select i1 %lt, i8 %t, i8 %s
+ ret i8 %r
+}
+
+define i8 @trunc_zext_i1(i32 %x) {
+; CHECK-LABEL: @trunc_zext_i1(
+; CHECK-NEXT: [[LT:%.*]] = icmp ult i32 [[X:%.*]], 2
+; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C]] to i8
+; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X]] to i8
+; CHECK-NEXT: [[R:%.*]] = select i1 [[LT]], i8 [[T]], i8 [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lt = icmp ult i32 %x, 2
+ %c = icmp sgt i32 %x, 0
+ %z = zext i1 %c to i8
+ %t = trunc nuw i32 %x to i8
+ %r = select i1 %lt, i8 %t, i8 %z
+ ret i8 %r
+}
>From 97d1c6af0d827bf5838b1ec55a13b63dab4d4d0e Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 28 Sep 2026 22:28:02 +0800
Subject: [PATCH 2/2] [InstCombine] Handle sext/zext of i1 "selects" in
canonicalizeClampLike
---
.../InstCombine/InstCombineSelect.cpp | 6 +--
.../InstCombine/truncating-saturate.ll | 42 ++++++++-----------
2 files changed, 20 insertions(+), 28 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index e4d95eb2beb19..907ea6095b4a1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1886,7 +1886,7 @@ static Value *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
m_CombineAnd(m_AnyIntegralConstant(), m_Constant(C0))))
return nullptr;
- if (!isa<SelectInst>(Sel1)) {
+ if (!match(Sel1, m_SelectLike(m_Value(), m_Value(), m_Value()))) {
Pred0 = ICmpInst::getInversePredicate(Pred0);
std::swap(X, Sel1);
}
@@ -1944,8 +1944,8 @@ static Value *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
CmpPredicate Pred1;
Constant *C2;
Value *ReplacementLow, *ReplacementHigh;
- if (!match(Sel1, m_Select(m_Value(Cmp1), m_Value(ReplacementLow),
- m_Value(ReplacementHigh))) ||
+ if (!match(Sel1, m_SelectLike(m_Value(Cmp1), m_Value(ReplacementLow),
+ m_Value(ReplacementHigh))) ||
!match(Cmp1,
m_ICmp(Pred1, m_Specific(X),
m_CombineAnd(m_AnyIntegralConstant(), m_Constant(C2)))))
diff --git a/llvm/test/Transforms/InstCombine/truncating-saturate.ll b/llvm/test/Transforms/InstCombine/truncating-saturate.ll
index e3a1eac7bcf6f..31fa0c4600474 100644
--- a/llvm/test/Transforms/InstCombine/truncating-saturate.ll
+++ b/llvm/test/Transforms/InstCombine/truncating-saturate.ll
@@ -619,7 +619,7 @@ define i8 @clamp_i16_to_i8(i16 %x) {
; CHECK-LABEL: @clamp_i16_to_i8(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = call i16 @llvm.smax.i16(i16 [[X:%.*]], i16 0)
-; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.smin.i16(i16 [[TMP0]], i16 255)
+; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.umin.i16(i16 [[TMP0]], i16 255)
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i16 [[TMP1]] to i8
; CHECK-NEXT: ret i8 [[TRUNC]]
;
@@ -636,7 +636,7 @@ define i8 @clamp_i32_to_i8(i32 %x) {
; CHECK-LABEL: @clamp_i32_to_i8(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.smax.i32(i32 [[X:%.*]], i32 0)
-; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 255)
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[TMP0]], i32 255)
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i32 [[TMP1]] to i8
; CHECK-NEXT: ret i8 [[TRUNC]]
;
@@ -653,7 +653,7 @@ define i8 @clamp_i64_to_i8(i64 %x) {
; CHECK-LABEL: @clamp_i64_to_i8(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.smax.i64(i64 [[X:%.*]], i64 0)
-; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 255)
+; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP0]], i64 255)
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 [[TMP1]] to i8
; CHECK-NEXT: ret i8 [[TRUNC]]
;
@@ -670,7 +670,7 @@ define i16 @clamp_i32_to_i16(i32 %x) {
; CHECK-LABEL: @clamp_i32_to_i16(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.smax.i32(i32 [[X:%.*]], i32 0)
-; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 65535)
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[TMP0]], i32 65535)
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i32 [[TMP1]] to i16
; CHECK-NEXT: ret i16 [[TRUNC]]
;
@@ -687,7 +687,7 @@ define i16 @clamp_i64_to_i16(i64 %x) {
; CHECK-LABEL: @clamp_i64_to_i16(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.smax.i64(i64 [[X:%.*]], i64 0)
-; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 65535)
+; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP0]], i64 65535)
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 [[TMP1]] to i16
; CHECK-NEXT: ret i16 [[TRUNC]]
;
@@ -704,7 +704,7 @@ define i32 @clamp_i64_to_i32(i64 %x) {
; CHECK-LABEL: @clamp_i64_to_i32(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.smax.i64(i64 [[X:%.*]], i64 0)
-; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 4294967295)
+; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP0]], i64 4294967295)
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 [[TMP1]] to i32
; CHECK-NEXT: ret i32 [[TRUNC]]
;
@@ -743,10 +743,9 @@ entry:
define i8 @no_clamp_i32_to_i8_multiple_use_select(i32 %x, ptr %p) {
; CHECK-LABEL: @no_clamp_i32_to_i8_multiple_use_select(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp ult i32 [[X:%.*]], 256
-; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[X]], 0
-; CHECK-NEXT: [[SHR:%.*]] = sext i1 [[TMP0]] to i32
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL_NOT]], i32 [[X]], i32 [[SHR]]
+; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[X:%.*]], 255
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 0)
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[TMP0]], i32 -1, i32 [[TMP1]]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[COND]] to i8
; CHECK-NEXT: [[EXTRA_USE:%.*]] = sext i32 [[COND]] to i64
; CHECK-NEXT: store i64 [[EXTRA_USE]], ptr [[P:%.*]], align 8
@@ -766,11 +765,9 @@ entry:
define <4 x i8> @no_clamp_i32_to_i8_vector(<4 x i32> %x) {
; CHECK-LABEL: @no_clamp_i32_to_i8_vector(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp ult <4 x i32> [[X:%.*]], splat (i32 256)
-; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt <4 x i32> [[X]], zeroinitializer
-; CHECK-NEXT: [[SHR:%.*]] = sext <4 x i1> [[TMP0]] to <4 x i32>
-; CHECK-NEXT: [[COND:%.*]] = select <4 x i1> [[TOBOOL_NOT]], <4 x i32> [[X]], <4 x i32> [[SHR]]
-; CHECK-NEXT: [[TRUNC:%.*]] = trunc <4 x i32> [[COND]] to <4 x i8>
+; CHECK-NEXT: [[TMP0:%.*]] = call <4 x i32> @llvm.smax.v4i32(<4 x i32> [[X:%.*]], <4 x i32> zeroinitializer)
+; CHECK-NEXT: [[COND:%.*]] = call <4 x i32> @llvm.smin.v4i32(<4 x i32> [[TMP0]], <4 x i32> splat (i32 255))
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw <4 x i32> [[COND]] to <4 x i8>
; CHECK-NEXT: ret <4 x i8> [[TRUNC]]
;
entry:
@@ -914,12 +911,10 @@ entry:
define i8 @trunc_sext_i1(i32 %x) {
; CHECK-LABEL: @trunc_sext_i1(
-; CHECK-NEXT: [[LT:%.*]] = icmp ult i32 [[X:%.*]], 256
-; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[X]], -1
-; CHECK-NEXT: [[S:%.*]] = sext i1 [[C]] to i8
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smax.i32(i32 [[X1:%.*]], i32 0)
+; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.umin.i32(i32 [[TMP1]], i32 255)
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X]] to i8
-; CHECK-NEXT: [[R:%.*]] = select i1 [[LT]], i8 [[T]], i8 [[S]]
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[T]]
;
%lt = icmp ult i32 %x, 256
%c = icmp sgt i32 %x, -1
@@ -931,12 +926,9 @@ define i8 @trunc_sext_i1(i32 %x) {
define i8 @trunc_zext_i1(i32 %x) {
; CHECK-LABEL: @trunc_zext_i1(
-; CHECK-NEXT: [[LT:%.*]] = icmp ult i32 [[X:%.*]], 2
-; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[X:%.*]], 0
; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C]] to i8
-; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X]] to i8
-; CHECK-NEXT: [[R:%.*]] = select i1 [[LT]], i8 [[T]], i8 [[Z]]
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[Z]]
;
%lt = icmp ult i32 %x, 2
%c = icmp sgt i32 %x, 0
More information about the llvm-commits
mailing list