[llvm-branch-commits] [llvm] [AMDGPU] Fold AND of any-extended SGPR booleans to select (PR #230701)

Alan Li via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Oct 9 16:08:52 PDT 2026


https://github.com/lialan created https://github.com/llvm/llvm-project/pull/230701

## Issue
Demanded-bits simplification can turn a boolean sign extension into `ANY_EXTEND` before the target `AND` combine, causing the existing AND-to-select fold to miss.

## Fix
Extend the i32 combine to accept `ANY_EXTEND` of an SGPR boolean on either operand. Its unspecified high bits can be chosen to match sign extension, allowing `and x, (anyext cc)` to fold to `select cc, x, 0`.

## Validation
On AMDGPU, for a sign-extended condition `AND`ed with a logical right shift by 31, this removes an extra `v_and_b32`. Add regression coverage for both operand orders and zero extension. Existing test RUN lines are unchanged.


>From 807079e0fd0f7d2cd03ccb4561da244cfe1b3c41 Mon Sep 17 00:00:00 2001
From: Alan Li <me at alanli.org>
Date: Fri, 9 Oct 2026 14:41:11 -0700
Subject: [PATCH] fix(AMDGPU): fold AND of any-extended booleans

Demanded-bits simplification can turn a boolean sign extension into an
any extension before the target AND combine. Choose sign extension for
the undefined high bits so the AND still folds to a select.

Cover reduced demand, swapped operands, and zero extension. Restore the
fold in setcc-multiple-use after symmetric demanded-bits simplification.
---
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp     | 18 +++++---
 .../CodeGen/AMDGPU/combine-and-sext-bool.ll   | 46 +++++++++++++++++++
 .../test/CodeGen/AMDGPU/setcc-multiple-use.ll |  7 ++-
 3 files changed, 60 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 7f2784726c4760..33bf38f3792bdb 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -15121,14 +15121,18 @@ SDValue SITargetLowering::performAndCombine(SDNode *N,
     }
   }
 
