[llvm] [InstCombine] Add baseline tests for icmp eq/ne with self-referentialselect (NFC) (PR #226760)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 26 23:01:41 PDT 2026
https://github.com/addmisol created https://github.com/llvm/llvm-project/pull/226760
## Summary
Add baseline tests showing current (unoptimized) behavior for the pattern:
`X == select(icmp pred X, P, C1, C2)`
## Test coverage
- Scalar tests: eq/ne with sgt, slt, uge predicates, commuted operands, reversed condition
- Vector tests: same patterns as scalar
- Negative tests: C1 not in range, C2 in range, multi-use select, unrelated condition
Updated tests in below follow-up patch that implements the optimization.
Precommit for #223249.
>From c6a78364263839875d9b0a20e898944a8139c4eb Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Sun, 27 Sep 2026 11:29:32 +0530
Subject: [PATCH] [InstCombine] Add baseline tests for icmp eq/ne with
self-referential select (NFC)
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
.../InstCombine/icmp-eq-select-self.ll | 277 ++++++++++++++++++
1 file changed, 277 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/icmp-eq-select-self.ll
diff --git a/llvm/test/Transforms/InstCombine/icmp-eq-select-self.ll b/llvm/test/Transforms/InstCombine/icmp-eq-select-self.ll
new file mode 100644
index 00000000000000..3813b67201b3b1
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-eq-select-self.ll
@@ -0,0 +1,277 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; Test folding: X == select(icmp pred X, P, C1, C2) -> (X == C1) | (X == C2)
+; when C1 satisfies (C1 pred P) and C2 does not satisfy (C2 pred P).
+
+; Basic test case from issue #223249
+define i1 @icmp_eq_select_sgt_zero(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_select_sgt_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 2, i32 0
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp sgt i32 %x, 0
+ %s = select i1 %cond, i32 2, i32 0
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; Commuted operands: select(...) == X
+define i1 @icmp_eq_select_sgt_zero_commuted(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_select_sgt_zero_commuted(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 2, i32 0
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[S]], [[X]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp sgt i32 %x, 0
+ %s = select i1 %cond, i32 2, i32 0
+ %r = icmp eq i32 %s, %x
+ ret i1 %r
+}
+
+; ne version: X != select(X >s 0, 2, 0) -> (X != 0) & (X != 2)
+define i1 @icmp_ne_select_sgt_zero(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_ne_select_sgt_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 2, i32 0
+; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp sgt i32 %x, 0
+ %s = select i1 %cond, i32 2, i32 0
+ %r = icmp ne i32 %x, %s
+ ret i1 %r
+}
+
+; slt version: X == select(X <s 0, -5, 3)
+define i1 @icmp_eq_select_slt_zero(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_select_slt_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 -5, i32 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp slt i32 %x, 0
+ %s = select i1 %cond, i32 -5, i32 3
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; uge version: X == select(X >=u 5, 10, 2)
+define i1 @icmp_eq_select_uge(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_select_uge(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp ugt i32 [[X]], 4
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 10, i32 2
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp uge i32 %x, 5
+ %s = select i1 %cond, i32 10, i32 2
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; Condition with constant on LHS: select(0 <s X, 2, 0) == X
+define i1 @icmp_eq_select_cond_reversed(i32 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_select_cond_reversed(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 2, i32 0
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp slt i32 0, %x
+ %s = select i1 %cond, i32 2, i32 0
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; Vector version - eq
+define <2 x i1> @icmp_eq_select_sgt_zero_vec(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_select_sgt_zero_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i32> [[X]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 2), <2 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp sgt <2 x i32> %x, zeroinitializer
+ %s = select <2 x i1> %cond, <2 x i32> <i32 2, i32 2>, <2 x i32> zeroinitializer
+ %r = icmp eq <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Vector version - ne
+define <2 x i1> @icmp_ne_select_sgt_zero_vec(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_ne_select_sgt_zero_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i32> [[X]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 2), <2 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp sgt <2 x i32> %x, zeroinitializer
+ %s = select <2 x i1> %cond, <2 x i32> <i32 2, i32 2>, <2 x i32> zeroinitializer
+ %r = icmp ne <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Vector version - commuted operands
+define <2 x i1> @icmp_eq_select_sgt_zero_vec_commuted(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_select_sgt_zero_vec_commuted(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i32> [[X]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 2), <2 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[S]], [[X]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp sgt <2 x i32> %x, zeroinitializer
+ %s = select <2 x i1> %cond, <2 x i32> <i32 2, i32 2>, <2 x i32> zeroinitializer
+ %r = icmp eq <2 x i32> %s, %x
+ ret <2 x i1> %r
+}
+
+; Vector version - slt predicate
+define <2 x i1> @icmp_eq_select_slt_zero_vec(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_select_slt_zero_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp slt <2 x i32> [[X]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 -5), <2 x i32> splat (i32 3)
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp slt <2 x i32> %x, zeroinitializer
+ %s = select <2 x i1> %cond, <2 x i32> <i32 -5, i32 -5>, <2 x i32> <i32 3, i32 3>
+ %r = icmp eq <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Vector version - condition with constant on LHS (reversed)
+define <2 x i1> @icmp_eq_select_cond_reversed_vec(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_select_cond_reversed_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i32> [[X]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 2), <2 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp slt <2 x i32> zeroinitializer, %x
+ %s = select <2 x i1> %cond, <2 x i32> <i32 2, i32 2>, <2 x i32> zeroinitializer
+ %r = icmp eq <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Vector version - unsigned predicate (uge)
+define <2 x i1> @icmp_eq_select_uge_vec(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_select_uge_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp ugt <2 x i32> [[X]], splat (i32 4)
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 10), <2 x i32> splat (i32 2)
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp uge <2 x i32> %x, <i32 5, i32 5>
+ %s = select <2 x i1> %cond, <2 x i32> <i32 10, i32 10>, <2 x i32> <i32 2, i32 2>
+ %r = icmp eq <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Vector negative test - multi use
+define <2 x i1> @negative_multi_use_vec(<2 x i32> %x, ptr %p) {
+; CHECK-LABEL: define <2 x i1> @negative_multi_use_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i32> [[X]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 2), <2 x i32> zeroinitializer
+; CHECK-NEXT: store <2 x i32> [[S]], ptr [[P]], align 8
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp sgt <2 x i32> %x, zeroinitializer
+ %s = select <2 x i1> %cond, <2 x i32> <i32 2, i32 2>, <2 x i32> zeroinitializer
+ store <2 x i32> %s, ptr %p
+ %r = icmp eq <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Vector negative test - unrelated condition
+define <2 x i1> @negative_unrelated_cond_vec(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i1> @negative_unrelated_cond_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i32> [[Y]], zeroinitializer
+; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 2), <2 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[X]], [[S]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %cond = icmp sgt <2 x i32> %y, zeroinitializer
+ %s = select <2 x i1> %cond, <2 x i32> <i32 2, i32 2>, <2 x i32> zeroinitializer
+ %r = icmp eq <2 x i32> %x, %s
+ ret <2 x i1> %r
+}
+
+; Negative test: C1 does not satisfy condition (2 is not > 5)
+; Should not be folded by our pattern
+define i1 @negative_c1_not_in_range(i32 %x) {
+; CHECK-LABEL: define i1 @negative_c1_not_in_range(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R1:%.*]] = icmp eq i32 [[X]], 0
+; CHECK-NEXT: ret i1 [[R1]]
+;
+ %cond = icmp sgt i32 %x, 5
+ %s = select i1 %cond, i32 2, i32 0
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; Negative test: C2 satisfies condition (3 > 0)
+; Should not be folded by our pattern
+define i1 @negative_c2_in_range(i32 %x) {
+; CHECK-LABEL: define i1 @negative_c2_in_range(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[R1:%.*]] = icmp eq i32 [[X]], 2
+; CHECK-NEXT: ret i1 [[R1]]
+;
+ %cond = icmp sgt i32 %x, 0
+ %s = select i1 %cond, i32 2, i32 3
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; Negative test: multiple uses of select
+define i1 @negative_multi_use(i32 %x, ptr %p) {
+; CHECK-LABEL: define i1 @negative_multi_use(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 2, i32 0
+; CHECK-NEXT: store i32 [[S]], ptr [[P]], align 4
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp sgt i32 %x, 0
+ %s = select i1 %cond, i32 2, i32 0
+ store i32 %s, ptr %p
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
+
+; Negative test: select condition doesn't involve X
+define i1 @negative_unrelated_cond(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @negative_unrelated_cond(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[Y]], 0
+; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 2, i32 0
+; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[S]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %cond = icmp sgt i32 %y, 0
+ %s = select i1 %cond, i32 2, i32 0
+ %r = icmp eq i32 %x, %s
+ ret i1 %r
+}
More information about the llvm-commits
mailing list