[llvm] [CodeGenPrepare] Invert select predicates when profitable (PR #218296)
Kevin Choi via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 15:35:03 PDT 2026
https://github.com/choikwa created https://github.com/llvm/llvm-project/pull/218296
Use the target cost model to compare an integer comparison with its inverse, and invert it while swapping the select operands when that is strictly cheaper. Look through one-use shuffle chains needed by vector selects, preserve profile metadata, and retain canonical predicates when costs are equal or the comparison is shared.
Assisted by gpt-5.6-sol
Fixes #216534
>From c0dfbd4f9a534339b1dc3574bf8410d9de8b6fed Mon Sep 17 00:00:00 2001
From: choikwa <code.kchoi at gmail.com>
Date: Sun, 23 Aug 2026 18:30:36 -0400
Subject: [PATCH] [CodeGenPrepare] Invert select predicates when profitable
Use the target cost model to compare an integer comparison with its inverse, and invert it while swapping the select operands when that is strictly cheaper. Look through one-use shuffle chains needed by vector selects, preserve profile metadata, and retain canonical predicates when costs are equal or the comparison is shared.
Assisted by gpt-5.6-sol
---
llvm/lib/CodeGen/CodeGenPrepare.cpp | 49 ++++++++++++++--
.../CodeGenPrepare/X86/invert-icmp-select.ll | 56 +++++++++++++++++++
2 files changed, 101 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/Transforms/CodeGenPrepare/X86/invert-icmp-select.ll
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 53b7a81537975..3ec30357a8589 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -434,6 +434,7 @@ class CodeGenPrepare {
bool optimizeLoadExt(LoadInst *Load);
bool optimizeShiftInst(BinaryOperator *BO);
bool optimizeFunnelShift(IntrinsicInst *Fsh);
+ bool invertSelectPredicateIfProfitable(SelectInst *SI);
bool optimizeSelectInst(SelectInst *SI);
bool optimizeShuffleVectorInst(ShuffleVectorInst *SVI);
bool optimizeSwitchType(SwitchInst *SI);
@@ -7717,15 +7718,55 @@ bool CodeGenPrepare::optimizeFunnelShift(IntrinsicInst *Fsh) {
return true;
}
+/// Invert an integer comparison feeding \p SI and swap the select operands if
+/// the target reports that the inverse predicate is strictly cheaper.
+bool CodeGenPrepare::invertSelectPredicateIfProfitable(SelectInst *SI) {
+ Value *Cond = SI->getCondition();
+
+ // Look through one-use shuffles of the compare result. Inverting a compare
+ // commutes with a shuffle whose second input is poison or undef.
+ while (auto *SVI = dyn_cast<ShuffleVectorInst>(Cond)) {
+ if (!SVI->hasOneUse() || !isa<PoisonValue, UndefValue>(SVI->getOperand(1)))
+ return false;
+ Cond = SVI->getOperand(0);
+ }
+
+ auto *Cmp = dyn_cast<ICmpInst>(Cond);
+ if (!Cmp || !Cmp->hasOneUse())
+ return false;
+
+ Type *ValTy = Cmp->getOperand(0)->getType();
+ Type *CondTy = Cmp->getType();
+ CmpInst::Predicate Pred = Cmp->getPredicate();
+ CmpInst::Predicate InversePred = Cmp->getInversePredicate();
+ TTI::OperandValueInfo Op1Info = TTI::getOperandInfo(Cmp->getOperand(0));
+ TTI::OperandValueInfo Op2Info = TTI::getOperandInfo(Cmp->getOperand(1));
+ InstructionCost Cost =
+ TTI->getCmpSelInstrCost(Instruction::ICmp, ValTy, CondTy, Pred,
+ TTI::TCK_RecipThroughput, Op1Info, Op2Info, Cmp);
+ InstructionCost InverseCost =
+ TTI->getCmpSelInstrCost(Instruction::ICmp, ValTy, CondTy, InversePred,
+ TTI::TCK_RecipThroughput, Op1Info, Op2Info, Cmp);
+ if (!Cost.isValid() || !InverseCost.isValid() || InverseCost >= Cost)
+ return false;
+
+ Cmp->setPredicate(InversePred);
+ SI->swapValues();
+ SI->swapProfMetadata();
+ return true;
+}
+
/// If we have a SelectInst that will likely profit from branch prediction,
/// turn it into a branch.
bool CodeGenPrepare::optimizeSelectInst(SelectInst *SI) {
+ bool Changed = invertSelectPredicateIfProfitable(SI);
+
if (DisableSelectToBranch)
- return false;
+ return Changed;
// If the SelectOptimize pass is enabled, selects have already been optimized.
if (!getCGPassBuilderOption().DisableSelectOptimize)
- return false;
+ return Changed;
// Find all consecutive select instructions that share the same condition.
SmallVector<SelectInst *, 2> ASI;
@@ -7754,7 +7795,7 @@ bool CodeGenPrepare::optimizeSelectInst(SelectInst *SI) {
// Can we convert the 'select' to CF ?
if (VectorCond || SI->getMetadata(LLVMContext::MD_unpredictable))
- return false;
+ return Changed;
TargetLowering::SelectSupportKind SelectKind;
if (SI->getType()->isVectorTy())
@@ -7765,7 +7806,7 @@ bool CodeGenPrepare::optimizeSelectInst(SelectInst *SI) {
if (TLI->isSelectSupported(SelectKind) &&
(!isFormingBranchFromSelectProfitable(TTI, TLI, SI) ||
llvm::shouldOptimizeForSize(SI->getParent(), PSI, BFI)))
- return false;
+ return Changed;
// Transform a sequence like this:
// start:
diff --git a/llvm/test/Transforms/CodeGenPrepare/X86/invert-icmp-select.ll b/llvm/test/Transforms/CodeGenPrepare/X86/invert-icmp-select.ll
new file mode 100644
index 0000000000000..2d6c2bacac211
--- /dev/null
+++ b/llvm/test/Transforms/CodeGenPrepare/X86/invert-icmp-select.ll
@@ -0,0 +1,56 @@
+; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' \
+; RUN: -mtriple=x86_64 -mattr=+avx2 %s | FileCheck %s --check-prefix=AVX2
+; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' \
+; RUN: -mtriple=x86_64 -mattr=+avx512bw %s | FileCheck %s --check-prefix=AVX512
+
+define <8 x i8> @direct(<8 x i8> %a, <8 x i8> %b, <8 x i8> %x, <8 x i8> %y) {
+; AVX2-LABEL: @direct(
+; AVX2: %cmp = icmp uge <8 x i8> %a, %b
+; AVX2: %sel = select <8 x i1> %cmp, <8 x i8> %y, <8 x i8> %x, !prof [[INVERTED_WEIGHTS:![0-9]+]]
+; AVX512-LABEL: @direct(
+; AVX512: %cmp = icmp ult <8 x i8> %a, %b
+; AVX512: %sel = select <8 x i1> %cmp, <8 x i8> %x, <8 x i8> %y, !prof [[ORIGINAL_WEIGHTS:![0-9]+]]
+ %cmp = icmp ult <8 x i8> %a, %b
+ %sel = select <8 x i1> %cmp, <8 x i8> %x, <8 x i8> %y, !prof !0
+ ret <8 x i8> %sel
+}
+
+define <4 x i8> @through_shuffle(<8 x i8> %a, <8 x i8> %b,
+ <4 x i8> %x, <4 x i8> %y) {
+; AVX2-LABEL: @through_shuffle(
+; AVX2: %cmp = icmp uge <8 x i8> %a, %b
+; AVX2: %cond = shufflevector <8 x i1> %cmp, <8 x i1> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
+; AVX2: %sel = select <4 x i1> %cond, <4 x i8> %y, <4 x i8> %x
+ %cmp = icmp ult <8 x i8> %a, %b
+ %cond = shufflevector <8 x i1> %cmp, <8 x i1> poison,
+ <4 x i32> <i32 3, i32 2, i32 1, i32 0>
+ %sel = select <4 x i1> %cond, <4 x i8> %x, <4 x i8> %y
+ ret <4 x i8> %sel
+}
+
+define <8 x i8> @not_profitable(<8 x i8> %a, <8 x i8> %b,
+ <8 x i8> %x, <8 x i8> %y) {
+; AVX2-LABEL: @not_profitable(
+; AVX2: %cmp = icmp eq <8 x i8> %a, %b
+; AVX2: %sel = select <8 x i1> %cmp, <8 x i8> %x, <8 x i8> %y
+ %cmp = icmp eq <8 x i8> %a, %b
+ %sel = select <8 x i1> %cmp, <8 x i8> %x, <8 x i8> %y
+ ret <8 x i8> %sel
+}
+
+define <8 x i8> @shared_cmp(<8 x i8> %a, <8 x i8> %b,
+ <8 x i8> %x, <8 x i8> %y) {
+; AVX2-LABEL: @shared_cmp(
+; AVX2: %cmp = icmp ult <8 x i8> %a, %b
+; AVX2: %sel1 = select <8 x i1> %cmp, <8 x i8> %x, <8 x i8> %y
+; AVX2: %sel2 = select <8 x i1> %cmp, <8 x i8> %sel1, <8 x i8> %a
+ %cmp = icmp ult <8 x i8> %a, %b
+ %sel1 = select <8 x i1> %cmp, <8 x i8> %x, <8 x i8> %y
+ %sel2 = select <8 x i1> %cmp, <8 x i8> %sel1, <8 x i8> %a
+ ret <8 x i8> %sel2
+}
+
+; AVX2: [[INVERTED_WEIGHTS]] = !{!"branch_weights", i32 90, i32 10}
+; AVX512: [[ORIGINAL_WEIGHTS]] = !{!"branch_weights", i32 10, i32 90}
+
+!0 = !{!"branch_weights", i32 10, i32 90}
More information about the llvm-commits
mailing list