[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