[llvm] [InstCombine] Fold a condition-derived shifted LSB into select arms (PR #213882)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 02:27:21 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Vito Kortbeek (vkortbeek-gf)
<details>
<summary>Changes</summary>
If a select condition determines bit 0 of X and an operand is shl(zext/trunc/self X, BW - 1), replace that operand with either the sign-bit mask or zero on each select arm.
Proof: https://alive2.llvm.org/ce/z/8M3ksf
Fixes: #<!-- -->213881
Assisted-by: AI
---
Full diff: https://github.com/llvm/llvm-project/pull/213882.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+54-11)
- (modified) llvm/test/Transforms/InstCombine/binop-select.ll (+77)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index acb144e1e807d..31996d62a36d5 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -45,6 +45,7 @@
#include "llvm/Analysis/BasicAliasAnalysis.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CmpInstAnalysis.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/InstructionSimplify.h"
@@ -1745,6 +1746,32 @@ Instruction *InstCombinerImpl::foldBinopOfSextBoolToSelect(BinaryOperator &BO) {
return createSelectInstWithUnknownProfile(X, TVal, FVal);
}
+// If SI's condition determines bit 0 of X and Op is
+// shl (zext/trunc/self X), BW - 1, return Op's known value on the given
+// select arm.
+static Constant *getShiftedLsbValueForSelectArm(Value *Op, SelectInst *SI,
+ bool IsTrueArm) {
+ auto BitTest =
+ decomposeBitTest(SI->getCondition(), /*LookThroughTrunc=*/true,
+ /*AllowNonZeroC=*/false, /*DecomposeAnd=*/true);
+ if (!BitTest || !BitTest->Mask.isOne())
+ return nullptr;
+
+ Type *Ty = Op->getType();
+ if (!Ty->isIntegerTy())
+ return nullptr;
+
+ unsigned BitWidth = Ty->getIntegerBitWidth();
+ if (!match(Op, m_Shl(m_ZExtOrTruncOrSelf(m_Specific(BitTest->X)),
+ m_SpecificInt(BitWidth - 1))))
+ return nullptr;
+
+ bool BitSetOnTrue = BitTest->Pred == ICmpInst::ICMP_NE;
+ return IsTrueArm == BitSetOnTrue
+ ? ConstantInt::get(Ty, APInt::getSignMask(BitWidth))
+ : ConstantInt::getNullValue(Ty);
+}
+
static Value *simplifyOperationIntoSelectOperand(Instruction &I, SelectInst *SI,
bool IsTrueArm) {
SmallVector<Value *> Ops;
@@ -1761,6 +1788,9 @@ static Value *simplifyOperationIntoSelectOperand(Instruction &I, SelectInst *SI,
} else if (match(Op, m_ZExt(m_Specific(SI->getCondition())))) {
V = IsTrueArm ? ConstantInt::get(Op->getType(), 1)
: ConstantInt::getNullValue(Op->getType());
+ } else if (Constant *C =
+ getShiftedLsbValueForSelectArm(Op, SI, IsTrueArm)) {
+ V = C;
} else {
V = Op;
}
@@ -1771,9 +1801,13 @@ static Value *simplifyOperationIntoSelectOperand(Instruction &I, SelectInst *SI,
}
static Value *foldOperationIntoSelectOperand(Instruction &I, SelectInst *SI,
- Value *NewOp, InstCombiner &IC) {
+ Value *NewOp, bool IsTrueArm,
+ InstCombiner &IC) {
Instruction *Clone = I.clone();
Clone->replaceUsesOfWith(SI, NewOp);
+ for (Use &U : Clone->operands())
+ if (Constant *C = getShiftedLsbValueForSelectArm(U, SI, IsTrueArm))
+ U.set(C);
Clone->dropUBImplyingAttrsAndMetadata();
IC.InsertNewInstBefore(Clone, I.getIterator());
return Clone;
@@ -1830,9 +1864,11 @@ Instruction *InstCombinerImpl::FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
// Create an instruction for the arm that did not fold.
if (!NewTV)
- NewTV = foldOperationIntoSelectOperand(Op, SI, TV, *this);
+ NewTV =
+ foldOperationIntoSelectOperand(Op, SI, TV, /*IsTrueArm=*/true, *this);
if (!NewFV)
- NewFV = foldOperationIntoSelectOperand(Op, SI, FV, *this);
+ NewFV =
+ foldOperationIntoSelectOperand(Op, SI, FV, /*IsTrueArm=*/false, *this);
SelectInst *NewSel = SelectInst::Create(SI->getCondition(), NewTV, NewFV);
@@ -2319,19 +2355,26 @@ Instruction *InstCombinerImpl::foldBinopWithPhiOperands(BinaryOperator &BO) {
}
Instruction *InstCombinerImpl::foldBinOpIntoSelectOrPhi(BinaryOperator &I) {
- auto TryFoldOperand = [&](unsigned OpIdx,
- bool IsOtherParamConst) -> Instruction * {
- if (auto *Sel = dyn_cast<SelectInst>(I.getOperand(OpIdx)))
- return FoldOpIntoSelect(I, Sel, false, !IsOtherParamConst);
- if (auto *PN = dyn_cast<PHINode>(I.getOperand(OpIdx)))
+ auto TryFoldOperand = [&](Value *Op, Value *OtherOp) -> Instruction * {
+ if (auto *Sel = dyn_cast<SelectInst>(Op)) {
+ // A shifted LSB determined by the condition is constant on each select
+ // arm. If the shift has one use, folding makes it dead, so one-arm
+ // folding is profitable.
+ bool IsProfitableToFoldOneArm =
+ OtherOp->hasOneUse() &&
+ getShiftedLsbValueForSelectArm(OtherOp, Sel, /*IsTrueArm=*/true);
+ return FoldOpIntoSelect(I, Sel, /*FoldWithMultiUse=*/false,
+ !isa<Constant>(OtherOp) &&
+ !IsProfitableToFoldOneArm);
+ }
+ if (auto *PN = dyn_cast<PHINode>(Op))
return foldOpIntoPhi(I, PN);
return nullptr;
};
- if (Instruction *NewI =
- TryFoldOperand(/*OpIdx=*/0, isa<Constant>(I.getOperand(1))))
+ if (Instruction *NewI = TryFoldOperand(I.getOperand(0), I.getOperand(1)))
return NewI;
- return TryFoldOperand(/*OpIdx=*/1, isa<Constant>(I.getOperand(0)));
+ return TryFoldOperand(I.getOperand(1), I.getOperand(0));
}
static bool shouldMergeGEPs(GEPOperator &GEP, GEPOperator &Src) {
diff --git a/llvm/test/Transforms/InstCombine/binop-select.ll b/llvm/test/Transforms/InstCombine/binop-select.ll
index 3265ea54a831e..da4bb67df00b5 100644
--- a/llvm/test/Transforms/InstCombine/binop-select.ll
+++ b/llvm/test/Transforms/InstCombine/binop-select.ll
@@ -2,6 +2,7 @@
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
declare void @use(i32)
+declare void @use_i8(i8)
declare void @use_f32(float)
declare void @use_v2f16(<2 x half>)
declare void @use_v2i8(<2 x i8>)
@@ -545,6 +546,82 @@ define i8 @commonArgWithAdd0(i1 %arg0) {
ret i8 %v3
}
+define i8 @commonArgWithOrShl0(i8 %arg0) {
+; CHECK-LABEL: @commonArgWithOrShl0(
+; CHECK-NEXT: [[V0:%.*]] = trunc i8 [[ARG0:%.*]] to i1
+; CHECK-NEXT: [[V4:%.*]] = select i1 [[V0]], i8 -123, i8 9
+; CHECK-NEXT: ret i8 [[V4]]
+;
+ %v0 = trunc i8 %arg0 to i1
+ %v1 = select i1 %v0, i8 5, i8 9
+ %v3 = shl i8 %arg0, 7
+ %v4 = or i8 %v1, %v3
+ ret i8 %v4
+}
+
+define i8 @commonArgWithOrShl1(i8 %arg0, i8 %arg1) {
+; CHECK-LABEL: @commonArgWithOrShl1(
+; CHECK-NEXT: [[V0:%.*]] = trunc i8 [[ARG0:%.*]] to i1
+; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[ARG1:%.*]], -128
+; CHECK-NEXT: [[V4:%.*]] = select i1 [[V0]], i8 [[TMP1]], i8 0
+; CHECK-NEXT: ret i8 [[V4]]
+;
+ %v0 = trunc i8 %arg0 to i1
+ %v1 = select i1 %v0, i8 %arg1, i8 0
+ %v3 = shl i8 %arg0, 7
+ %v4 = or i8 %v1, %v3
+ ret i8 %v4
+}
+
+define i16 @commonArgWithOrShlEqZext(i8 %x, i16 %y) {
+; CHECK-LABEL: @commonArgWithOrShlEqZext(
+; CHECK-NEXT: [[MASKED:%.*]] = and i8 [[X:%.*]], 1
+; CHECK-NEXT: [[COND:%.*]] = icmp eq i8 [[MASKED]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = or i16 [[Y:%.*]], -32768
+; CHECK-NEXT: [[RESULT:%.*]] = select i1 [[COND]], i16 0, i16 [[TMP1]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %masked = and i8 %x, 1
+ %cond = icmp eq i8 %masked, 0
+ %sel = select i1 %cond, i16 0, i16 %y
+ %wide = zext i8 %x to i16
+ %shift = shl i16 %wide, 15
+ %result = or i16 %sel, %shift
+ ret i16 %result
+}
+
+define i8 @commonArgWithOrShlTrunc(i16 %x, i8 %y) {
+; CHECK-LABEL: @commonArgWithOrShlTrunc(
+; CHECK-NEXT: [[COND:%.*]] = trunc i16 [[X:%.*]] to i1
+; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Y:%.*]], -128
+; CHECK-NEXT: [[RESULT:%.*]] = select i1 [[COND]], i8 [[TMP1]], i8 0
+; CHECK-NEXT: ret i8 [[RESULT]]
+;
+ %cond = trunc i16 %x to i1
+ %sel = select i1 %cond, i8 %y, i8 0
+ %narrow = trunc i16 %x to i8
+ %shift = shl i8 %narrow, 7
+ %result = or i8 %sel, %shift
+ ret i8 %result
+}
+
+define i8 @commonArgWithOrShlMultiUse(i8 %x, i8 %y) {
+; CHECK-LABEL: @commonArgWithOrShlMultiUse(
+; CHECK-NEXT: [[COND:%.*]] = trunc i8 [[X:%.*]] to i1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i8 [[Y:%.*]], i8 0
+; CHECK-NEXT: [[SHIFT:%.*]] = shl i8 [[X]], 7
+; CHECK-NEXT: call void @use_i8(i8 [[SHIFT]])
+; CHECK-NEXT: [[RESULT:%.*]] = or i8 [[SEL]], [[SHIFT]]
+; CHECK-NEXT: ret i8 [[RESULT]]
+;
+ %cond = trunc i8 %x to i1
+ %sel = select i1 %cond, i8 %y, i8 0
+ %shift = shl i8 %x, 7
+ call void @use_i8(i8 %shift)
+ %result = or i8 %sel, %shift
+ ret i8 %result
+}
+
define i32 @OrSelectIcmpZero(i32 %a, i32 %b) {
; CHECK-LABEL: @OrSelectIcmpZero(
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 0
``````````
</details>
https://github.com/llvm/llvm-project/pull/213882
More information about the llvm-commits
mailing list