[llvm] [CodeGenPrepare] Split x/select(cond,y,C) to select(cond,x/y,x/C) (PR #215967)
Le Gia Bao via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 14 00:13:40 PDT 2026
https://github.com/L3GiaBao updated https://github.com/llvm/llvm-project/pull/215967
>From 993e9941c6c1340e6ad022e2ea552d1d4d0df8cd Mon Sep 17 00:00:00 2001
From: Le Gia Bao <legiabao7300 at gmail.com>
Date: Thu, 13 Aug 2026 13:33:07 +0700
Subject: [PATCH 1/4] [CodeGenPrepare] Split x/select(cond,y,C) to
select(cond,x/y,x/C)
---
llvm/lib/CodeGen/CodeGenPrepare.cpp | 143 +++++++++++++++
.../CodeGen/X86/divrem-by-select-scalar.ll | 100 +++++++++++
.../CodeGenPrepare/X86/div-rem-by-select.ll | 165 ++++++++++++++++++
3 files changed, 408 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/divrem-by-select-scalar.ll
create mode 100644 llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 9d3f18c8fce63..d05f1c33d5b88 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -558,6 +558,132 @@ PreservedAnalyses CodeGenPreparePass::run(Function &F,
return PA;
}
+static bool isDivOrRem(unsigned Opcode) {
+ switch (Opcode) {
+ case Instruction::SDiv:
+ case Instruction::UDiv:
+ case Instruction::SRem:
+ case Instruction::URem:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isCheapConstantDivisor(Value *V, const TargetLowering *TLI,
+ const DataLayout &DL, AttributeList Attr) {
+ auto *C = dyn_cast<ConstantInt>(V);
+ if (!C || C->isZero())
+ return false;
+ return !TLI->isIntDivCheap(TLI->getValueType(DL, C->getType()), Attr);
+}
+
+/// Fold a div/rem of a select divisor with a cheap constant arm into a
+/// branch with one division per arm:
+/// div X, (select Cond, Y, C) -->
+/// select Cond, (div X, Y), (div X, C)
+/// so the constant arm keeps its cheap lowering instead of being dragged
+/// into a variable divide. Returns true if \p I was replaced.
+static bool splitDivRemBySelectDivisor(Instruction *I,
+ const TargetLowering *TLI,
+ const DataLayout &DL,
+ DomTreeUpdater *DTU, LoopInfo *LI) {
+ if (!isDivOrRem(I->getOpcode()) || I->getType()->isVectorTy())
+ return false;
+
+ // Don't touch loops. foldURemOfLoopIncrement handles that case
+ if (LI && LI->getLoopFor(I->getParent()))
+ return false;
+
+ // If the select has more than one use, there's probably a sibling rem
+ // sharing this divisor and splitting them separately would duplicate the division
+ auto *Sel = dyn_cast<SelectInst>(I->getOperand(1));
+ if (!Sel || !Sel->hasOneUse())
+ return false;
+
+ // If the dividend is also constant, foldBinOpIntoSelect handles it.
+ Value *Dividend = I->getOperand(0);
+ if (isa<Constant>(Dividend))
+ return false;
+
+ AttributeList Attr = I->getFunction()->getAttributes();
+ Value *TrueVal = Sel->getTrueValue();
+ Value *FalseVal = Sel->getFalseValue();
+ if (!isCheapConstantDivisor(TrueVal, TLI, DL, Attr) &&
+ !isCheapConstantDivisor(FalseVal, TLI, DL, Attr))
+ return false;
+
+ BasicBlock *MainBB = I->getParent();
+ Function *F = MainBB->getParent();
+ LLVMContext &Ctx = F->getContext();
+ auto Opcode = static_cast<Instruction::BinaryOps>(I->getOpcode());
+
+ BasicBlock *TailBB = SplitBlock(MainBB, I, DTU, LI, nullptr, "select.end");
+ // Replace the unconditional branch that was created by the split with a
+ // branch on the select's condition.
+ MainBB->back().eraseFromParent();
+
+ auto CreateArmBB = [&](Value *Divisor, const Twine &Name) {
+ BasicBlock *ArmBB = BasicBlock::Create(Ctx, Name, F, TailBB);
+ IRBuilder<> Builder(ArmBB);
+ Builder.SetCurrentDebugLocation(I->getDebugLoc());
+ Value *Op = Builder.CreateBinOp(Opcode, Dividend, Divisor);
+ if (auto *NewI = dyn_cast<Instruction>(Op))
+ NewI->copyIRFlags(I);
+ Builder.CreateBr(TailBB);
+ return std::make_pair(ArmBB, Op);
+ };
+
+ auto [TrueBB, TrueOp] = CreateArmBB(TrueVal, "select.true");
+ auto [FalseBB, FalseOp] = CreateArmBB(FalseVal, "select.false");
+
+ IRBuilder<> HeadBuilder(MainBB, MainBB->end());
+ HeadBuilder.SetCurrentDebugLocation(I->getDebugLoc());
+ Value *Cond = HeadBuilder.CreateFreeze(Sel->getCondition());
+ HeadBuilder.CreateCondBr(Cond, TrueBB, FalseBB);
+
+ IRBuilder<> TailBuilder(TailBB, TailBB->begin());
+ TailBuilder.SetCurrentDebugLocation(I->getDebugLoc());
+ PHINode *Phi = TailBuilder.CreatePHI(I->getType(), 2);
+ Phi->addIncoming(TrueOp, TrueBB);
+ Phi->addIncoming(FalseOp, FalseBB);
+
+ I->replaceAllUsesWith(Phi);
+ I->eraseFromParent();
+ if (Sel->use_empty())
+ Sel->eraseFromParent();
+
+ if (DTU)
+ DTU->applyUpdates({{DominatorTree::Insert, MainBB, TrueBB},
+ {DominatorTree::Insert, MainBB, FalseBB},
+ {DominatorTree::Insert, TrueBB, TailBB},
+ {DominatorTree::Insert, FalseBB, TailBB},
+ {DominatorTree::Delete, MainBB, TailBB}});
+
+ return true;
+}
+
+/// Scan \p BB for div/rem instructions with a select divisor and split them
+/// (see splitDivRemBySelectDivisor).
+static bool optimizeDivRemBySelectDivisor(BasicBlock *BB,
+ const TargetLowering *TLI,
+ const DataLayout &DL,
+ DomTreeUpdater *DTU, LoopInfo *LI) {
+ bool MadeChange = false;
+ Instruction *Next = &*BB->begin();
+ while (Next != nullptr) {
+ // We may add instructions immediately after I but we want to skip over them.
+ Instruction *I = Next;
+ Next = Next->getNextNode();
+
+ if (I->use_empty())
+ continue;
+
+ MadeChange |= splitDivRemBySelectDivisor(I, TLI, DL, DTU, LI);
+ }
+ return MadeChange;
+}
+
bool CodeGenPrepare::run(Function &F, FunctionAnalysisManager &AM) {
DL = &F.getDataLayout();
SubtargetInfo = TM->getSubtargetImpl(F);
@@ -624,6 +750,23 @@ bool CodeGenPrepare::_run(Function &F) {
}
}
+ /// Split a div/rem whose divisor is a select with a constant arm the
+ /// target would otherwise strength-reduce, so each arm can reach its own
+ /// cheapest lowering instead of being dragged into a variable divide.
+ /// Unlike the slow-division bypass above, this doesn't depend on the
+ /// target populating a bypass-width table.
+ if (!OptSize) {
+ BasicBlock *BB = &*F.begin();
+ while (BB != nullptr) {
+ // optimizeDivRemBySelectDivisor may create new BBs, but we don't want
+ // to reapply the optimization to those blocks.
+ BasicBlock *Next = BB->getNextNode();
+ if (!llvm::shouldOptimizeForSize(BB, PSI, BFI))
+ EverMadeChange |= optimizeDivRemBySelectDivisor(BB, TLI, *DL, DTU, LI);
+ BB = Next;
+ }
+ }
+
// Get rid of @llvm.assume builtins before attempting to eliminate empty
// blocks, since there might be blocks that only contain @llvm.assume calls
// (plus arguments that we can get rid of).
diff --git a/llvm/test/CodeGen/X86/divrem-by-select-scalar.ll b/llvm/test/CodeGen/X86/divrem-by-select-scalar.ll
new file mode 100644
index 0000000000000..d4bd714ce0b20
--- /dev/null
+++ b/llvm/test/CodeGen/X86/divrem-by-select-scalar.ll
@@ -0,0 +1,100 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown | FileCheck %s
+
+; https://github.com/llvm/llvm-project/issues/214962
+define i32 @sdiv_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: sdiv_by_select_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dl
+; CHECK-NEXT: je .LBB0_2
+; CHECK-NEXT: # %bb.1: # %select.true
+; CHECK-NEXT: movl %edi, %eax
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: # kill: def $eax killed $eax def $rax
+; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB0_2: # %select.false
+; CHECK-NEXT: movslq %edi, %rax
+; CHECK-NEXT: imulq $-1840700269, %rax, %rax # imm = 0x92492493
+; CHECK-NEXT: shrq $32, %rax
+; CHECK-NEXT: addl %edi, %eax
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: shrl $31, %ecx
+; CHECK-NEXT: sarl $2, %eax
+; CHECK-NEXT: addl %ecx, %eax
+; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT: retq
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+define i32 @udiv_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: udiv_by_select_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dl
+; CHECK-NEXT: je .LBB1_2
+; CHECK-NEXT: # %bb.1: # %select.true
+; CHECK-NEXT: movl %edi, %eax
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: divl %esi
+; CHECK-NEXT: # kill: def $eax killed $eax def $rax
+; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB1_2: # %select.false
+; CHECK-NEXT: movl %edi, %eax
+; CHECK-NEXT: movabsq $2635249153617166336, %rcx # imm = 0x24924924A0000000
+; CHECK-NEXT: mulq %rcx
+; CHECK-NEXT: movq %rdx, %rax
+; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT: retq
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = udiv i32 %x, %sel
+ ret i32 %r
+}
+
+define i32 @srem_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: srem_by_select_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dl
+; CHECK-NEXT: je .LBB2_2
+; CHECK-NEXT: # %bb.1: # %select.true
+; CHECK-NEXT: movl %edi, %eax
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: movl %edx, %eax
+; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB2_2: # %select.false
+; CHECK-NEXT: movslq %edi, %rax
+; CHECK-NEXT: imulq $-1840700269, %rax, %rax # imm = 0x92492493
+; CHECK-NEXT: shrq $32, %rax
+; CHECK-NEXT: addl %edi, %eax
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: shrl $31, %ecx
+; CHECK-NEXT: sarl $2, %eax
+; CHECK-NEXT: addl %ecx, %eax
+; CHECK-NEXT: leal (,%rax,8), %ecx
+; CHECK-NEXT: subl %ecx, %eax
+; CHECK-NEXT: addl %edi, %eax
+; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT: retq
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = srem i32 %x, %sel
+ ret i32 %r
+}
+
+define i32 @sdiv_by_select_no_const_arm(i32 %x, i32 %y, i32 %z, i1 %k) {
+; CHECK-LABEL: sdiv_by_select_no_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl %edi, %eax
+; CHECK-NEXT: testb $1, %cl
+; CHECK-NEXT: cmovel %edx, %esi
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: retq
+ %sel = select i1 %k, i32 %y, i32 %z
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
diff --git a/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll b/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll
new file mode 100644
index 0000000000000..799af29e74c46
--- /dev/null
+++ b/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll
@@ -0,0 +1,165 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' < %s | FileCheck %s
+
+target triple = "x86_64--linux-gnu"
+
+; https://github.com/llvm/llvm-project/issues/214962
+define i32 @sdiv_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm(
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP4:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP3:%.*]] = sdiv i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP5:%.*]] = sdiv i32 [[X]], 7
+; CHECK-NEXT: br label [[DOTSPLIT]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ [[TMP5]], [[TMP4]] ]
+; CHECK-NEXT: ret i32 [[TMP6]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+define i32 @urem_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @urem_by_select_const_arm(
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP4:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP3:%.*]] = urem i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP5:%.*]] = urem i32 [[X]], 7
+; CHECK-NEXT: br label [[DOTSPLIT]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ [[TMP5]], [[TMP4]] ]
+; CHECK-NEXT: ret i32 [[TMP6]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = urem i32 %x, %sel
+ ret i32 %r
+}
+
+define i32 @sdiv_by_select_both_const_arms(i32 %x, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_both_const_arms(
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP4:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP3:%.*]] = sdiv i32 [[X:%.*]], 3
+; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP5:%.*]] = sdiv i32 [[X]], 7
+; CHECK-NEXT: br label [[DOTSPLIT]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ [[TMP5]], [[TMP4]] ]
+; CHECK-NEXT: ret i32 [[TMP6]]
+;
+ %sel = select i1 %k, i32 3, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - select feeds the dividend
+define i32 @sdiv_by_select_dividend_not_divisor(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_dividend_not_divisor(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %sel, %x
+ ret i32 %r
+}
+
+define i32 @sdiv_by_select_no_const_arm(i32 %x, i32 %y, i32 %z, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_no_const_arm(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 %z
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - don't want to duplicate the division
+define i32 @sdiv_by_select_const_arm_multiuse(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm_multiuse(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: [[R2:%.*]] = add i32 [[R]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R2]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ %r2 = add i32 %r, %sel
+ ret i32 %r2
+}
+
+; Negative test - minsize
+define i32 @sdiv_by_select_const_arm_minsize(i32 %x, i32 %y, i1 %k) minsize {
+; CHECK-LABEL: @sdiv_by_select_const_arm_minsize(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - optsize
+define i32 @sdiv_by_select_const_arm_optsize(i32 %x, i32 %y, i1 %k) optsize {
+; CHECK-LABEL: @sdiv_by_select_const_arm_optsize(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - zero divisor is UB
+define i32 @sdiv_by_select_zero_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_zero_const_arm(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 0
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 0
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - vectors are out of scope
+define <4 x i32> @sdiv_by_select_const_arm_vector(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm_vector(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], <4 x i32> [[Y:%.*]], <4 x i32> splat (i32 7)
+; CHECK-NEXT: [[R:%.*]] = sdiv <4 x i32> [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret <4 x i32> [[R]]
+;
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+; Negative test - don't want to divide twice
+define { i32, i32 } @sdiv_srem_by_same_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_srem_by_same_select_const_arm(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[Q:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[SEL]]
+; CHECK-NEXT: [[DM0:%.*]] = insertvalue { i32, i32 } poison, i32 [[Q]], 0
+; CHECK-NEXT: [[DM1:%.*]] = insertvalue { i32, i32 } [[DM0]], i32 [[R]], 1
+; CHECK-NEXT: ret { i32, i32 } [[DM1]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %q = sdiv i32 %x, %sel
+ %r = srem i32 %x, %sel
+ %dm0 = insertvalue { i32, i32 } poison, i32 %q, 0
+ %dm1 = insertvalue { i32, i32 } %dm0, i32 %r, 1
+ ret { i32, i32 } %dm1
+}
>From fd3d2c0955c10bdf2e026545228dad75d4b69b7b Mon Sep 17 00:00:00 2001
From: Le Gia Bao <legiabao7300 at gmail.com>
Date: Thu, 13 Aug 2026 17:44:02 +0700
Subject: [PATCH 2/4] format
---
llvm/lib/CodeGen/CodeGenPrepare.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index d05f1c33d5b88..1fe6461b80b73 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -596,7 +596,8 @@ static bool splitDivRemBySelectDivisor(Instruction *I,
return false;
// If the select has more than one use, there's probably a sibling rem
- // sharing this divisor and splitting them separately would duplicate the division
+ // sharing this divisor and splitting them separately would duplicate the
+ // division
auto *Sel = dyn_cast<SelectInst>(I->getOperand(1));
if (!Sel || !Sel->hasOneUse())
return false;
@@ -672,7 +673,8 @@ static bool optimizeDivRemBySelectDivisor(BasicBlock *BB,
bool MadeChange = false;
Instruction *Next = &*BB->begin();
while (Next != nullptr) {
- // We may add instructions immediately after I but we want to skip over them.
+ // We may add instructions immediately after I but we want to skip over
+ // them.
Instruction *I = Next;
Next = Next->getNextNode();
>From a89f20eedaee0301736da01afd10ca95ff2996ce Mon Sep 17 00:00:00 2001
From: Le Gia Bao <legiabao7300 at gmail.com>
Date: Thu, 13 Aug 2026 18:56:14 +0700
Subject: [PATCH 3/4] all tests
---
.../AArch64/div-rem-by-select.ll | 36 +++++++++++++++++++
.../CodeGenPrepare/RISCV/div-rem-by-select.ll | 36 +++++++++++++++++++
2 files changed, 72 insertions(+)
create mode 100644 llvm/test/Transforms/CodeGenPrepare/AArch64/div-rem-by-select.ll
create mode 100644 llvm/test/Transforms/CodeGenPrepare/RISCV/div-rem-by-select.ll
diff --git a/llvm/test/Transforms/CodeGenPrepare/AArch64/div-rem-by-select.ll b/llvm/test/Transforms/CodeGenPrepare/AArch64/div-rem-by-select.ll
new file mode 100644
index 0000000000000..1318a35736b85
--- /dev/null
+++ b/llvm/test/Transforms/CodeGenPrepare/AArch64/div-rem-by-select.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' < %s | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
+
+; https://github.com/llvm/llvm-project/issues/214962
+define i32 @sdiv_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm(
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[SELECT_TRUE:%.*]], label [[SELECT_FALSE:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP2:%.*]] = sdiv i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br label [[SELECT_END:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP3:%.*]] = sdiv i32 [[X]], 7
+; CHECK-NEXT: br label [[SELECT_END]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP4:%.*]] = phi i32 [ [[TMP2]], [[SELECT_TRUE]] ], [ [[TMP3]], [[SELECT_FALSE]] ]
+; CHECK-NEXT: ret i32 [[TMP4]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - minsize
+define i32 @sdiv_by_select_const_arm_minsize(i32 %x, i32 %y, i1 %k) minsize {
+; CHECK-LABEL: @sdiv_by_select_const_arm_minsize(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
diff --git a/llvm/test/Transforms/CodeGenPrepare/RISCV/div-rem-by-select.ll b/llvm/test/Transforms/CodeGenPrepare/RISCV/div-rem-by-select.ll
new file mode 100644
index 0000000000000..00ba2bf8f85fc
--- /dev/null
+++ b/llvm/test/Transforms/CodeGenPrepare/RISCV/div-rem-by-select.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' < %s | FileCheck %s
+
+target triple = "riscv64-unknown-linux-gnu"
+
+; https://github.com/llvm/llvm-project/issues/214962
+define i32 @sdiv_by_select_const_arm(i32 %x, i32 %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm(
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[SELECT_TRUE:%.*]], label [[SELECT_FALSE:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP2:%.*]] = sdiv i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br label [[SELECT_END:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP3:%.*]] = sdiv i32 [[X]], 7
+; CHECK-NEXT: br label [[SELECT_END]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP4:%.*]] = phi i32 [ [[TMP2]], [[SELECT_TRUE]] ], [ [[TMP3]], [[SELECT_FALSE]] ]
+; CHECK-NEXT: ret i32 [[TMP4]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
+
+; Negative test - minsize
+define i32 @sdiv_by_select_const_arm_minsize(i32 %x, i32 %y, i1 %k) minsize {
+; CHECK-LABEL: @sdiv_by_select_const_arm_minsize(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], i32 [[Y:%.*]], i32 7
+; CHECK-NEXT: [[R:%.*]] = sdiv i32 [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %sel = select i1 %k, i32 %y, i32 7
+ %r = sdiv i32 %x, %sel
+ ret i32 %r
+}
>From ca015cf97d5b0508dc0724c71801ccb3abae7a9f Mon Sep 17 00:00:00 2001
From: Le Gia Bao <legiabao7300 at gmail.com>
Date: Fri, 14 Aug 2026 14:13:11 +0700
Subject: [PATCH 4/4] support vector divs
---
llvm/lib/CodeGen/CodeGenPrepare.cpp | 27 ++-
.../CodeGen/X86/divrem-by-select-vector.ll | 181 ++++++++++++++++++
.../CodeGenPrepare/X86/div-rem-by-select.ll | 80 +++++++-
3 files changed, 280 insertions(+), 8 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/divrem-by-select-vector.ll
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 1fe6461b80b73..4f0b27775bf9b 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -572,10 +572,23 @@ static bool isDivOrRem(unsigned Opcode) {
static bool isCheapConstantDivisor(Value *V, const TargetLowering *TLI,
const DataLayout &DL, AttributeList Attr) {
- auto *C = dyn_cast<ConstantInt>(V);
- if (!C || C->isZero())
- return false;
- return !TLI->isIntDivCheap(TLI->getValueType(DL, C->getType()), Attr);
+ auto *C = dyn_cast<Constant>(V);
+ if (!C)
+ return false;
+ Type *ScalarTy = C->getType()->getScalarType();
+ if (auto *VecTy = dyn_cast<FixedVectorType>(C->getType())) {
+ // BuildSDIV compute per lane so it can be nonuniform
+ for (unsigned I = 0, E = VecTy->getNumElements(); I != E; ++I) {
+ auto *CI = dyn_cast_or_null<ConstantInt>(C->getAggregateElement(I));
+ if (!CI || CI->isZero())
+ return false;
+ }
+ } else {
+ auto *CI = dyn_cast<ConstantInt>(C);
+ if (!CI || CI->isZero())
+ return false;
+ }
+ return !TLI->isIntDivCheap(TLI->getValueType(DL, ScalarTy), Attr);
}
/// Fold a div/rem of a select divisor with a cheap constant arm into a
@@ -588,7 +601,7 @@ static bool splitDivRemBySelectDivisor(Instruction *I,
const TargetLowering *TLI,
const DataLayout &DL,
DomTreeUpdater *DTU, LoopInfo *LI) {
- if (!isDivOrRem(I->getOpcode()) || I->getType()->isVectorTy())
+ if (!isDivOrRem(I->getOpcode()))
return false;
// Don't touch loops. foldURemOfLoopIncrement handles that case
@@ -602,6 +615,10 @@ static bool splitDivRemBySelectDivisor(Instruction *I,
if (!Sel || !Sel->hasOneUse())
return false;
+ // Lanes pick different divisors which a branch can't express.
+ if (Sel->getCondition()->getType()->isVectorTy())
+ return false;
+
// If the dividend is also constant, foldBinOpIntoSelect handles it.
Value *Dividend = I->getOperand(0);
if (isa<Constant>(Dividend))
diff --git a/llvm/test/CodeGen/X86/divrem-by-select-vector.ll b/llvm/test/CodeGen/X86/divrem-by-select-vector.ll
new file mode 100644
index 0000000000000..5f97f454575a6
--- /dev/null
+++ b/llvm/test/CodeGen/X86/divrem-by-select-vector.ll
@@ -0,0 +1,181 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mcpu=x86-64-v3 | FileCheck %s
+
+; https://github.com/llvm/llvm-project/issues/214962
+define <4 x i32> @sdiv_by_select_const_arm(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: sdiv_by_select_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dil
+; CHECK-NEXT: je .LBB0_2
+; CHECK-NEXT: # %bb.1: # %select.true
+; CHECK-NEXT: vpextrd $1, %xmm0, %eax
+; CHECK-NEXT: vpextrd $1, %xmm1, %ecx
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %ecx
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: vmovd %xmm0, %eax
+; CHECK-NEXT: vmovd %xmm1, %esi
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: vmovd %eax, %xmm2
+; CHECK-NEXT: vpinsrd $1, %ecx, %xmm2, %xmm2
+; CHECK-NEXT: vpextrd $2, %xmm0, %eax
+; CHECK-NEXT: vpextrd $2, %xmm1, %ecx
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %ecx
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: vpextrd $3, %xmm0, %eax
+; CHECK-NEXT: vpextrd $3, %xmm1, %esi
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: vpinsrd $2, %ecx, %xmm2, %xmm0
+; CHECK-NEXT: vpinsrd $3, %eax, %xmm0, %xmm0
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB0_2: # %select.false
+; CHECK-NEXT: vpshufd {{.*#+}} xmm1 = xmm0[1,1,3,3]
+; CHECK-NEXT: vpbroadcastd {{.*#+}} xmm2 = [2454267027,2454267027,2454267027,2454267027]
+; CHECK-NEXT: vpmuldq %xmm2, %xmm1, %xmm1
+; CHECK-NEXT: vpmuldq %xmm2, %xmm0, %xmm2
+; CHECK-NEXT: vpshufd {{.*#+}} xmm2 = xmm2[1,1,3,3]
+; CHECK-NEXT: vpblendd {{.*#+}} xmm1 = xmm2[0],xmm1[1],xmm2[2],xmm1[3]
+; CHECK-NEXT: vpaddd %xmm0, %xmm1, %xmm0
+; CHECK-NEXT: vpsrld $31, %xmm0, %xmm1
+; CHECK-NEXT: vpsrad $2, %xmm0, %xmm0
+; CHECK-NEXT: vpaddd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT: retq
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+define <4 x i32> @udiv_by_select_const_arm(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: udiv_by_select_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dil
+; CHECK-NEXT: je .LBB1_2
+; CHECK-NEXT: # %bb.1: # %select.true
+; CHECK-NEXT: vpextrd $1, %xmm0, %eax
+; CHECK-NEXT: vpextrd $1, %xmm1, %ecx
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: divl %ecx
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: vmovd %xmm0, %eax
+; CHECK-NEXT: vmovd %xmm1, %esi
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: divl %esi
+; CHECK-NEXT: vpextrd $2, %xmm0, %edx
+; CHECK-NEXT: vmovd %eax, %xmm2
+; CHECK-NEXT: vpextrd $2, %xmm1, %esi
+; CHECK-NEXT: movl %edx, %eax
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: divl %esi
+; CHECK-NEXT: movl %eax, %esi
+; CHECK-NEXT: vpinsrd $1, %ecx, %xmm2, %xmm2
+; CHECK-NEXT: vpextrd $3, %xmm0, %eax
+; CHECK-NEXT: vpextrd $3, %xmm1, %ecx
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: divl %ecx
+; CHECK-NEXT: vpinsrd $2, %esi, %xmm2, %xmm0
+; CHECK-NEXT: vpinsrd $3, %eax, %xmm0, %xmm0
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB1_2: # %select.false
+; CHECK-NEXT: vpshufd {{.*#+}} xmm1 = xmm0[1,1,3,3]
+; CHECK-NEXT: vpbroadcastd {{.*#+}} xmm2 = [613566757,613566757,613566757,613566757]
+; CHECK-NEXT: vpmuludq %xmm2, %xmm1, %xmm1
+; CHECK-NEXT: vpmuludq %xmm2, %xmm0, %xmm2
+; CHECK-NEXT: vpshufd {{.*#+}} xmm2 = xmm2[1,1,3,3]
+; CHECK-NEXT: vpblendd {{.*#+}} xmm1 = xmm2[0],xmm1[1],xmm2[2],xmm1[3]
+; CHECK-NEXT: vpsubd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT: vpsrld $1, %xmm0, %xmm0
+; CHECK-NEXT: vpaddd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT: vpsrld $2, %xmm0, %xmm0
+; CHECK-NEXT: retq
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %r = udiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+define <4 x i32> @srem_by_select_const_arm(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: srem_by_select_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dil
+; CHECK-NEXT: je .LBB2_2
+; CHECK-NEXT: # %bb.1: # %select.true
+; CHECK-NEXT: vpextrd $1, %xmm0, %eax
+; CHECK-NEXT: vpextrd $1, %xmm1, %ecx
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %ecx
+; CHECK-NEXT: movl %edx, %ecx
+; CHECK-NEXT: vmovd %xmm0, %eax
+; CHECK-NEXT: vmovd %xmm1, %esi
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: vmovd %edx, %xmm2
+; CHECK-NEXT: vpinsrd $1, %ecx, %xmm2, %xmm2
+; CHECK-NEXT: vpextrd $2, %xmm0, %eax
+; CHECK-NEXT: vpextrd $2, %xmm1, %ecx
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %ecx
+; CHECK-NEXT: movl %edx, %ecx
+; CHECK-NEXT: vpextrd $3, %xmm0, %eax
+; CHECK-NEXT: vpextrd $3, %xmm1, %esi
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: vpinsrd $2, %ecx, %xmm2, %xmm0
+; CHECK-NEXT: vpinsrd $3, %edx, %xmm0, %xmm0
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB2_2: # %select.false
+; CHECK-NEXT: vpshufd {{.*#+}} xmm1 = xmm0[1,1,3,3]
+; CHECK-NEXT: vpbroadcastd {{.*#+}} xmm2 = [2454267027,2454267027,2454267027,2454267027]
+; CHECK-NEXT: vpmuldq %xmm2, %xmm1, %xmm1
+; CHECK-NEXT: vpmuldq %xmm2, %xmm0, %xmm2
+; CHECK-NEXT: vpshufd {{.*#+}} xmm2 = xmm2[1,1,3,3]
+; CHECK-NEXT: vpblendd {{.*#+}} xmm1 = xmm2[0],xmm1[1],xmm2[2],xmm1[3]
+; CHECK-NEXT: vpaddd %xmm0, %xmm1, %xmm1
+; CHECK-NEXT: vpsrld $31, %xmm1, %xmm2
+; CHECK-NEXT: vpsrad $2, %xmm1, %xmm1
+; CHECK-NEXT: vpaddd %xmm2, %xmm1, %xmm1
+; CHECK-NEXT: vpslld $3, %xmm1, %xmm2
+; CHECK-NEXT: vpsubd %xmm2, %xmm1, %xmm1
+; CHECK-NEXT: vpaddd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT: retq
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %r = srem <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+define <4 x i32> @sdiv_by_select_no_const_arm(<4 x i32> %x, <4 x i32> %y, <4 x i32> %z, i1 %k) {
+; CHECK-LABEL: sdiv_by_select_no_const_arm:
+; CHECK: # %bb.0:
+; CHECK-NEXT: testb $1, %dil
+; CHECK-NEXT: jne .LBB3_2
+; CHECK-NEXT: # %bb.1:
+; CHECK-NEXT: vmovdqa %xmm2, %xmm1
+; CHECK-NEXT: .LBB3_2:
+; CHECK-NEXT: vpextrd $1, %xmm1, %ecx
+; CHECK-NEXT: vpextrd $1, %xmm0, %eax
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %ecx
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: vmovd %xmm1, %esi
+; CHECK-NEXT: vmovd %xmm0, %eax
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: vmovd %eax, %xmm2
+; CHECK-NEXT: vpinsrd $1, %ecx, %xmm2, %xmm2
+; CHECK-NEXT: vpextrd $2, %xmm1, %ecx
+; CHECK-NEXT: vpextrd $2, %xmm0, %eax
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %ecx
+; CHECK-NEXT: movl %eax, %ecx
+; CHECK-NEXT: vpextrd $3, %xmm1, %esi
+; CHECK-NEXT: vpextrd $3, %xmm0, %eax
+; CHECK-NEXT: cltd
+; CHECK-NEXT: idivl %esi
+; CHECK-NEXT: vpinsrd $2, %ecx, %xmm2, %xmm0
+; CHECK-NEXT: vpinsrd $3, %eax, %xmm0, %xmm0
+; CHECK-NEXT: retq
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> %z
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
diff --git a/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll b/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll
index 799af29e74c46..96b54b1c2bc65 100644
--- a/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll
+++ b/llvm/test/Transforms/CodeGenPrepare/X86/div-rem-by-select.ll
@@ -134,14 +134,88 @@ define i32 @sdiv_by_select_zero_const_arm(i32 %x, i32 %y, i1 %k) {
ret i32 %r
}
-; Negative test - vectors are out of scope
+; Negative test - zero divisor is UB
+define <4 x i32> @sdiv_by_select_zero_const_arm_vector(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_zero_const_arm_vector(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], <4 x i32> [[Y:%.*]], <4 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = sdiv <4 x i32> [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret <4 x i32> [[R]]
+;
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> zeroinitializer
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
define <4 x i32> @sdiv_by_select_const_arm_vector(<4 x i32> %x, <4 x i32> %y, i1 %k) {
; CHECK-LABEL: @sdiv_by_select_const_arm_vector(
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], <4 x i32> [[Y:%.*]], <4 x i32> splat (i32 7)
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP4:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP3:%.*]] = sdiv <4 x i32> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP5:%.*]] = sdiv <4 x i32> [[X]], splat (i32 7)
+; CHECK-NEXT: br label [[DOTSPLIT]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP6:%.*]] = phi <4 x i32> [ [[TMP3]], [[TMP2]] ], [ [[TMP5]], [[TMP4]] ]
+; CHECK-NEXT: ret <4 x i32> [[TMP6]]
+;
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+define <4 x i32> @sdiv_by_select_const_arm_vector_nonsplat(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm_vector_nonsplat(
+; CHECK-NEXT: [[TMP1:%.*]] = freeze i1 [[K:%.*]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP4:%.*]]
+; CHECK: select.true:
+; CHECK-NEXT: [[TMP3:%.*]] = sdiv <4 x i32> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
+; CHECK: select.false:
+; CHECK-NEXT: [[TMP5:%.*]] = sdiv <4 x i32> [[X]], <i32 5, i32 7, i32 9, i32 11>
+; CHECK-NEXT: br label [[DOTSPLIT]]
+; CHECK: select.end:
+; CHECK-NEXT: [[TMP6:%.*]] = phi <4 x i32> [ [[TMP3]], [[TMP2]] ], [ [[TMP5]], [[TMP4]] ]
+; CHECK-NEXT: ret <4 x i32> [[TMP6]]
+;
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 5, i32 7, i32 9, i32 11>
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+; Negative test - vector cond
+define <4 x i32> @sdiv_by_select_const_arm_vector_cond(<4 x i32> %x, <4 x i32> %y, <4 x i1> %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm_vector_cond(
+; CHECK-NEXT: [[SEL:%.*]] = select <4 x i1> [[K:%.*]], <4 x i32> [[Y:%.*]], <4 x i32> splat (i32 7)
; CHECK-NEXT: [[R:%.*]] = sdiv <4 x i32> [[X:%.*]], [[SEL]]
; CHECK-NEXT: ret <4 x i32> [[R]]
;
- %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %sel = select <4 x i1> %k, <4 x i32> %y, <4 x i32> <i32 7, i32 7, i32 7, i32 7>
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+; Negative test - one lane is zero
+define <4 x i32> @sdiv_by_select_const_arm_vector_partial_zero(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm_vector_partial_zero(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], <4 x i32> [[Y:%.*]], <4 x i32> <i32 2, i32 0, i32 1, i32 2>
+; CHECK-NEXT: [[R:%.*]] = sdiv <4 x i32> [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret <4 x i32> [[R]]
+;
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 2, i32 0, i32 1, i32 2>
+ %r = sdiv <4 x i32> %x, %sel
+ ret <4 x i32> %r
+}
+
+; Negative test - one lane is poison
+define <4 x i32> @sdiv_by_select_const_arm_vector_partial_poison(<4 x i32> %x, <4 x i32> %y, i1 %k) {
+; CHECK-LABEL: @sdiv_by_select_const_arm_vector_partial_poison(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[K:%.*]], <4 x i32> [[Y:%.*]], <4 x i32> <i32 2, i32 poison, i32 1, i32 2>
+; CHECK-NEXT: [[R:%.*]] = sdiv <4 x i32> [[X:%.*]], [[SEL]]
+; CHECK-NEXT: ret <4 x i32> [[R]]
+;
+ %sel = select i1 %k, <4 x i32> %y, <4 x i32> <i32 2, i32 poison, i32 1, i32 2>
%r = sdiv <4 x i32> %x, %sel
ret <4 x i32> %r
}
More information about the llvm-commits
mailing list