-  if (VT == MVT::i32 && (RHS.getOpcode() == ISD::SIGN_EXTEND ||
-                         LHS.getOpcode() == ISD::SIGN_EXTEND)) {
-    // and x, (sext cc from i1) => select cc, x, 0
-    if (RHS.getOpcode() != ISD::SIGN_EXTEND)
+  auto IsBoolExt = [](SDValue V) {
+    return (V.getOpcode() == ISD::SIGN_EXTEND ||
+            V.getOpcode() == ISD::ANY_EXTEND) &&
+           isBoolSGPR(V.getOperand(0));
+  };
+  if (VT == MVT::i32 && (IsBoolExt(RHS) || IsBoolExt(LHS))) {
+    // and x, (sext/anyext cc from i1) => select cc, x, 0
+    // Any-extended bits can be chosen to match sign extension.
+    if (!IsBoolExt(RHS))
       std::swap(LHS, RHS);
-    if (isBoolSGPR(RHS.getOperand(0)))
-      return DAG.getSelect(SDLoc(N), MVT::i32, RHS.getOperand(0), LHS,
-                           DAG.getConstant(0, SDLoc(N), MVT::i32));
+    return DAG.getSelect(SDLoc(N), MVT::i32, RHS.getOperand(0), LHS,
+                         DAG.getConstant(0, SDLoc(N), MVT::i32));
   }
 
   // and (op x, c1), (op y, c2) -> perm x, y, permute_mask(c1, c2)
diff --git a/llvm/test/CodeGen/AMDGPU/combine-and-sext-bool.ll b/llvm/test/CodeGen/AMDGPU/combine-and-sext-bool.ll
index c18d2234470ae6..33c03533ac766a 100644
--- a/llvm/test/CodeGen/AMDGPU/combine-and-sext-bool.ll
+++ b/llvm/test/CodeGen/AMDGPU/combine-and-sext-bool.ll
@@ -149,6 +149,52 @@ define i32 @and_sext_bool_umul_w_overflow(i32 %x, i32 %y) {
   ret i32 %and
 }
 
+; Only bit 0 of the sext is demanded, so it becomes an anyext before the
+; target combine. The AND should still fold to a select.
+define i32 @and_anyext_bool(i32 %x, i32 %y) {
+; GCN-LABEL: and_anyext_bool:
+; GCN:       ; %bb.0:
+; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GCN-NEXT:    v_lshrrev_b32_e32 v1, 31, v1
+; GCN-NEXT:    v_cmp_eq_u32_e32 vcc, 0, v0
+; GCN-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc
+; GCN-NEXT:    s_setpc_b64 s[30:31]
+  %cmp = icmp eq i32 %x, 0
+  %ext = sext i1 %cmp to i32
+  %bit = lshr i32 %y, 31
+  %and = and i32 %ext, %bit
+  ret i32 %and
+}
+
+define i32 @and_anyext_bool_commuted(i32 %x, i32 %y) {
+; GCN-LABEL: and_anyext_bool_commuted:
+; GCN:       ; %bb.0:
+; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GCN-NEXT:    v_lshrrev_b32_e32 v1, 31, v1
+; GCN-NEXT:    v_cmp_eq_u32_e32 vcc, 0, v0
+; GCN-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc
+; GCN-NEXT:    s_setpc_b64 s[30:31]
+  %cmp = icmp eq i32 %x, 0
+  %ext = sext i1 %cmp to i32
+  %bit = lshr i32 %y, 31
+  %and = and i32 %bit, %ext
+  ret i32 %and
+}
+
+; A zext must clear the high bits even when the condition is true.
+define i32 @and_zext_bool(i32 %x, i32 %y) {
+; GCN-LABEL: and_zext_bool:
+; GCN:       ; %bb.0:
+; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GCN-NEXT:    v_cmp_eq_u32_e32 vcc, 0, v0
+; GCN-NEXT:    v_cndmask_b32_e64 v0, 0, 1, vcc
+; GCN-NEXT:    v_and_b32_e32 v0, v0, v1
+; GCN-NEXT:    s_setpc_b64 s[30:31]
+  %cmp = icmp eq i32 %x, 0
+  %ext = zext i1 %cmp to i32
+  %and = and i32 %ext, %y
+  ret i32 %and
+}
 
 declare i32 @llvm.amdgcn.workitem.id.x() #0
 
diff --git a/llvm/test/CodeGen/AMDGPU/setcc-multiple-use.ll b/llvm/test/CodeGen/AMDGPU/setcc-multiple-use.ll
index cda1c07d058866..3fbeecfbf92133 100644
--- a/llvm/test/CodeGen/AMDGPU/setcc-multiple-use.ll
+++ b/llvm/test/CodeGen/AMDGPU/setcc-multiple-use.ll
@@ -14,11 +14,10 @@ define i32 @f() {
 ; CHECK-NEXT:    v_mov_b32_e32 v0, 0
 ; CHECK-NEXT:    ds_read_b32 v0, v0
 ; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 0, v0
-; CHECK-NEXT:    v_cndmask_b32_e64 v1, 0, 1, vcc_lo
 ; CHECK-NEXT:    v_cmp_eq_u32_e32 vcc_lo, 0, v0
-; CHECK-NEXT:    v_cndmask_b32_e64 v0, 0, 1, vcc_lo
-; CHECK-NEXT:    v_and_b32_e32 v0, v0, v1
+; CHECK-NEXT:    v_cndmask_b32_e64 v1, 0, 1, vcc_lo
+; CHECK-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 0, v0
+; CHECK-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc_lo
 ; CHECK-NEXT:    s_setpc_b64 s[30:31]
 bb:
   %i = load i32, ptr addrspace(3) zeroinitializer, align 16



More information about the llvm-branch-commits mailing list