[llvm] [InstCombine] Lower flag check pattern to use a bitmask-shift (PR #169557)

Ryan Buchner via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 5 17:31:59 PST 2025


https://github.com/bababuck updated https://github.com/llvm/llvm-project/pull/169557

>From 5d4f0f773bb9a378e4bd7172a7954b8bad5a679e Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 25 Nov 2025 09:47:21 -0800
Subject: [PATCH 01/17] [RISCV] Add new test for RISCV flag check optimization

---
 llvm/test/CodeGen/RISCV/flag_check.ll | 218 ++++++++++++++++++++++++++
 1 file changed, 218 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/flag_check.ll

diff --git a/llvm/test/CodeGen/RISCV/flag_check.ll b/llvm/test/CodeGen/RISCV/flag_check.ll
new file mode 100644
index 0000000000000..22df3d1859633
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/flag_check.ll
@@ -0,0 +1,218 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV64
+
+define i1 @or_icmp_2(i32 signext %type) {
+; CHECK-LABEL: or_icmp_2:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_3(i32 signext %type) {
+; CHECK-LABEL: or_icmp_3:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    seqz a2, a0
+; CHECK-NEXT:    addi a0, a0, -15
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    or a1, a1, a2
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp3 = icmp eq i32 %type, 15
+  %or.cond1 = or i1 %cmp3, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_4_tree(i32 signext %type) {
+; CHECK-LABEL: or_icmp_4_tree:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    seqz a2, a0
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    or a1, a1, a2
+; CHECK-NEXT:    addi a2, a0, -15
+; CHECK-NEXT:    addi a0, a0, -22
+; CHECK-NEXT:    seqz a2, a2
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a0, a2, a0
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 15
+  %cmp3 = icmp eq i32 %type, 22
+  %or.cond1 = or i1 %cmp2, %cmp3
+  %or.cond2 = or i1 %or.cond1, %or.cond
+  ret i1 %or.cond2
+}
+
+define i1 @or_icmp_7(i32 signext %type) {
+; CHECK-LABEL: or_icmp_7:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    seqz a2, a0
+; CHECK-NEXT:    addi a3, a0, -17
+; CHECK-NEXT:    addi a4, a0, -3
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    or a1, a1, a2
+; CHECK-NEXT:    addi a2, a0, -31
+; CHECK-NEXT:    seqz a3, a3
+; CHECK-NEXT:    seqz a4, a4
+; CHECK-NEXT:    or a3, a4, a3
+; CHECK-NEXT:    addi a4, a0, -14
+; CHECK-NEXT:    seqz a2, a2
+; CHECK-NEXT:    seqz a4, a4
+; CHECK-NEXT:    or a2, a4, a2
+; CHECK-NEXT:    addi a0, a0, -28
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a1, a3, a1
+; CHECK-NEXT:    or a0, a0, a2
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 17
+  %or.cond1 = or i1 %cmp2, %or.cond
+  %cmp3 = icmp eq i32 %type, 3
+  %or.cond2 = or i1 %cmp3, %or.cond1
+  %cmp4 = icmp eq i32 %type, 31
+  %or.cond3 = or i1 %cmp4, %or.cond2
+  %cmp5 = icmp eq i32 %type, 14
+  %or.cond4 = or i1 %cmp5, %or.cond3
+  %cmp6 = icmp eq i32 %type, 28
+  %or.cond5 = or i1 %cmp6, %or.cond4
+  ret i1 %or.cond5
+}
+
+define i1 @or_icmp_gte_64(i32 signext %type) {
+; CHECK-LABEL: or_icmp_gte_64:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    addi a0, a0, -64
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 64
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_multiple_uses(i32 signext %type) {
+; CHECK-LABEL: or_icmp_multiple_uses:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:    xor a0, a1, a0
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %or.cond1 = xor i1 %cmp, %or.cond
+  ret i1 %or.cond1
+}
+
+
+define i1 @or_icmp_not_eq(i32 signext %type) {
+; CHECK-LABEL: or_icmp_not_eq:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    snez a0, a0
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp ugt i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_xlen(i32 signext %type) {
+; CHECK-LABEL: or_icmp_xlen:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a1, a0, -6
+; CHECK-NEXT:    addi a0, a0, -32
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 32
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_i64(i64 signext %type) {
+; RV32-LABEL: or_icmp_i64:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    xori a2, a0, 6
+; RV32-NEXT:    or a3, a0, a1
+; RV32-NEXT:    xori a0, a0, 15
+; RV32-NEXT:    or a2, a2, a1
+; RV32-NEXT:    seqz a3, a3
+; RV32-NEXT:    or a0, a0, a1
+; RV32-NEXT:    seqz a1, a2
+; RV32-NEXT:    or a1, a1, a3
+; RV32-NEXT:    seqz a0, a0
+; RV32-NEXT:    or a0, a0, a1
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: or_icmp_i64:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    addi a1, a0, -6
+; RV64-NEXT:    seqz a2, a0
+; RV64-NEXT:    addi a0, a0, -15
+; RV64-NEXT:    seqz a1, a1
+; RV64-NEXT:    or a1, a1, a2
+; RV64-NEXT:    seqz a0, a0
+; RV64-NEXT:    or a0, a0, a1
+; RV64-NEXT:    ret
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp3 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp3, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_specific(i32 signext %type, i32 signext %type1) {
+; CHECK-LABEL: or_icmp_specific:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi a0, a0, -6
+; CHECK-NEXT:    addi a1, a1, -32
+; CHECK-NEXT:    seqz a0, a0
+; CHECK-NEXT:    seqz a1, a1
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    ret
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type1, 32
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}

>From 95514cc36066b52711aacac66871b947c191ade2 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 10 Nov 2025 14:23:02 -0800
Subject: [PATCH 02/17] [RISCV] Lower flag check pattern to use a bitmask-shift

The following C-code:

bool or_icmp(int type) {
  return type == 0 || type == 6 || type == 15;
}

Currently lowers to:

define i1 @or_icmp(i32 signext %type) {
entry:
  %cmp = icmp eq i32 %type, 6
  %cmp1 = icmp eq i32 %type, 0
  %or.cond = or i1 %cmp, %cmp1
  %cmp3 = icmp eq i32 %type, 15
  %or.cond1 = or i1 %cmp3, %or.cond
  ret i1 %or.cond1
}

But more optimally lowers to:

define i1 @or_icmp(i32 signext %type) {
entry:
  %srl = lshr i32 32833, %type
  %srl.1 = trunc i32 %srl to i1
  %cmp = icmp ult i32 %type, 64
  %and = and i1 %srl.1, %cmp
  ret i1 %and
}
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp |  49 ++++++
 llvm/test/CodeGen/RISCV/flag_check.ll       | 157 +++++++++++---------
 2 files changed, 139 insertions(+), 67 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 3b250d7d9ad1f..50076dbb4555e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -16736,6 +16736,52 @@ static SDValue combineOrAndToBitfieldInsert(SDNode *N, SelectionDAG &DAG,
   return DAG.getNode(RISCVISD::QC_INSB, DL, MVT::i32, Ops);
 }
 
+// or (icmp eq x, imm0), (icmp eq x, imm1) -> czero.eqz (sltui x, 64), (bext x,
+// 1 << imm0 | 1 << imm1) If [imm0, imm1] < 64
+static SDValue combineOrOfImmCmpToBitExtract(SDNode *N, SelectionDAG &DAG,
+                                             const RISCVSubtarget &Subtarget) {
+  using namespace SDPatternMatch;
+
+  auto CollectSetEqImmTree = [](auto &&Self, SmallVector<APInt, 4> &FlagVals,
+                                SDNode *N, SDValue &X) -> bool {
+    APInt Imm;
+    if (X ? sd_match(N, m_OneUse(m_SetCC(m_Specific(X), m_ConstInt(Imm),
+                                         m_SpecificCondCode(ISD::SETEQ))))
+          : sd_match(N, m_OneUse(m_SetCC(m_Value(X), m_ConstInt(Imm),
+                                         m_SpecificCondCode(ISD::SETEQ))))) {
+      FlagVals.push_back(Imm);
+      return true;
+    }
+    SDValue LHS, RHS;
+    if (sd_match(N, m_OneUse(m_Or(m_Value(LHS), m_Value(RHS))))) {
+      return Self(Self, FlagVals, LHS.getNode(), X) &&
+             Self(Self, FlagVals, RHS.getNode(), X);
+    }
+    return false;
+  };
+
+  SmallVector<APInt, 4> FlagVals;
+  SDValue X;
+  if (!CollectSetEqImmTree(CollectSetEqImmTree, FlagVals, N, X))
+    return SDValue();
+
+  unsigned XLen = Subtarget.getXLen();
+  uint64_t BitMask = 0;
+  for (auto &Imm : FlagVals) {
+    if (Imm.uge(XLen))
+      return SDValue();
+    BitMask |= ((uint64_t)1 << Imm.getZExtValue());
+  }
+
+  SDLoc DL(N);
+  EVT VT = N->getValueType(0);
+  SDValue BitExtract =
+      DAG.getNode(ISD::SRL, DL, VT, DAG.getConstant(BitMask, DL, VT), X);
+  SDValue Lt64Check =
+      DAG.getSetCC(DL, VT, X, DAG.getConstant(XLen, DL, VT), ISD::SETULT);
+  return DAG.getNode(ISD::AND, DL, VT, Lt64Check, BitExtract);
+}
+
 static SDValue performORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
                                 const RISCVSubtarget &Subtarget) {
   SelectionDAG &DAG = DCI.DAG;
@@ -16748,6 +16794,9 @@ static SDValue performORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
     return V;
   if (SDValue V = combineBinOpOfExtractToReduceTree(N, DAG, Subtarget))
     return V;
+  if (DCI.isAfterLegalizeDAG())
+    if (SDValue V = combineOrOfImmCmpToBitExtract(N, DAG, Subtarget))
+      return V;
 
   if (DCI.isAfterLegalizeDAG())
     if (SDValue V = combineDeMorganOfBoolean(N, DAG))
diff --git a/llvm/test/CodeGen/RISCV/flag_check.ll b/llvm/test/CodeGen/RISCV/flag_check.ll
index 22df3d1859633..86049bf53379c 100644
--- a/llvm/test/CodeGen/RISCV/flag_check.ll
+++ b/llvm/test/CodeGen/RISCV/flag_check.ll
@@ -3,13 +3,21 @@
 ; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV64
 
 define i1 @or_icmp_2(i32 signext %type) {
-; CHECK-LABEL: or_icmp_2:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a0, a1, a0
-; CHECK-NEXT:    ret
+; RV32-LABEL: or_icmp_2:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    li a1, 65
+; RV32-NEXT:    srl a1, a1, a0
+; RV32-NEXT:    sltiu a0, a0, 32
+; RV32-NEXT:    and a0, a0, a1
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: or_icmp_2:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    li a1, 65
+; RV64-NEXT:    srl a1, a1, a0
+; RV64-NEXT:    sltiu a0, a0, 64
+; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    ret
 entry:
   %cmp = icmp eq i32 %type, 6
   %cmp1 = icmp eq i32 %type, 0
@@ -18,16 +26,23 @@ entry:
 }
 
 define i1 @or_icmp_3(i32 signext %type) {
-; CHECK-LABEL: or_icmp_3:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    seqz a2, a0
-; CHECK-NEXT:    addi a0, a0, -15
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    or a1, a1, a2
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a0, a0, a1
-; CHECK-NEXT:    ret
+; RV32-LABEL: or_icmp_3:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    lui a1, 8
+; RV32-NEXT:    addi a1, a1, 65
+; RV32-NEXT:    srl a1, a1, a0
+; RV32-NEXT:    sltiu a0, a0, 32
+; RV32-NEXT:    and a0, a0, a1
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: or_icmp_3:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    lui a1, 8
+; RV64-NEXT:    addi a1, a1, 65
+; RV64-NEXT:    srl a1, a1, a0
+; RV64-NEXT:    sltiu a0, a0, 64
+; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    ret
 entry:
   %cmp = icmp eq i32 %type, 6
   %cmp1 = icmp eq i32 %type, 0
@@ -38,19 +53,23 @@ entry:
 }
 
 define i1 @or_icmp_4_tree(i32 signext %type) {
-; CHECK-LABEL: or_icmp_4_tree:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    seqz a2, a0
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    or a1, a1, a2
-; CHECK-NEXT:    addi a2, a0, -15
-; CHECK-NEXT:    addi a0, a0, -22
-; CHECK-NEXT:    seqz a2, a2
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a0, a2, a0
-; CHECK-NEXT:    or a0, a0, a1
-; CHECK-NEXT:    ret
+; RV32-LABEL: or_icmp_4_tree:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    lui a1, 1032
+; RV32-NEXT:    addi a1, a1, 65
+; RV32-NEXT:    srl a1, a1, a0
+; RV32-NEXT:    sltiu a0, a0, 32
+; RV32-NEXT:    and a0, a0, a1
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: or_icmp_4_tree:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    lui a1, 1032
+; RV64-NEXT:    addi a1, a1, 65
+; RV64-NEXT:    srl a1, a1, a0
+; RV64-NEXT:    sltiu a0, a0, 64
+; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    ret
 entry:
   %cmp = icmp eq i32 %type, 6
   %cmp1 = icmp eq i32 %type, 0
@@ -63,28 +82,24 @@ entry:
 }
 
 define i1 @or_icmp_7(i32 signext %type) {
-; CHECK-LABEL: or_icmp_7:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    seqz a2, a0
-; CHECK-NEXT:    addi a3, a0, -17
-; CHECK-NEXT:    addi a4, a0, -3
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    or a1, a1, a2
-; CHECK-NEXT:    addi a2, a0, -31
-; CHECK-NEXT:    seqz a3, a3
-; CHECK-NEXT:    seqz a4, a4
-; CHECK-NEXT:    or a3, a4, a3
-; CHECK-NEXT:    addi a4, a0, -14
-; CHECK-NEXT:    seqz a2, a2
-; CHECK-NEXT:    seqz a4, a4
-; CHECK-NEXT:    or a2, a4, a2
-; CHECK-NEXT:    addi a0, a0, -28
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a1, a3, a1
-; CHECK-NEXT:    or a0, a0, a2
-; CHECK-NEXT:    or a0, a0, a1
-; CHECK-NEXT:    ret
+; RV32-LABEL: or_icmp_7:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    lui a1, 589860
+; RV32-NEXT:    addi a1, a1, 73
+; RV32-NEXT:    srl a1, a1, a0
+; RV32-NEXT:    sltiu a0, a0, 32
+; RV32-NEXT:    and a0, a0, a1
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: or_icmp_7:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    lui a1, 147465
+; RV64-NEXT:    slli a1, a1, 2
+; RV64-NEXT:    addi a1, a1, 73
+; RV64-NEXT:    srl a1, a1, a0
+; RV64-NEXT:    sltiu a0, a0, 64
+; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    ret
 entry:
   %cmp = icmp eq i32 %type, 6
   %cmp1 = icmp eq i32 %type, 0
@@ -152,14 +167,24 @@ entry:
 }
 
 define i1 @or_icmp_xlen(i32 signext %type) {
-; CHECK-LABEL: or_icmp_xlen:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    addi a0, a0, -32
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a0, a1, a0
-; CHECK-NEXT:    ret
+; RV32-LABEL: or_icmp_xlen:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    addi a1, a0, -6
+; RV32-NEXT:    addi a0, a0, -32
+; RV32-NEXT:    seqz a1, a1
+; RV32-NEXT:    seqz a0, a0
+; RV32-NEXT:    or a0, a1, a0
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: or_icmp_xlen:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    li a1, 1
+; RV64-NEXT:    slli a1, a1, 32
+; RV64-NEXT:    addi a1, a1, 64
+; RV64-NEXT:    srl a1, a1, a0
+; RV64-NEXT:    sltiu a0, a0, 64
+; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    ret
 entry:
   %cmp = icmp eq i32 %type, 6
   %cmp1 = icmp eq i32 %type, 32
@@ -184,13 +209,11 @@ define i1 @or_icmp_i64(i64 signext %type) {
 ;
 ; RV64-LABEL: or_icmp_i64:
 ; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    addi a1, a0, -6
-; RV64-NEXT:    seqz a2, a0
-; RV64-NEXT:    addi a0, a0, -15
-; RV64-NEXT:    seqz a1, a1
-; RV64-NEXT:    or a1, a1, a2
-; RV64-NEXT:    seqz a0, a0
-; RV64-NEXT:    or a0, a0, a1
+; RV64-NEXT:    lui a1, 8
+; RV64-NEXT:    addi a1, a1, 65
+; RV64-NEXT:    srl a1, a1, a0
+; RV64-NEXT:    sltiu a0, a0, 64
+; RV64-NEXT:    and a0, a0, a1
 ; RV64-NEXT:    ret
 entry:
   %cmp = icmp eq i64 %type, 6

>From ccfc7a84816f00b396f68ae9160770d41addaca9 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Wed, 26 Nov 2025 14:41:12 -0800
Subject: [PATCH 03/17] [RISCV] Revert implementation in RISCV

Will move to InstCombine
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp |  49 ----
 llvm/test/CodeGen/RISCV/flag_check.ll       | 241 --------------------
 2 files changed, 290 deletions(-)
 delete mode 100644 llvm/test/CodeGen/RISCV/flag_check.ll

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 50076dbb4555e..3b250d7d9ad1f 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -16736,52 +16736,6 @@ static SDValue combineOrAndToBitfieldInsert(SDNode *N, SelectionDAG &DAG,
   return DAG.getNode(RISCVISD::QC_INSB, DL, MVT::i32, Ops);
 }
 
-// or (icmp eq x, imm0), (icmp eq x, imm1) -> czero.eqz (sltui x, 64), (bext x,
-// 1 << imm0 | 1 << imm1) If [imm0, imm1] < 64
-static SDValue combineOrOfImmCmpToBitExtract(SDNode *N, SelectionDAG &DAG,
-                                             const RISCVSubtarget &Subtarget) {
-  using namespace SDPatternMatch;
-
-  auto CollectSetEqImmTree = [](auto &&Self, SmallVector<APInt, 4> &FlagVals,
-                                SDNode *N, SDValue &X) -> bool {
-    APInt Imm;
-    if (X ? sd_match(N, m_OneUse(m_SetCC(m_Specific(X), m_ConstInt(Imm),
-                                         m_SpecificCondCode(ISD::SETEQ))))
-          : sd_match(N, m_OneUse(m_SetCC(m_Value(X), m_ConstInt(Imm),
-                                         m_SpecificCondCode(ISD::SETEQ))))) {
-      FlagVals.push_back(Imm);
-      return true;
-    }
-    SDValue LHS, RHS;
-    if (sd_match(N, m_OneUse(m_Or(m_Value(LHS), m_Value(RHS))))) {
-      return Self(Self, FlagVals, LHS.getNode(), X) &&
-             Self(Self, FlagVals, RHS.getNode(), X);
-    }
-    return false;
-  };
-
-  SmallVector<APInt, 4> FlagVals;
-  SDValue X;
-  if (!CollectSetEqImmTree(CollectSetEqImmTree, FlagVals, N, X))
-    return SDValue();
-
-  unsigned XLen = Subtarget.getXLen();
-  uint64_t BitMask = 0;
-  for (auto &Imm : FlagVals) {
-    if (Imm.uge(XLen))
-      return SDValue();
-    BitMask |= ((uint64_t)1 << Imm.getZExtValue());
-  }
-
-  SDLoc DL(N);
-  EVT VT = N->getValueType(0);
-  SDValue BitExtract =
-      DAG.getNode(ISD::SRL, DL, VT, DAG.getConstant(BitMask, DL, VT), X);
-  SDValue Lt64Check =
-      DAG.getSetCC(DL, VT, X, DAG.getConstant(XLen, DL, VT), ISD::SETULT);
-  return DAG.getNode(ISD::AND, DL, VT, Lt64Check, BitExtract);
-}
-
 static SDValue performORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
                                 const RISCVSubtarget &Subtarget) {
   SelectionDAG &DAG = DCI.DAG;
@@ -16794,9 +16748,6 @@ static SDValue performORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
     return V;
   if (SDValue V = combineBinOpOfExtractToReduceTree(N, DAG, Subtarget))
     return V;
-  if (DCI.isAfterLegalizeDAG())
-    if (SDValue V = combineOrOfImmCmpToBitExtract(N, DAG, Subtarget))
-      return V;
 
   if (DCI.isAfterLegalizeDAG())
     if (SDValue V = combineDeMorganOfBoolean(N, DAG))
diff --git a/llvm/test/CodeGen/RISCV/flag_check.ll b/llvm/test/CodeGen/RISCV/flag_check.ll
deleted file mode 100644
index 86049bf53379c..0000000000000
--- a/llvm/test/CodeGen/RISCV/flag_check.ll
+++ /dev/null
@@ -1,241 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV32
-; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV64
-
-define i1 @or_icmp_2(i32 signext %type) {
-; RV32-LABEL: or_icmp_2:
-; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    li a1, 65
-; RV32-NEXT:    srl a1, a1, a0
-; RV32-NEXT:    sltiu a0, a0, 32
-; RV32-NEXT:    and a0, a0, a1
-; RV32-NEXT:    ret
-;
-; RV64-LABEL: or_icmp_2:
-; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    li a1, 65
-; RV64-NEXT:    srl a1, a1, a0
-; RV64-NEXT:    sltiu a0, a0, 64
-; RV64-NEXT:    and a0, a0, a1
-; RV64-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_3(i32 signext %type) {
-; RV32-LABEL: or_icmp_3:
-; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    lui a1, 8
-; RV32-NEXT:    addi a1, a1, 65
-; RV32-NEXT:    srl a1, a1, a0
-; RV32-NEXT:    sltiu a0, a0, 32
-; RV32-NEXT:    and a0, a0, a1
-; RV32-NEXT:    ret
-;
-; RV64-LABEL: or_icmp_3:
-; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    lui a1, 8
-; RV64-NEXT:    addi a1, a1, 65
-; RV64-NEXT:    srl a1, a1, a0
-; RV64-NEXT:    sltiu a0, a0, 64
-; RV64-NEXT:    and a0, a0, a1
-; RV64-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp3 = icmp eq i32 %type, 15
-  %or.cond1 = or i1 %cmp3, %or.cond
-  ret i1 %or.cond1
-}
-
-define i1 @or_icmp_4_tree(i32 signext %type) {
-; RV32-LABEL: or_icmp_4_tree:
-; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    lui a1, 1032
-; RV32-NEXT:    addi a1, a1, 65
-; RV32-NEXT:    srl a1, a1, a0
-; RV32-NEXT:    sltiu a0, a0, 32
-; RV32-NEXT:    and a0, a0, a1
-; RV32-NEXT:    ret
-;
-; RV64-LABEL: or_icmp_4_tree:
-; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    lui a1, 1032
-; RV64-NEXT:    addi a1, a1, 65
-; RV64-NEXT:    srl a1, a1, a0
-; RV64-NEXT:    sltiu a0, a0, 64
-; RV64-NEXT:    and a0, a0, a1
-; RV64-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i32 %type, 15
-  %cmp3 = icmp eq i32 %type, 22
-  %or.cond1 = or i1 %cmp2, %cmp3
-  %or.cond2 = or i1 %or.cond1, %or.cond
-  ret i1 %or.cond2
-}
-
-define i1 @or_icmp_7(i32 signext %type) {
-; RV32-LABEL: or_icmp_7:
-; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    lui a1, 589860
-; RV32-NEXT:    addi a1, a1, 73
-; RV32-NEXT:    srl a1, a1, a0
-; RV32-NEXT:    sltiu a0, a0, 32
-; RV32-NEXT:    and a0, a0, a1
-; RV32-NEXT:    ret
-;
-; RV64-LABEL: or_icmp_7:
-; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    lui a1, 147465
-; RV64-NEXT:    slli a1, a1, 2
-; RV64-NEXT:    addi a1, a1, 73
-; RV64-NEXT:    srl a1, a1, a0
-; RV64-NEXT:    sltiu a0, a0, 64
-; RV64-NEXT:    and a0, a0, a1
-; RV64-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i32 %type, 17
-  %or.cond1 = or i1 %cmp2, %or.cond
-  %cmp3 = icmp eq i32 %type, 3
-  %or.cond2 = or i1 %cmp3, %or.cond1
-  %cmp4 = icmp eq i32 %type, 31
-  %or.cond3 = or i1 %cmp4, %or.cond2
-  %cmp5 = icmp eq i32 %type, 14
-  %or.cond4 = or i1 %cmp5, %or.cond3
-  %cmp6 = icmp eq i32 %type, 28
-  %or.cond5 = or i1 %cmp6, %or.cond4
-  ret i1 %or.cond5
-}
-
-define i1 @or_icmp_gte_64(i32 signext %type) {
-; CHECK-LABEL: or_icmp_gte_64:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    addi a0, a0, -64
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a0, a1, a0
-; CHECK-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 64
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_multiple_uses(i32 signext %type) {
-; CHECK-LABEL: or_icmp_multiple_uses:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    or a0, a1, a0
-; CHECK-NEXT:    xor a0, a1, a0
-; CHECK-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %or.cond1 = xor i1 %cmp, %or.cond
-  ret i1 %or.cond1
-}
-
-
-define i1 @or_icmp_not_eq(i32 signext %type) {
-; CHECK-LABEL: or_icmp_not_eq:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a1, a0, -6
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    snez a0, a0
-; CHECK-NEXT:    or a0, a1, a0
-; CHECK-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp ugt i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_xlen(i32 signext %type) {
-; RV32-LABEL: or_icmp_xlen:
-; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    addi a1, a0, -6
-; RV32-NEXT:    addi a0, a0, -32
-; RV32-NEXT:    seqz a1, a1
-; RV32-NEXT:    seqz a0, a0
-; RV32-NEXT:    or a0, a1, a0
-; RV32-NEXT:    ret
-;
-; RV64-LABEL: or_icmp_xlen:
-; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    li a1, 1
-; RV64-NEXT:    slli a1, a1, 32
-; RV64-NEXT:    addi a1, a1, 64
-; RV64-NEXT:    srl a1, a1, a0
-; RV64-NEXT:    sltiu a0, a0, 64
-; RV64-NEXT:    and a0, a0, a1
-; RV64-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 32
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_i64(i64 signext %type) {
-; RV32-LABEL: or_icmp_i64:
-; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    xori a2, a0, 6
-; RV32-NEXT:    or a3, a0, a1
-; RV32-NEXT:    xori a0, a0, 15
-; RV32-NEXT:    or a2, a2, a1
-; RV32-NEXT:    seqz a3, a3
-; RV32-NEXT:    or a0, a0, a1
-; RV32-NEXT:    seqz a1, a2
-; RV32-NEXT:    or a1, a1, a3
-; RV32-NEXT:    seqz a0, a0
-; RV32-NEXT:    or a0, a0, a1
-; RV32-NEXT:    ret
-;
-; RV64-LABEL: or_icmp_i64:
-; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    lui a1, 8
-; RV64-NEXT:    addi a1, a1, 65
-; RV64-NEXT:    srl a1, a1, a0
-; RV64-NEXT:    sltiu a0, a0, 64
-; RV64-NEXT:    and a0, a0, a1
-; RV64-NEXT:    ret
-entry:
-  %cmp = icmp eq i64 %type, 6
-  %cmp1 = icmp eq i64 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp3 = icmp eq i64 %type, 15
-  %or.cond1 = or i1 %cmp3, %or.cond
-  ret i1 %or.cond1
-}
-
-define i1 @or_icmp_specific(i32 signext %type, i32 signext %type1) {
-; CHECK-LABEL: or_icmp_specific:
-; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    addi a0, a0, -6
-; CHECK-NEXT:    addi a1, a1, -32
-; CHECK-NEXT:    seqz a0, a0
-; CHECK-NEXT:    seqz a1, a1
-; CHECK-NEXT:    or a0, a0, a1
-; CHECK-NEXT:    ret
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type1, 32
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}

>From 3f57238f231361d5b666e90f8a57e3d090ad88ac Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 25 Nov 2025 19:21:05 -0800
Subject: [PATCH 04/17] [InstCombine] Add new tests for flag check optimization

---
 .../test/Transforms/InstCombine/flag_check.ll | 374 ++++++++++++++++++
 1 file changed, 374 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/flag_check.ll

diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
new file mode 100644
index 0000000000000..fc6c4bd71c96e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -0,0 +1,374 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+target datalayout = "n32:64"
+
+define i1 @or_icmp_2(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_2(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_3(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_3(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_7(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_7(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 17
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[TYPE]], 3
+; CHECK-NEXT:    [[OR_COND2:%.*]] = or i1 [[CMP3]], [[OR_COND1]]
+; CHECK-NEXT:    [[CMP4:%.*]] = icmp eq i32 [[TYPE]], 31
+; CHECK-NEXT:    [[OR_COND3:%.*]] = or i1 [[CMP4]], [[OR_COND2]]
+; CHECK-NEXT:    [[CMP5:%.*]] = icmp eq i32 [[TYPE]], 14
+; CHECK-NEXT:    [[OR_COND4:%.*]] = or i1 [[CMP5]], [[OR_COND3]]
+; CHECK-NEXT:    [[CMP6:%.*]] = icmp eq i32 [[TYPE]], 28
+; CHECK-NEXT:    [[OR_COND5:%.*]] = or i1 [[CMP6]], [[OR_COND4]]
+; CHECK-NEXT:    ret i1 [[OR_COND5]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 17
+  %or.cond1 = or i1 %cmp2, %or.cond
+  %cmp3 = icmp eq i32 %type, 3
+  %or.cond2 = or i1 %cmp3, %or.cond1
+  %cmp4 = icmp eq i32 %type, 31
+  %or.cond3 = or i1 %cmp4, %or.cond2
+  %cmp5 = icmp eq i32 %type, 14
+  %or.cond4 = or i1 %cmp5, %or.cond3
+  %cmp6 = icmp eq i32 %type, 28
+  %or.cond5 = or i1 %cmp6, %or.cond4
+  ret i1 %or.cond5
+}
+
+; Cannot optimize since Imm > XLen
+define i1 @or_icmp_gte_64(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_gte_64(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 64
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 64
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize since %type has multiple uses
+define i32 @or_icmp_multiple_uses(i32 signext noundef %type) {
+; CHECK-LABEL: define i32 @or_icmp_multiple_uses(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[CMP_EXT:%.*]] = zext i1 [[CMP1]] to i32
+; CHECK-NEXT:    [[OR_COND_EXT:%.*]] = zext i1 [[OR_COND]] to i32
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i32 [[CMP_EXT]], [[OR_COND_EXT]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp.ext = zext i1 %cmp to i32
+  %or.cond.ext = zext i1 %or.cond to i32
+  %add = add i32 %cmp.ext, %or.cond.ext
+  ret i32 %add
+}
+
+; Cannot optimize since not == comparison
+define i1 @or_icmp_not_eq(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_not_eq(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[TYPE]], -7
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[TMP0]], -5
+; CHECK-NEXT:    ret i1 [[CMP1]]
+;
+entry:
+  %cmp = icmp ugt i32 %type, 6
+  %cmp1 = icmp ult i32 %type, 2
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_i64(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_i64(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+; Cannot optimize since not the same value being compared
+define i1 @or_icmp_specific(i64 signext noundef %type, i64 signext noundef %type1, i64 signext noundef %type2) {
+; CHECK-LABEL: define i1 @or_icmp_specific(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]], i64 noundef signext [[TYPE1:%.*]], i64 noundef signext [[TYPE2:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE1]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type1, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+; Cannot optimize since %type can be undef
+define i1 @or_icmp_undef(i64 signext %type) {
+; CHECK-LABEL: define i1 @or_icmp_undef(
+; CHECK-SAME: i64 signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_expand(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %shr = lshr i64 65, %type
+  %cmp = icmp ult i64 %type, 7
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize bounds check smaller than largest BitMap bit
+define i1 @or_icmp_expand_small_bounds(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_small_bounds(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 3
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %shr = lshr i64 65, %type
+  %cmp = icmp ult i64 %type, 3
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize bounds check larger than XLen
+define i1 @or_icmp_expand_large_bounds(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_large_bounds(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 65
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %shr = lshr i64 65, %type
+  %cmp = icmp ult i64 %type, 65
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_expand_trunc_type_shr(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_trunc_type_shr(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 64
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %type.t = trunc i128 %type to i64
+  %shr = lshr i64 65, %type.t
+  %cmp = icmp ult i128 %type, 64
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i128 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_expand_zext_cmp(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_zext_cmp(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 64
+; CHECK-NEXT:    [[TMP2:%.*]] = trunc i64 [[TMP0]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %type.e = zext i64 %type to i128
+  %shr = lshr i64 65, %type
+  %cmp = icmp ult i128 %type.e, 64
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_i128(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_i128(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i128 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i128 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i128 %type, 6
+  %cmp1 = icmp eq i128 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i128 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_expand_128(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_128(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i128 65, [[TYPE]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 64
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i128 [[SHR]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %shr = lshr i128 65, %type
+  %cmp = icmp ult i128 %type, 64
+  %trunc = trunc i128 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i128 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize BitMap lrger than XLen
+define i1 @or_icmp_expand_large_bitmap(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_large_bitmap(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i128 73786976294838206465, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 64
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i128 [[SHR]] to i1
+; CHECK-NEXT:    [[AND1:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[AND:%.*]] = icmp eq i128 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND1]], [[AND]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %shr = lshr i128 73786976294838206465, %type
+  %cmp = icmp ult i128 %type, 64
+  %trunc = trunc i128 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i128 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}

>From 14c6bad6c17dcb6f7b09c08b2beaecd1282c498f Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 10 Nov 2025 14:23:02 -0800
Subject: [PATCH 05/17] [InstCombine] Lower flag check pattern to use a
 bitmask-shift

The following C-code:

bool or_icmp(int type) {
  return type == 0 || type == 6 || type == 15;
}

Currently lowers to:

define i1 @or_icmp(i32 signext %type) {
entry:
  %cmp = icmp eq i32 %type, 6
  %cmp1 = icmp eq i32 %type, 0
  %or.cond = or i1 %cmp, %cmp1
  %cmp3 = icmp eq i32 %type, 15
  %or.cond1 = or i1 %cmp3, %or.cond
  ret i1 %or.cond1
}

But more optimally lowers to:

define i1 @or_icmp(i32 signext %type) {
entry:
  %srl = lshr i32 32833, %type
  %srl.1 = trunc i32 %srl to i1
  %cmp = icmp ult i32 %type, 64
  %and = select i1 %cmd, i1 %srl.1, i1 false
  ret i1 %and
}
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 87 +++++++++++++++++++
 .../test/Transforms/InstCombine/flag_check.ll | 39 +++++----
 2 files changed, 111 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ba5568b00441b..eed3a021a05f9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3119,6 +3119,89 @@ static Instruction *matchFunnelShift(Instruction &Or, InstCombinerImpl &IC) {
   return nullptr;
 }
 
+static Value *combineOrOfImmCmpToBitExtract(Instruction &Or,
+                                            InstCombiner::BuilderTy &Builder,
+                                            const DataLayout &DL) {
+
+  auto isICmpEqImm = [](Value *N, ConstantInt *&Imm, Value *&X) -> bool {
+    if (X)
+      return match(N, m_OneUse(m_SpecificICmp(ICmpInst::ICMP_EQ, m_Specific(X),
+                                              m_ConstantInt(Imm))));
+
+    return match(N, m_OneUse(m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(X),
+                                            m_ConstantInt(Imm))));
+  };
+
+  // %srl = lshr %bitmap, %X
+  // %icmp = icmp ult %X, %max_value
+  // %trunc = trunc %srl to i1
+  // %sel = select %icmp, %trunc, false
+  auto CreateBitExtractSeq = [&](APInt BitMap, APInt MaxValue,
+                                 Value *X) -> Value * {
+    LLVMContext &Context = Or.getContext();
+
+    // %srl = lshr %bitmap, %X
+    // It is okay for the shift amount to be truncated because
+    // if information is lost then it is garunteed to fail the bounds
+    // check and the shift result will be discarded
+    ConstantInt *BitMapConst = ConstantInt::get(Context, BitMap);
+    Value *ShiftAmt =
+        Builder.CreateZExtOrTrunc(X, BitMapConst->getIntegerType());
+    Value *LShr = Builder.CreateLShr(BitMapConst, ShiftAmt);
+
+    // %icmp = icmp ult %X, %max_value
+    // Use the type that is the larger of 'X' and the bounds integer
+    // so that no information is lost
+    Value *MaxVal = ConstantInt::get(Context, MaxValue);
+    if (MaxVal->getType()->getIntegerBitWidth() >
+        X->getType()->getIntegerBitWidth())
+      X = Builder.CreateZExt(X, MaxVal->getType());
+    else
+      MaxVal = Builder.CreateZExt(MaxVal, X->getType());
+    Value *BoundsCheck = Builder.CreateICmp(ICmpInst::ICMP_ULT, X, MaxVal);
+
+    // %trunc = trunc %srl to i1
+    // Only care about the low bit
+    Value *ShrTrunc = Builder.CreateTrunc(LShr, IntegerType::get(Context, 1));
+
+    // %sel = select %icmp, %trunc, false
+    return Builder.CreateSelect(BoundsCheck, ShrTrunc,
+                                ConstantInt::getFalse(Context));
+  };
+
+  // Our BitMap should be able to fit into a single arch register
+  // otherwise the tranformation won't be profitable
+  unsigned XLen = DL.getLargestLegalIntTypeSizeInBits();
+  auto validImm = [&](APInt APImm) -> bool {
+    auto Imm = APImm.tryZExtValue();
+    return Imm && (*Imm < XLen);
+  };
+
+  // Match (or (icmp eq X, Imm0), (icmp eq X, Imm1))
+  ConstantInt *LHS, *RHS;
+  Value *X = nullptr;
+  if (isICmpEqImm(Or.getOperand(0), LHS, X) &&
+      isICmpEqImm(Or.getOperand(1), RHS, X)) {
+    // The Shr with become poison when shifted by Undef
+    if (!isGuaranteedNotToBeUndefOrPoison(X))
+      return nullptr;
+
+    APInt LHSAP = LHS->getValue();
+    APInt RHSAP = RHS->getValue();
+    if (!validImm(LHSAP) || !validImm(RHSAP))
+      return nullptr;
+    LHSAP = LHSAP.zextOrTrunc(XLen);
+    RHSAP = RHSAP.zextOrTrunc(XLen);
+
+    // Create the BitMap and Bounds check immediates
+    // +1 to bound becuase strictly less than
+    APInt BitMap = (APInt(XLen, 1) << LHSAP) | (APInt(XLen, 1) << RHSAP);
+    APInt Bound = RHSAP.ugt(LHSAP) ? RHSAP : LHSAP;
+    return CreateBitExtractSeq(BitMap, Bound + 1, X);
+  }
+  return nullptr;
+}
+
 /// Attempt to combine or(zext(x),shl(zext(y),bw/2) concat packing patterns.
 static Value *matchOrConcat(Instruction &Or, InstCombiner::BuilderTy &Builder) {
   assert(Or.getOpcode() == Instruction::Or && "bswap requires an 'or'");
@@ -4084,6 +4167,10 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
   if (Instruction *Funnel = matchFunnelShift(I, *this))
     return Funnel;
 
+  if (Value *BitExtract =
+          combineOrOfImmCmpToBitExtract(I, Builder, getDataLayout()))
+    return replaceInstUsesWith(I, BitExtract);
+
   if (Value *Concat = matchOrConcat(I, Builder))
     return replaceInstUsesWith(I, Concat);
 
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index fc6c4bd71c96e..f8b3f53d6f002 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -7,9 +7,11 @@ define i1 @or_icmp_2(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_2(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
 ; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
@@ -23,9 +25,11 @@ define i1 @or_icmp_3(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_3(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
 ; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]
@@ -43,9 +47,11 @@ define i1 @or_icmp_7(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_7(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 17
 ; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
 ; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[TYPE]], 3
@@ -135,9 +141,10 @@ define i1 @or_icmp_i64(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_i64(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP2:%.*]] = trunc i64 [[TMP0]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
 ; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]
@@ -312,9 +319,11 @@ define i1 @or_icmp_i128(i128 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_i128(
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i128 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i128 [[TYPE]], 15
 ; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]

>From 4352ec0e2bd9d5ff004a62b982536d2d1f4e34e9 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 10 Nov 2025 14:23:02 -0800
Subject: [PATCH 06/17] [InstCombine] Expand flag check patterns

The following C-code:

bool or_icmp(int type) {
  return type == 0 || type == 6 || type == 15;
}

Currently lowers to:

define i1 @or_icmp(i32 signext %type) {
entry:
  %cmp = icmp eq i32 %type, 6
  %cmp1 = icmp eq i32 %type, 0
  %or.cond = or i1 %cmp, %cmp1
  %cmp3 = icmp eq i32 %type, 15
  %or.cond1 = or i1 %cmp3, %or.cond
  ret i1 %or.cond1
}

But more optimally lowers to:

define i1 @or_icmp(i32 signext %type) {
entry:
  %srl = lshr i32 32833, %type
  %srl.1 = trunc i32 %srl to i1
  %cmp = icmp ult i32 %type, 64
  %and = select i1 %cmd, i1 %srl.1, i1 false
  ret i1 %and
}
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 38 ++++++++++
 .../test/Transforms/InstCombine/flag_check.ll | 69 +++++++------------
 2 files changed, 61 insertions(+), 46 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index eed3a021a05f9..113dc971bb88d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3199,6 +3199,44 @@ static Value *combineOrOfImmCmpToBitExtract(Instruction &Or,
     APInt Bound = RHSAP.ugt(LHSAP) ? RHSAP : LHSAP;
     return CreateBitExtractSeq(BitMap, Bound + 1, X);
   }
+
+  // Expand an already existing BitMap sequence
+  // Match: (or (%BitMapSeq(X)), (icmp eq X, Imm))
+  ConstantInt *BitMap, *Bound, *CmpImm;
+  Value *Cmp;
+  if (match(&Or, m_OneUse(m_c_Or(m_Value(Cmp),
+                                 m_OneUse(m_Select(
+                                     m_SpecificICmp(ICmpInst::ICMP_ULT,
+                                                    m_ZExtOrSelf(m_Value(X)),
+                                                    m_ConstantInt(Bound)),
+                                     m_OneUse(m_Trunc(m_OneUse(m_Shr(
+                                         m_ConstantInt(BitMap),
+                                         m_ZExtOrTruncOrSelf(m_Deferred(X)))))),
+                                     m_Zero()))))) &&
+      isICmpEqImm(Cmp, CmpImm, X)) {
+    if (!isGuaranteedNotToBeUndefOrPoison(X))
+      return nullptr;
+
+    APInt NewAP = CmpImm->getValue();
+    APInt BitMapAP = BitMap->getValue();
+    APInt BoundAP = Bound->getValue().zextOrTrunc(XLen);
+    // BitMap must fit in native arch register
+    if (!validImm(NewAP) || !DL.fitsInLegalInteger(BitMapAP.getActiveBits()))
+      return nullptr;
+
+    NewAP = NewAP.zextOrTrunc(XLen);
+    BitMapAP = BitMapAP.zextOrTrunc(XLen);
+
+    // Bounding immediate must be greater than the largest bit in the BitMap
+    // and less then XLen
+    if (BoundAP.ult(BitMapAP.getActiveBits()) || BoundAP.ugt(XLen))
+      return nullptr;
+
+    if (NewAP.uge(BoundAP))
+      BoundAP = NewAP + 1;
+    BitMapAP |= (APInt(XLen, 1) << NewAP);
+    return CreateBitExtractSeq(BitMapAP, BoundAP, X);
+  }
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index f8b3f53d6f002..4f4dcf102aee0 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -26,13 +26,11 @@ define i1 @or_icmp_3(i32 signext noundef %type) {
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 32833, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 16
 ; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %cmp = icmp eq i32 %type, 6
@@ -48,21 +46,11 @@ define i1 @or_icmp_7(i32 signext noundef %type) {
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 2416066633, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 32
 ; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 17
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[TYPE]], 3
-; CHECK-NEXT:    [[OR_COND2:%.*]] = or i1 [[CMP3]], [[OR_COND1]]
-; CHECK-NEXT:    [[CMP4:%.*]] = icmp eq i32 [[TYPE]], 31
-; CHECK-NEXT:    [[OR_COND3:%.*]] = or i1 [[CMP4]], [[OR_COND2]]
-; CHECK-NEXT:    [[CMP5:%.*]] = icmp eq i32 [[TYPE]], 14
-; CHECK-NEXT:    [[OR_COND4:%.*]] = or i1 [[CMP5]], [[OR_COND3]]
-; CHECK-NEXT:    [[CMP6:%.*]] = icmp eq i32 [[TYPE]], 28
-; CHECK-NEXT:    [[OR_COND5:%.*]] = or i1 [[CMP6]], [[OR_COND4]]
-; CHECK-NEXT:    ret i1 [[OR_COND5]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %cmp = icmp eq i32 %type, 6
@@ -141,13 +129,11 @@ define i1 @or_icmp_i64(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_i64(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 65, [[TYPE]]
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 32833, [[TYPE]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 16
 ; CHECK-NEXT:    [[TMP2:%.*]] = trunc i64 [[TMP0]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %cmp = icmp eq i64 %type, 6
@@ -204,13 +190,11 @@ define i1 @or_icmp_expand(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 34359738433, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 36
 ; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
 ; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[AND]]
 ;
 entry:
   %shr = lshr i64 65, %type
@@ -273,13 +257,11 @@ define i1 @or_icmp_expand_trunc_type_shr(i128 signext noundef %type) {
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 34359738433, [[TMP0]]
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 64
 ; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %type.t = trunc i128 %type to i64
@@ -296,13 +278,11 @@ define i1 @or_icmp_expand_zext_cmp(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand_zext_cmp(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 34359738433, [[TYPE]]
 ; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 64
 ; CHECK-NEXT:    [[TMP2:%.*]] = trunc i64 [[TMP0]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %type.e = zext i64 %type to i128
@@ -320,13 +300,11 @@ define i1 @or_icmp_i128(i128 signext noundef %type) {
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 7
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 32833, [[TMP0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 16
 ; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i128 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %cmp = icmp eq i128 %type, 6
@@ -341,13 +319,12 @@ define i1 @or_icmp_expand_128(i128 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand_128(
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i128 65, [[TYPE]]
+; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 34359738433, [[TMP0]]
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 64
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i128 [[SHR]] to i1
+; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
 ;
 entry:
   %shr = lshr i128 65, %type

>From 2090e8f6fdacd8f8fbfa45dff24320da8e4c22d9 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Wed, 26 Nov 2025 15:08:02 -0800
Subject: [PATCH 07/17] Remove undef in comment to pass formatter

---
 llvm/test/Transforms/InstCombine/flag_check.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index 4f4dcf102aee0..ccf150c311f8d 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -165,7 +165,7 @@ entry:
   ret i1 %or.cond1
 }
 
-; Cannot optimize since %type can be undef
+; Cannot optimize since %type can be un-def
 define i1 @or_icmp_undef(i64 signext %type) {
 ; CHECK-LABEL: define i1 @or_icmp_undef(
 ; CHECK-SAME: i64 signext [[TYPE:%.*]]) {

>From 5ea6d10d6f59df62463f07b7c42676a74f3a2b9a Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 17:25:07 -0800
Subject: [PATCH 08/17] Revert prior standalone changes, will redo to reuse
 logic from SimplifyCFG

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 125 ------
 .../test/Transforms/InstCombine/flag_check.ll | 360 ------------------
 2 files changed, 485 deletions(-)
 delete mode 100644 llvm/test/Transforms/InstCombine/flag_check.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 113dc971bb88d..ba5568b00441b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3119,127 +3119,6 @@ static Instruction *matchFunnelShift(Instruction &Or, InstCombinerImpl &IC) {
   return nullptr;
 }
 
-static Value *combineOrOfImmCmpToBitExtract(Instruction &Or,
-                                            InstCombiner::BuilderTy &Builder,
-                                            const DataLayout &DL) {
-
-  auto isICmpEqImm = [](Value *N, ConstantInt *&Imm, Value *&X) -> bool {
-    if (X)
-      return match(N, m_OneUse(m_SpecificICmp(ICmpInst::ICMP_EQ, m_Specific(X),
-                                              m_ConstantInt(Imm))));
-
-    return match(N, m_OneUse(m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(X),
-                                            m_ConstantInt(Imm))));
-  };
-
-  // %srl = lshr %bitmap, %X
-  // %icmp = icmp ult %X, %max_value
-  // %trunc = trunc %srl to i1
-  // %sel = select %icmp, %trunc, false
-  auto CreateBitExtractSeq = [&](APInt BitMap, APInt MaxValue,
-                                 Value *X) -> Value * {
-    LLVMContext &Context = Or.getContext();
-
-    // %srl = lshr %bitmap, %X
-    // It is okay for the shift amount to be truncated because
-    // if information is lost then it is garunteed to fail the bounds
-    // check and the shift result will be discarded
-    ConstantInt *BitMapConst = ConstantInt::get(Context, BitMap);
-    Value *ShiftAmt =
-        Builder.CreateZExtOrTrunc(X, BitMapConst->getIntegerType());
-    Value *LShr = Builder.CreateLShr(BitMapConst, ShiftAmt);
-
-    // %icmp = icmp ult %X, %max_value
-    // Use the type that is the larger of 'X' and the bounds integer
-    // so that no information is lost
-    Value *MaxVal = ConstantInt::get(Context, MaxValue);
-    if (MaxVal->getType()->getIntegerBitWidth() >
-        X->getType()->getIntegerBitWidth())
-      X = Builder.CreateZExt(X, MaxVal->getType());
-    else
-      MaxVal = Builder.CreateZExt(MaxVal, X->getType());
-    Value *BoundsCheck = Builder.CreateICmp(ICmpInst::ICMP_ULT, X, MaxVal);
-
-    // %trunc = trunc %srl to i1
-    // Only care about the low bit
-    Value *ShrTrunc = Builder.CreateTrunc(LShr, IntegerType::get(Context, 1));
-
-    // %sel = select %icmp, %trunc, false
-    return Builder.CreateSelect(BoundsCheck, ShrTrunc,
-                                ConstantInt::getFalse(Context));
-  };
-
-  // Our BitMap should be able to fit into a single arch register
-  // otherwise the tranformation won't be profitable
-  unsigned XLen = DL.getLargestLegalIntTypeSizeInBits();
-  auto validImm = [&](APInt APImm) -> bool {
-    auto Imm = APImm.tryZExtValue();
-    return Imm && (*Imm < XLen);
-  };
-
-  // Match (or (icmp eq X, Imm0), (icmp eq X, Imm1))
-  ConstantInt *LHS, *RHS;
-  Value *X = nullptr;
-  if (isICmpEqImm(Or.getOperand(0), LHS, X) &&
-      isICmpEqImm(Or.getOperand(1), RHS, X)) {
-    // The Shr with become poison when shifted by Undef
-    if (!isGuaranteedNotToBeUndefOrPoison(X))
-      return nullptr;
-
-    APInt LHSAP = LHS->getValue();
-    APInt RHSAP = RHS->getValue();
-    if (!validImm(LHSAP) || !validImm(RHSAP))
-      return nullptr;
-    LHSAP = LHSAP.zextOrTrunc(XLen);
-    RHSAP = RHSAP.zextOrTrunc(XLen);
-
-    // Create the BitMap and Bounds check immediates
-    // +1 to bound becuase strictly less than
-    APInt BitMap = (APInt(XLen, 1) << LHSAP) | (APInt(XLen, 1) << RHSAP);
-    APInt Bound = RHSAP.ugt(LHSAP) ? RHSAP : LHSAP;
-    return CreateBitExtractSeq(BitMap, Bound + 1, X);
-  }
-
-  // Expand an already existing BitMap sequence
-  // Match: (or (%BitMapSeq(X)), (icmp eq X, Imm))
-  ConstantInt *BitMap, *Bound, *CmpImm;
-  Value *Cmp;
-  if (match(&Or, m_OneUse(m_c_Or(m_Value(Cmp),
-                                 m_OneUse(m_Select(
-                                     m_SpecificICmp(ICmpInst::ICMP_ULT,
-                                                    m_ZExtOrSelf(m_Value(X)),
-                                                    m_ConstantInt(Bound)),
-                                     m_OneUse(m_Trunc(m_OneUse(m_Shr(
-                                         m_ConstantInt(BitMap),
-                                         m_ZExtOrTruncOrSelf(m_Deferred(X)))))),
-                                     m_Zero()))))) &&
-      isICmpEqImm(Cmp, CmpImm, X)) {
-    if (!isGuaranteedNotToBeUndefOrPoison(X))
-      return nullptr;
-
-    APInt NewAP = CmpImm->getValue();
-    APInt BitMapAP = BitMap->getValue();
-    APInt BoundAP = Bound->getValue().zextOrTrunc(XLen);
-    // BitMap must fit in native arch register
-    if (!validImm(NewAP) || !DL.fitsInLegalInteger(BitMapAP.getActiveBits()))
-      return nullptr;
-
-    NewAP = NewAP.zextOrTrunc(XLen);
-    BitMapAP = BitMapAP.zextOrTrunc(XLen);
-
-    // Bounding immediate must be greater than the largest bit in the BitMap
-    // and less then XLen
-    if (BoundAP.ult(BitMapAP.getActiveBits()) || BoundAP.ugt(XLen))
-      return nullptr;
-
-    if (NewAP.uge(BoundAP))
-      BoundAP = NewAP + 1;
-    BitMapAP |= (APInt(XLen, 1) << NewAP);
-    return CreateBitExtractSeq(BitMapAP, BoundAP, X);
-  }
-  return nullptr;
-}
-
 /// Attempt to combine or(zext(x),shl(zext(y),bw/2) concat packing patterns.
 static Value *matchOrConcat(Instruction &Or, InstCombiner::BuilderTy &Builder) {
   assert(Or.getOpcode() == Instruction::Or && "bswap requires an 'or'");
@@ -4205,10 +4084,6 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
   if (Instruction *Funnel = matchFunnelShift(I, *this))
     return Funnel;
 
-  if (Value *BitExtract =
-          combineOrOfImmCmpToBitExtract(I, Builder, getDataLayout()))
-    return replaceInstUsesWith(I, BitExtract);
-
   if (Value *Concat = matchOrConcat(I, Builder))
     return replaceInstUsesWith(I, Concat);
 
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
deleted file mode 100644
index ccf150c311f8d..0000000000000
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ /dev/null
@@ -1,360 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt < %s -passes=instcombine -S | FileCheck %s
-
-target datalayout = "n32:64"
-
-define i1 @or_icmp_2(i32 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_2(
-; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 65, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 7
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_3(i32 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_3(
-; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 32833, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 16
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i32 %type, 15
-  %or.cond1 = or i1 %cmp2, %or.cond
-  ret i1 %or.cond1
-}
-
-define i1 @or_icmp_7(i32 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_7(
-; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 2416066633, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i32 [[TYPE]], 32
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i32 %type, 17
-  %or.cond1 = or i1 %cmp2, %or.cond
-  %cmp3 = icmp eq i32 %type, 3
-  %or.cond2 = or i1 %cmp3, %or.cond1
-  %cmp4 = icmp eq i32 %type, 31
-  %or.cond3 = or i1 %cmp4, %or.cond2
-  %cmp5 = icmp eq i32 %type, 14
-  %or.cond4 = or i1 %cmp5, %or.cond3
-  %cmp6 = icmp eq i32 %type, 28
-  %or.cond5 = or i1 %cmp6, %or.cond4
-  ret i1 %or.cond5
-}
-
-; Cannot optimize since Imm > XLen
-define i1 @or_icmp_gte_64(i32 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_gte_64(
-; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 64
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 64
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-; Cannot optimize since %type has multiple uses
-define i32 @or_icmp_multiple_uses(i32 signext noundef %type) {
-; CHECK-LABEL: define i32 @or_icmp_multiple_uses(
-; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
-; CHECK-NEXT:    [[CMP_EXT:%.*]] = zext i1 [[CMP1]] to i32
-; CHECK-NEXT:    [[OR_COND_EXT:%.*]] = zext i1 [[OR_COND]] to i32
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i32 [[CMP_EXT]], [[OR_COND_EXT]]
-; CHECK-NEXT:    ret i32 [[ADD]]
-;
-entry:
-  %cmp = icmp eq i32 %type, 6
-  %cmp1 = icmp eq i32 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp.ext = zext i1 %cmp to i32
-  %or.cond.ext = zext i1 %or.cond to i32
-  %add = add i32 %cmp.ext, %or.cond.ext
-  ret i32 %add
-}
-
-; Cannot optimize since not == comparison
-define i1 @or_icmp_not_eq(i32 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_not_eq(
-; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[TYPE]], -7
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[TMP0]], -5
-; CHECK-NEXT:    ret i1 [[CMP1]]
-;
-entry:
-  %cmp = icmp ugt i32 %type, 6
-  %cmp1 = icmp ult i32 %type, 2
-  %or.cond = or i1 %cmp, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_i64(i64 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_i64(
-; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 32833, [[TYPE]]
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 16
-; CHECK-NEXT:    [[TMP2:%.*]] = trunc i64 [[TMP0]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %cmp = icmp eq i64 %type, 6
-  %cmp1 = icmp eq i64 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i64 %type, 15
-  %or.cond1 = or i1 %cmp2, %or.cond
-  ret i1 %or.cond1
-}
-
-; Cannot optimize since not the same value being compared
-define i1 @or_icmp_specific(i64 signext noundef %type, i64 signext noundef %type1, i64 signext noundef %type2) {
-; CHECK-LABEL: define i1 @or_icmp_specific(
-; CHECK-SAME: i64 noundef signext [[TYPE:%.*]], i64 noundef signext [[TYPE1:%.*]], i64 noundef signext [[TYPE2:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE1]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
-;
-entry:
-  %cmp = icmp eq i64 %type, 6
-  %cmp1 = icmp eq i64 %type1, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i64 %type, 15
-  %or.cond1 = or i1 %cmp2, %or.cond
-  ret i1 %or.cond1
-}
-
-; Cannot optimize since %type can be un-def
-define i1 @or_icmp_undef(i64 signext %type) {
-; CHECK-LABEL: define i1 @or_icmp_undef(
-; CHECK-SAME: i64 signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
-;
-entry:
-  %cmp = icmp eq i64 %type, 6
-  %cmp1 = icmp eq i64 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i64 %type, 15
-  %or.cond1 = or i1 %cmp2, %or.cond
-  ret i1 %or.cond1
-}
-
-define i1 @or_icmp_expand(i64 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand(
-; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 34359738433, [[TYPE]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 36
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
-; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    ret i1 [[AND]]
-;
-entry:
-  %shr = lshr i64 65, %type
-  %cmp = icmp ult i64 %type, 7
-  %trunc = trunc i64 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i64 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}
-
-; Cannot optimize bounds check smaller than largest BitMap bit
-define i1 @or_icmp_expand_small_bounds(i64 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand_small_bounds(
-; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 3
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
-; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %shr = lshr i64 65, %type
-  %cmp = icmp ult i64 %type, 3
-  %trunc = trunc i64 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i64 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}
-
-; Cannot optimize bounds check larger than XLen
-define i1 @or_icmp_expand_large_bounds(i64 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand_large_bounds(
-; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 65
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
-; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %shr = lshr i64 65, %type
-  %cmp = icmp ult i64 %type, 65
-  %trunc = trunc i64 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i64 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_expand_trunc_type_shr(i128 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand_trunc_type_shr(
-; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 34359738433, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 64
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %type.t = trunc i128 %type to i64
-  %shr = lshr i64 65, %type.t
-  %cmp = icmp ult i128 %type, 64
-  %trunc = trunc i64 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i128 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_expand_zext_cmp(i64 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand_zext_cmp(
-; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 34359738433, [[TYPE]]
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TYPE]], 64
-; CHECK-NEXT:    [[TMP2:%.*]] = trunc i64 [[TMP0]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %type.e = zext i64 %type to i128
-  %shr = lshr i64 65, %type
-  %cmp = icmp ult i128 %type.e, 64
-  %trunc = trunc i64 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i64 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}
-
-define i1 @or_icmp_i128(i128 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_i128(
-; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 32833, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 16
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %cmp = icmp eq i128 %type, 6
-  %cmp1 = icmp eq i128 %type, 0
-  %or.cond = or i1 %cmp, %cmp1
-  %cmp2 = icmp eq i128 %type, 15
-  %or.cond1 = or i1 %cmp2, %or.cond
-  ret i1 %or.cond1
-}
-
-define i1 @or_icmp_expand_128(i128 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand_128(
-; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 34359738433, [[TMP0]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i128 [[TYPE]], 64
-; CHECK-NEXT:    [[TMP3:%.*]] = trunc i64 [[TMP1]] to i1
-; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[TMP2]], i1 [[TMP3]], i1 false
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %shr = lshr i128 65, %type
-  %cmp = icmp ult i128 %type, 64
-  %trunc = trunc i128 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i128 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}
-
-; Cannot optimize BitMap lrger than XLen
-define i1 @or_icmp_expand_large_bitmap(i128 signext noundef %type) {
-; CHECK-LABEL: define i1 @or_icmp_expand_large_bitmap(
-; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i128 73786976294838206465, [[TYPE]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 64
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i128 [[SHR]] to i1
-; CHECK-NEXT:    [[AND1:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[AND:%.*]] = icmp eq i128 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND1]], [[AND]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
-;
-entry:
-  %shr = lshr i128 73786976294838206465, %type
-  %cmp = icmp ult i128 %type, 64
-  %trunc = trunc i128 %shr to i1
-  %and = select i1 %cmp, i1 %trunc, i1 false
-  %cmp1 = icmp eq i128 %type, 35
-  %or.cond = or i1 %and, %cmp1
-  ret i1 %or.cond
-}

>From 7b05ace25acce22a304115853fb566bcb9eef7da Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 25 Nov 2025 19:21:05 -0800
Subject: [PATCH 09/17] [InstCombine] Add new tests for flag check optimization

---
 .../test/Transforms/InstCombine/flag_check.ll | 526 ++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/flag_check.ll

diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
new file mode 100644
index 0000000000000..761078cdf3339
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -0,0 +1,526 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+target datalayout = "n32:64"
+
+define i1 @or_icmp_2(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_2(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_3(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_3(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+; Don't currently handle a single non-equal
+define i1 @or_icmp_extra(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_extra(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[TYPE]], -7
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[TMP0]], -6
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %cmp = icmp ugt i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @and_icmp_all_neq(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @and_icmp_all_neq(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[TYPE]], 0
+; CHECK-NEXT:    [[AND_COND:%.*]] = and i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[TYPE]], 15
+; CHECK-NEXT:    [[AND_COND1:%.*]] = and i1 [[CMP2]], [[AND_COND]]
+; CHECK-NEXT:    ret i1 [[AND_COND1]]
+;
+entry:
+  %cmp = icmp ne i32 %type, 6
+  %cmp1 = icmp ne i32 %type, 0
+  %and.cond = and i1 %cmp, %cmp1
+  %cmp2 = icmp ne i32 %type, 15
+  %and.cond1 = and i1 %cmp2, %and.cond
+  ret i1 %and.cond1
+}
+
+define i1 @and_icmp_ugt(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @and_icmp_ugt(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[TYPE]], 22
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ugt i32 [[TYPE]], 11
+; CHECK-NEXT:    [[AND_COND:%.*]] = and i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[TYPE]], 15
+; CHECK-NEXT:    [[AND_COND1:%.*]] = and i1 [[CMP2]], [[AND_COND]]
+; CHECK-NEXT:    ret i1 [[AND_COND1]]
+;
+entry:
+  %cmp = icmp ne i32 %type, 22
+  %cmp1 = icmp ugt i32 %type, 11
+  %and.cond = and i1 %cmp, %cmp1
+  %cmp2 = icmp ne i32 %type, 15
+  %and.cond1 = and i1 %cmp2, %and.cond
+  ret i1 %and.cond1
+}
+
+define i1 @or_icmp_ltu(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_ltu(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[TYPE]], 4
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp ult i32 %type, 4
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_7(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_7(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 17
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[TYPE]], 3
+; CHECK-NEXT:    [[OR_COND2:%.*]] = or i1 [[CMP3]], [[OR_COND1]]
+; CHECK-NEXT:    [[CMP4:%.*]] = icmp eq i32 [[TYPE]], 31
+; CHECK-NEXT:    [[OR_COND3:%.*]] = or i1 [[CMP4]], [[OR_COND2]]
+; CHECK-NEXT:    [[CMP5:%.*]] = icmp eq i32 [[TYPE]], 14
+; CHECK-NEXT:    [[OR_COND4:%.*]] = or i1 [[CMP5]], [[OR_COND3]]
+; CHECK-NEXT:    [[CMP6:%.*]] = icmp eq i32 [[TYPE]], 28
+; CHECK-NEXT:    [[OR_COND5:%.*]] = or i1 [[CMP6]], [[OR_COND4]]
+; CHECK-NEXT:    ret i1 [[OR_COND5]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %type, 17
+  %or.cond1 = or i1 %cmp2, %or.cond
+  %cmp3 = icmp eq i32 %type, 3
+  %or.cond2 = or i1 %cmp3, %or.cond1
+  %cmp4 = icmp eq i32 %type, 31
+  %or.cond3 = or i1 %cmp4, %or.cond2
+  %cmp5 = icmp eq i32 %type, 14
+  %or.cond4 = or i1 %cmp5, %or.cond3
+  %cmp6 = icmp eq i32 %type, 28
+  %or.cond5 = or i1 %cmp6, %or.cond4
+  ret i1 %or.cond5
+}
+
+; Cannot optimize since Imm > XLen
+define i1 @or_icmp_gte_64(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_gte_64(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 64
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 64
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize since %type has multiple uses
+define i32 @or_icmp_multiple_uses(i32 signext noundef %type) {
+; CHECK-LABEL: define i32 @or_icmp_multiple_uses(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    [[CMP_EXT:%.*]] = zext i1 [[CMP1]] to i32
+; CHECK-NEXT:    [[OR_COND_EXT:%.*]] = zext i1 [[OR_COND]] to i32
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i32 [[CMP_EXT]], [[OR_COND_EXT]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %cmp = icmp eq i32 %type, 6
+  %cmp1 = icmp eq i32 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp.ext = zext i1 %cmp to i32
+  %or.cond.ext = zext i1 %or.cond to i32
+  %add = add i32 %cmp.ext, %or.cond.ext
+  ret i32 %add
+}
+
+; Cannot optimize since not == comparison
+define i1 @or_icmp_not_eq(i32 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_not_eq(
+; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[TYPE]], -7
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[TMP0]], -5
+; CHECK-NEXT:    ret i1 [[CMP1]]
+;
+entry:
+  %cmp = icmp ugt i32 %type, 6
+  %cmp1 = icmp ult i32 %type, 2
+  %or.cond = or i1 %cmp, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_i64(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_i64(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+; Cannot optimize since not the same value being compared
+define i1 @or_icmp_specific(i64 signext noundef %type, i64 signext noundef %type1, i64 signext noundef %type2) {
+; CHECK-LABEL: define i1 @or_icmp_specific(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]], i64 noundef signext [[TYPE1:%.*]], i64 noundef signext [[TYPE2:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE1]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type1, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+; Cannot optimize since %type can be undef
+define i1 @or_icmp_undef(i64 signext %type) {
+; CHECK-LABEL: define i1 @or_icmp_undef(
+; CHECK-SAME: i64 signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i64 %type, 6
+  %cmp1 = icmp eq i64 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i64 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_expand(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i64 [[TYPE]] to i7
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i7 27, [[TYPE_T]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i7 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %type.t = trunc i64 %type to i7
+  %shr = lshr i7 27, %type.t
+  %cmp = icmp ult i64 %type, 7
+  %trunc = trunc i7 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @and_icmp_expand(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @and_icmp_expand(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i64 [[TYPE]] to i7
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i7 27, [[TYPE_T]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i7 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i64 [[TYPE]], 35
+; CHECK-NEXT:    [[AND_COND:%.*]] = xor i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[AND_COND]]
+;
+entry:
+  %type.t = trunc i64 %type to i7
+  %shr = lshr i7 27, %type.t
+  %cmp = icmp ult i64 %type, 7
+  %trunc = trunc i7 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp ne i64 %type, 35
+  %not = xor i1 %and, true
+  %and.cond = and i1 %not, %cmp1
+  ret i1 %and.cond
+}
+
+; Can't mix existing IsEq sequence and new !IsEq sequence
+define i1 @and_icmp_no_expand(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @and_icmp_no_expand(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i64 [[TYPE]] to i7
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i7 27, [[TYPE_T]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 7
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i7 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+entry:
+  %type.t = trunc i64 %type to i7
+  %shr = lshr i7 27, %type.t
+  %cmp = icmp ult i64 %type, 7
+  %trunc = trunc i7 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp ne i64 %type, 35
+  %and.cond = and i1 %and, %cmp1
+  ret i1 %and.cond
+}
+
+; Can't mix existing !IsEq sequence and new IsEq sequence
+define i1 @or_icmp_no_expand(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_no_expand(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    ret i1 [[CMP1]]
+;
+entry:
+  %type.t = trunc i64 %type to i7
+  %shr = lshr i7 27, %type.t
+  %cmp = icmp ult i64 %type, 7
+  %trunc = trunc i7 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %not = xor i1 %and, true
+  %or.cond = and i1 %not, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize bounds check smaller than largest BitMap bit
+define i1 @or_icmp_expand_small_bounds(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_small_bounds(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i64 [[TYPE]] to i7
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i7 27, [[TYPE_T]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 3
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i7 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %type.t = trunc i64 %type to i7
+  %shr = lshr i7 27, %type.t
+  %cmp = icmp ult i64 %type, 3
+  %trunc = trunc i7 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize bounds check larger than XLen
+define i1 @or_icmp_expand_large_bounds(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_large_bounds(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 65
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %shr = lshr i64 65, %type
+  %cmp = icmp ult i64 %type, 65
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_expand_trunc_type_shr(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_trunc_type_shr(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i128 [[TYPE]] to i64
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE_T]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 64
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %type.t = trunc i128 %type to i64
+  %shr = lshr i64 65, %type.t
+  %cmp = icmp ult i128 %type, 64
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i128 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_expand_zext_cmp(i64 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_zext_cmp(
+; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 64
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %type.e = zext i64 %type to i128
+  %shr = lshr i64 65, %type
+  %cmp = icmp ult i128 %type.e, 64
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i64 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+define i1 @or_icmp_i128(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_i128(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i128 [[TYPE]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i128 [[TYPE]], 15
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %cmp = icmp eq i128 %type, 6
+  %cmp1 = icmp eq i128 %type, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i128 %type, 15
+  %or.cond1 = or i1 %cmp2, %or.cond
+  ret i1 %or.cond1
+}
+
+define i1 @or_icmp_expand_128(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_128(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i128 [[TYPE]] to i64
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE_T]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 64
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
+;
+entry:
+  %type.t = trunc i128 %type to i64
+  %shr = lshr i64 65, %type.t
+  %cmp = icmp ult i128 %type, 64
+  %trunc = trunc i64 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i128 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}
+
+; Cannot optimize BitMap larger than XLen
+define i1 @or_icmp_expand_large_bitmap(i128 signext noundef %type) {
+; CHECK-LABEL: define i1 @or_icmp_expand_large_bitmap(
+; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i128 73786976294838206465, [[TYPE]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 128
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i128 [[SHR]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+entry:
+  %shr = lshr i128 73786976294838206465, %type
+  %cmp = icmp ult i128 %type, 128
+  %trunc = trunc i128 %shr to i1
+  %and = select i1 %cmp, i1 %trunc, i1 false
+  %cmp1 = icmp eq i128 %type, 35
+  %or.cond = or i1 %and, %cmp1
+  ret i1 %or.cond
+}

>From 6516c13cbcc14789b18e71357d777666887b8c1f Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 2 Dec 2025 14:53:46 -0800
Subject: [PATCH 10/17] [NFC] Move ConstantComparesGatherer to ValueTracking.h

---
 llvm/include/llvm/Analysis/ValueTracking.h |  49 ++++
 llvm/lib/Analysis/ValueTracking.cpp        | 270 ++++++++++++++++++
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp  | 316 ---------------------
 3 files changed, 319 insertions(+), 316 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index b730a36488780..ae99f153bc1fa 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -1034,6 +1034,55 @@ collectPossibleValues(const Value *V,
                       SmallPtrSetImpl<const Constant *> &Constants,
                       unsigned MaxCount, bool AllowUndefOrPoison = true);
 
+/// Extract ConstantInt from value, looking through IntToPtr
+/// and PointerNullValue. Return NULL if value is not a constant int.
+ConstantInt *getConstantInt(Value *V, const DataLayout &DL);
+
+/// Given a chain of or (||) or and (&&) comparison of a value against a
+/// constant, this will try to recover the information required for a switch
+/// structure.
+/// It will depth-first traverse the chain of comparison, seeking for patterns
+/// like %a == 12 or %a < 4 and combine them to produce a set of integer
+/// representing the different cases for the switch.
+/// Note that if the chain is composed of '||' it will build the set of elements
+/// that matches the comparisons (i.e. any of this value validate the chain)
+/// while for a chain of '&&' it will build the set elements that make the test
+/// fail.
+struct ConstantComparesGatherer {
+  const DataLayout &DL;
+
+  /// Value found for the switch comparison
+  Value *CompValue = nullptr;
+
+  /// Extra clause to be checked before the switch
+  Value *Extra = nullptr;
+
+  /// Set of integers to match in switch
+  SmallVector<ConstantInt *, 8> Vals;
+
+  /// Number of comparisons matched in the and/or chain
+  unsigned UsedICmps = 0;
+
+  /// If the elements in Vals matches the comparisons
+  bool IsEq = false;
+
+  // Used to check if the first matched CompValue shall be the Extra check.
+  bool IgnoreFirstMatch = false;
+  bool MultipleMatches = false;
+
+  /// Construct and compute the result for the comparison instruction Cond
+  ConstantComparesGatherer(Instruction *Cond, const DataLayout &DL);
+
+  ConstantComparesGatherer(const ConstantComparesGatherer &) = delete;
+  ConstantComparesGatherer &
+  operator=(const ConstantComparesGatherer &) = delete;
+
+private:
+  bool setValueOnce(Value *NewVal);
+  bool matchInstruction(Instruction *I, bool isEQ);
+  void gather(Value *V);
+};
+
 } // end namespace llvm
 
 #endif // LLVM_ANALYSIS_VALUETRACKING_H
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index dbceb8e557849..1ec29ea64361c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10550,3 +10550,273 @@ bool llvm::collectPossibleValues(const Value *V,
   }
   return true;
 }
+
+ConstantInt *llvm::getConstantInt(Value *V, const DataLayout &DL) {
+  // Normal constant int.
+  ConstantInt *CI = dyn_cast<ConstantInt>(V);
+  if (CI || !isa<Constant>(V) || !V->getType()->isPointerTy())
+    return CI;
+
+  // It is not safe to look through inttoptr or ptrtoint when using unstable
+  // pointer types.
+  if (DL.hasUnstableRepresentation(V->getType()))
+    return nullptr;
+
+  // This is some kind of pointer constant. Turn it into a pointer-sized
+  // ConstantInt if possible.
+  IntegerType *IntPtrTy = cast<IntegerType>(DL.getIntPtrType(V->getType()));
+
+  // Null pointer means 0, see SelectionDAGBuilder::getValue(const Value*).
+  if (isa<ConstantPointerNull>(V))
+    return ConstantInt::get(IntPtrTy, 0);
+
+  // IntToPtr const int, we can look through this if the semantics of
+  // inttoptr for this address space are a simple (truncating) bitcast.
+  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(V))
+    if (CE->getOpcode() == Instruction::IntToPtr)
+      if (ConstantInt *CI = dyn_cast<ConstantInt>(CE->getOperand(0))) {
+        // The constant is very likely to have the right type already.
+        if (CI->getType() == IntPtrTy)
+          return CI;
+        else
+          return cast<ConstantInt>(
+              ConstantFoldIntegerCast(CI, IntPtrTy, /*isSigned=*/false, DL));
+      }
+  return nullptr;
+}
+
+/// Construct and compute the result for the comparison instruction Cond
+ConstantComparesGatherer::ConstantComparesGatherer(Instruction *Cond,
+                                                   const DataLayout &DL)
+    : DL(DL) {
+  gather(Cond);
+  if (CompValue || !MultipleMatches)
+    return;
+  Extra = nullptr;
+  Vals.clear();
+  UsedICmps = 0;
+  IgnoreFirstMatch = true;
+  gather(Cond);
+}
+
+/// Try to set the current value used for the comparison, it succeeds only if
+/// it wasn't set before or if the new value is the same as the old one
+bool ConstantComparesGatherer::setValueOnce(Value *NewVal) {
+  if (IgnoreFirstMatch) {
+    IgnoreFirstMatch = false;
+    return false;
+  }
+  if (CompValue && CompValue != NewVal) {
+    MultipleMatches = true;
+    return false;
+  }
+  CompValue = NewVal;
+  return true;
+}
+
+/// Try to match Instruction "I" as a comparison against a constant and
+/// populates the array Vals with the set of values that match (or do not
+/// match depending on isEQ).
+/// Return false on failure. On success, the Value the comparison matched
+/// against is placed in CompValue.
+/// If CompValue is already set, the function is expected to fail if a match
+/// is found but the value compared to is different.
+bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ) {
+  if (match(I, m_Not(m_Instruction(I))))
+    isEQ = !isEQ;
+
+  Value *Val;
+  if (match(I, m_NUWTrunc(m_Value(Val)))) {
+    // If we already have a value for the switch, it has to match!
+    if (!setValueOnce(Val))
+      return false;
+    UsedICmps++;
+    Vals.push_back(ConstantInt::get(cast<IntegerType>(Val->getType()), isEQ));
+    return true;
+  }
+  // If this is an icmp against a constant, handle this as one of the cases.
+  ICmpInst *ICI;
+  ConstantInt *C;
+  if (!((ICI = dyn_cast<ICmpInst>(I)) &&
+        (C = getConstantInt(I->getOperand(1), DL)))) {
+    return false;
+  }
+
+  Value *RHSVal;
+  const APInt *RHSC;
+
+  // Pattern match a special case
+  // (x & ~2^z) == y --> x == y || x == y|2^z
+  // This undoes a transformation done by instcombine to fuse 2 compares.
+  if (ICI->getPredicate() == (isEQ ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE)) {
+    // It's a little bit hard to see why the following transformations are
+    // correct. Here is a CVC3 program to verify them for 64-bit values:
+
+    /*
+      ONE  : BITVECTOR(64) = BVZEROEXTEND(0bin1, 63);
+      x    : BITVECTOR(64);
+      y    : BITVECTOR(64);
+      z    : BITVECTOR(64);
+      mask : BITVECTOR(64) = BVSHL(ONE, z);
+      QUERY( (y & ~mask = y) =>
+      ((x & ~mask = y) <=> (x = y OR x = (y |  mask)))
+      );
+      QUERY( (y |  mask = y) =>
+      ((x |  mask = y) <=> (x = y OR x = (y & ~mask)))
+      );
+    */
+
+    // Please note that each pattern must be a dual implication (<--> or
+    // iff). One directional implication can create spurious matches. If the
+    // implication is only one-way, an unsatisfiable condition on the left
+    // side can imply a satisfiable condition on the right side. Dual
+    // implication ensures that satisfiable conditions are transformed to
+    // other satisfiable conditions and unsatisfiable conditions are
+    // transformed to other unsatisfiable conditions.
+
+    // Here is a concrete example of a unsatisfiable condition on the left
+    // implying a satisfiable condition on the right:
+    //
+    // mask = (1 << z)
+    // (x & ~mask) == y  --> (x == y || x == (y | mask))
+    //
+    // Substituting y = 3, z = 0 yields:
+    // (x & -2) == 3 --> (x == 3 || x == 2)
+
+    // Pattern match a special case:
+    /*
+      QUERY( (y & ~mask = y) =>
+      ((x & ~mask = y) <=> (x = y OR x = (y |  mask)))
+      );
+    */
+    if (match(ICI->getOperand(0), m_And(m_Value(RHSVal), m_APInt(RHSC)))) {
+      APInt Mask = ~*RHSC;
+      if (Mask.isPowerOf2() && (C->getValue() & ~Mask) == C->getValue()) {
+        // If we already have a value for the switch, it has to match!
+        if (!setValueOnce(RHSVal))
+          return false;
+
+        Vals.push_back(C);
+        Vals.push_back(ConstantInt::get(C->getContext(), C->getValue() | Mask));
+        UsedICmps++;
+        return true;
+      }
+    }
+
+    // Pattern match a special case:
+    /*
+      QUERY( (y |  mask = y) =>
+      ((x |  mask = y) <=> (x = y OR x = (y & ~mask)))
+      );
+    */
+    if (match(ICI->getOperand(0), m_Or(m_Value(RHSVal), m_APInt(RHSC)))) {
+      APInt Mask = *RHSC;
+      if (Mask.isPowerOf2() && (C->getValue() | Mask) == C->getValue()) {
+        // If we already have a value for the switch, it has to match!
+        if (!setValueOnce(RHSVal))
+          return false;
+
+        Vals.push_back(C);
+        Vals.push_back(
+            ConstantInt::get(C->getContext(), C->getValue() & ~Mask));
+        UsedICmps++;
+        return true;
+      }
+    }
+
+    // If we already have a value for the switch, it has to match!
+    if (!setValueOnce(ICI->getOperand(0)))
+      return false;
+
+    UsedICmps++;
+    Vals.push_back(C);
+    return true;
+  }
+
+  // If we have "x ult 3", for example, then we can add 0,1,2 to the set.
+  ConstantRange Span =
+      ConstantRange::makeExactICmpRegion(ICI->getPredicate(), C->getValue());
+
+  // Shift the range if the compare is fed by an add. This is the range
+  // compare idiom as emitted by instcombine.
+  Value *CandidateVal = I->getOperand(0);
+  if (match(I->getOperand(0), m_Add(m_Value(RHSVal), m_APInt(RHSC)))) {
+    Span = Span.subtract(*RHSC);
+    CandidateVal = RHSVal;
+  }
+
+  // If this is an and/!= check, then we are looking to build the set of
+  // value that *don't* pass the and chain. I.e. to turn "x ugt 2" into
+  // x != 0 && x != 1.
+  if (!isEQ)
+    Span = Span.inverse();
+
+  // If there are a ton of values, we don't want to make a ginormous switch.
+  if (Span.isSizeLargerThan(8) || Span.isEmptySet()) {
+    return false;
+  }
+
+  // If we already have a value for the switch, it has to match!
+  if (!setValueOnce(CandidateVal))
+    return false;
+
+  // Add all values from the range to the set
+  APInt Tmp = Span.getLower();
+  do
+    Vals.push_back(ConstantInt::get(I->getContext(), Tmp));
+  while (++Tmp != Span.getUpper());
+
+  UsedICmps++;
+  return true;
+}
+
+/// Given a potentially 'or'd or 'and'd together collection of icmp
+/// eq/ne/lt/gt instructions that compare a value against a constant, extract
+/// the value being compared, and stick the list constants into the Vals
+/// vector.
+/// One "Extra" case is allowed to differ from the other.
+void ConstantComparesGatherer::gather(Value *V) {
+  Value *Op0, *Op1;
+  if (match(V, m_LogicalOr(m_Value(Op0), m_Value(Op1))))
+    IsEq = true;
+  else if (match(V, m_LogicalAnd(m_Value(Op0), m_Value(Op1))))
+    IsEq = false;
+  else
+    return;
+  // Keep a stack (SmallVector for efficiency) for depth-first traversal
+  SmallVector<Value *, 8> DFT{Op0, Op1};
+  SmallPtrSet<Value *, 8> Visited{V, Op0, Op1};
+
+  while (!DFT.empty()) {
+    V = DFT.pop_back_val();
+
+    if (Instruction *I = dyn_cast<Instruction>(V)) {
+      // If it is a || (or && depending on isEQ), process the operands.
+      if (IsEq ? match(I, m_LogicalOr(m_Value(Op0), m_Value(Op1)))
+               : match(I, m_LogicalAnd(m_Value(Op0), m_Value(Op1)))) {
+        if (Visited.insert(Op1).second)
+          DFT.push_back(Op1);
+        if (Visited.insert(Op0).second)
+          DFT.push_back(Op0);
+
+        continue;
+      }
+
+      // Try to match the current instruction
+      if (matchInstruction(I, IsEq))
+        // Match succeed, continue the loop
+        continue;
+    }
+
+    // One element of the sequence of || (or &&) could not be match as a
+    // comparison against the same value as the others.
+    // We allow only one "Extra" case to be checked before the switch
+    if (!Extra) {
+      Extra = V;
+      continue;
+    }
+    // Failed to parse a proper sequence, abort now
+    CompValue = nullptr;
+    break;
+  }
+}
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index ed2a5c292fa54..2d7a00a146d9c 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -527,322 +527,6 @@ static bool dominatesMergePoint(
   return true;
 }
 
-/// Extract ConstantInt from value, looking through IntToPtr
-/// and PointerNullValue. Return NULL if value is not a constant int.
-static ConstantInt *getConstantInt(Value *V, const DataLayout &DL) {
-  // Normal constant int.
-  ConstantInt *CI = dyn_cast<ConstantInt>(V);
-  if (CI || !isa<Constant>(V) || !V->getType()->isPointerTy())
-    return CI;
-
-  // It is not safe to look through inttoptr or ptrtoint when using unstable
-  // pointer types.
-  if (DL.hasUnstableRepresentation(V->getType()))
-    return nullptr;
-
-  // This is some kind of pointer constant. Turn it into a pointer-sized
-  // ConstantInt if possible.
-  IntegerType *IntPtrTy = cast<IntegerType>(DL.getIntPtrType(V->getType()));
-
-  // Null pointer means 0, see SelectionDAGBuilder::getValue(const Value*).
-  if (isa<ConstantPointerNull>(V))
-    return ConstantInt::get(IntPtrTy, 0);
-
-  // IntToPtr const int, we can look through this if the semantics of
-  // inttoptr for this address space are a simple (truncating) bitcast.
-  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(V))
-    if (CE->getOpcode() == Instruction::IntToPtr)
-      if (ConstantInt *CI = dyn_cast<ConstantInt>(CE->getOperand(0))) {
-        // The constant is very likely to have the right type already.
-        if (CI->getType() == IntPtrTy)
-          return CI;
-        else
-          return cast<ConstantInt>(
-              ConstantFoldIntegerCast(CI, IntPtrTy, /*isSigned=*/false, DL));
-      }
-  return nullptr;
-}
-
-namespace {
-
-/// Given a chain of or (||) or and (&&) comparison of a value against a
-/// constant, this will try to recover the information required for a switch
-/// structure.
-/// It will depth-first traverse the chain of comparison, seeking for patterns
-/// like %a == 12 or %a < 4 and combine them to produce a set of integer
-/// representing the different cases for the switch.
-/// Note that if the chain is composed of '||' it will build the set of elements
-/// that matches the comparisons (i.e. any of this value validate the chain)
-/// while for a chain of '&&' it will build the set elements that make the test
-/// fail.
-struct ConstantComparesGatherer {
-  const DataLayout &DL;
-
-  /// Value found for the switch comparison
-  Value *CompValue = nullptr;
-
-  /// Extra clause to be checked before the switch
-  Value *Extra = nullptr;
-
-  /// Set of integers to match in switch
-  SmallVector<ConstantInt *, 8> Vals;
-
-  /// Number of comparisons matched in the and/or chain
-  unsigned UsedICmps = 0;
-
-  /// If the elements in Vals matches the comparisons
-  bool IsEq = false;
-
-  // Used to check if the first matched CompValue shall be the Extra check.
-  bool IgnoreFirstMatch = false;
-  bool MultipleMatches = false;
-
-  /// Construct and compute the result for the comparison instruction Cond
-  ConstantComparesGatherer(Instruction *Cond, const DataLayout &DL) : DL(DL) {
-    gather(Cond);
-    if (CompValue || !MultipleMatches)
-      return;
-    Extra = nullptr;
-    Vals.clear();
-    UsedICmps = 0;
-    IgnoreFirstMatch = true;
-    gather(Cond);
-  }
-
-  ConstantComparesGatherer(const ConstantComparesGatherer &) = delete;
-  ConstantComparesGatherer &
-  operator=(const ConstantComparesGatherer &) = delete;
-
-private:
-  /// Try to set the current value used for the comparison, it succeeds only if
-  /// it wasn't set before or if the new value is the same as the old one
-  bool setValueOnce(Value *NewVal) {
-    if (IgnoreFirstMatch) {
-      IgnoreFirstMatch = false;
-      return false;
-    }
-    if (CompValue && CompValue != NewVal) {
-      MultipleMatches = true;
-      return false;
-    }
-    CompValue = NewVal;
-    return true;
-  }
-
-  /// Try to match Instruction "I" as a comparison against a constant and
-  /// populates the array Vals with the set of values that match (or do not
-  /// match depending on isEQ).
-  /// Return false on failure. On success, the Value the comparison matched
-  /// against is placed in CompValue.
-  /// If CompValue is already set, the function is expected to fail if a match
-  /// is found but the value compared to is different.
-  bool matchInstruction(Instruction *I, bool isEQ) {
-    if (match(I, m_Not(m_Instruction(I))))
-      isEQ = !isEQ;
-
-    Value *Val;
-    if (match(I, m_NUWTrunc(m_Value(Val)))) {
-      // If we already have a value for the switch, it has to match!
-      if (!setValueOnce(Val))
-        return false;
-      UsedICmps++;
-      Vals.push_back(ConstantInt::get(cast<IntegerType>(Val->getType()), isEQ));
-      return true;
-    }
-    // If this is an icmp against a constant, handle this as one of the cases.
-    ICmpInst *ICI;
-    ConstantInt *C;
-    if (!((ICI = dyn_cast<ICmpInst>(I)) &&
-          (C = getConstantInt(I->getOperand(1), DL)))) {
-      return false;
-    }
-
-    Value *RHSVal;
-    const APInt *RHSC;
-
-    // Pattern match a special case
-    // (x & ~2^z) == y --> x == y || x == y|2^z
-    // This undoes a transformation done by instcombine to fuse 2 compares.
-    if (ICI->getPredicate() == (isEQ ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE)) {
-      // It's a little bit hard to see why the following transformations are
-      // correct. Here is a CVC3 program to verify them for 64-bit values:
-
-      /*
-         ONE  : BITVECTOR(64) = BVZEROEXTEND(0bin1, 63);
-         x    : BITVECTOR(64);
-         y    : BITVECTOR(64);
-         z    : BITVECTOR(64);
-         mask : BITVECTOR(64) = BVSHL(ONE, z);
-         QUERY( (y & ~mask = y) =>
-                ((x & ~mask = y) <=> (x = y OR x = (y |  mask)))
-         );
-         QUERY( (y |  mask = y) =>
-                ((x |  mask = y) <=> (x = y OR x = (y & ~mask)))
-         );
-      */
-
-      // Please note that each pattern must be a dual implication (<--> or
-      // iff). One directional implication can create spurious matches. If the
-      // implication is only one-way, an unsatisfiable condition on the left
-      // side can imply a satisfiable condition on the right side. Dual
-      // implication ensures that satisfiable conditions are transformed to
-      // other satisfiable conditions and unsatisfiable conditions are
-      // transformed to other unsatisfiable conditions.
-
-      // Here is a concrete example of a unsatisfiable condition on the left
-      // implying a satisfiable condition on the right:
-      //
-      // mask = (1 << z)
-      // (x & ~mask) == y  --> (x == y || x == (y | mask))
-      //
-      // Substituting y = 3, z = 0 yields:
-      // (x & -2) == 3 --> (x == 3 || x == 2)
-
-      // Pattern match a special case:
-      /*
-        QUERY( (y & ~mask = y) =>
-               ((x & ~mask = y) <=> (x = y OR x = (y |  mask)))
-        );
-      */
-      if (match(ICI->getOperand(0),
-                m_And(m_Value(RHSVal), m_APInt(RHSC)))) {
-        APInt Mask = ~*RHSC;
-        if (Mask.isPowerOf2() && (C->getValue() & ~Mask) == C->getValue()) {
-          // If we already have a value for the switch, it has to match!
-          if (!setValueOnce(RHSVal))
-            return false;
-
-          Vals.push_back(C);
-          Vals.push_back(
-              ConstantInt::get(C->getContext(),
-                               C->getValue() | Mask));
-          UsedICmps++;
-          return true;
-        }
-      }
-
-      // Pattern match a special case:
-      /*
-        QUERY( (y |  mask = y) =>
-               ((x |  mask = y) <=> (x = y OR x = (y & ~mask)))
-        );
-      */
-      if (match(ICI->getOperand(0),
-                m_Or(m_Value(RHSVal), m_APInt(RHSC)))) {
-        APInt Mask = *RHSC;
-        if (Mask.isPowerOf2() && (C->getValue() | Mask) == C->getValue()) {
-          // If we already have a value for the switch, it has to match!
-          if (!setValueOnce(RHSVal))
-            return false;
-
-          Vals.push_back(C);
-          Vals.push_back(ConstantInt::get(C->getContext(),
-                                          C->getValue() & ~Mask));
-          UsedICmps++;
-          return true;
-        }
-      }
-
-      // If we already have a value for the switch, it has to match!
-      if (!setValueOnce(ICI->getOperand(0)))
-        return false;
-
-      UsedICmps++;
-      Vals.push_back(C);
-      return true;
-    }
-
-    // If we have "x ult 3", for example, then we can add 0,1,2 to the set.
-    ConstantRange Span =
-        ConstantRange::makeExactICmpRegion(ICI->getPredicate(), C->getValue());
-
-    // Shift the range if the compare is fed by an add. This is the range
-    // compare idiom as emitted by instcombine.
-    Value *CandidateVal = I->getOperand(0);
-    if (match(I->getOperand(0), m_Add(m_Value(RHSVal), m_APInt(RHSC)))) {
-      Span = Span.subtract(*RHSC);
-      CandidateVal = RHSVal;
-    }
-
-    // If this is an and/!= check, then we are looking to build the set of
-    // value that *don't* pass the and chain. I.e. to turn "x ugt 2" into
-    // x != 0 && x != 1.
-    if (!isEQ)
-      Span = Span.inverse();
-
-    // If there are a ton of values, we don't want to make a ginormous switch.
-    if (Span.isSizeLargerThan(8) || Span.isEmptySet()) {
-      return false;
-    }
-
-    // If we already have a value for the switch, it has to match!
-    if (!setValueOnce(CandidateVal))
-      return false;
-
-    // Add all values from the range to the set
-    APInt Tmp = Span.getLower();
-    do
-      Vals.push_back(ConstantInt::get(I->getContext(), Tmp));
-    while (++Tmp != Span.getUpper());
-
-    UsedICmps++;
-    return true;
-  }
-
-  /// Given a potentially 'or'd or 'and'd together collection of icmp
-  /// eq/ne/lt/gt instructions that compare a value against a constant, extract
-  /// the value being compared, and stick the list constants into the Vals
-  /// vector.
-  /// One "Extra" case is allowed to differ from the other.
-  void gather(Value *V) {
-    Value *Op0, *Op1;
-    if (match(V, m_LogicalOr(m_Value(Op0), m_Value(Op1))))
-      IsEq = true;
-    else if (match(V, m_LogicalAnd(m_Value(Op0), m_Value(Op1))))
-      IsEq = false;
-    else
-      return;
-    // Keep a stack (SmallVector for efficiency) for depth-first traversal
-    SmallVector<Value *, 8> DFT{Op0, Op1};
-    SmallPtrSet<Value *, 8> Visited{V, Op0, Op1};
-
-    while (!DFT.empty()) {
-      V = DFT.pop_back_val();
-
-      if (Instruction *I = dyn_cast<Instruction>(V)) {
-        // If it is a || (or && depending on isEQ), process the operands.
-        if (IsEq ? match(I, m_LogicalOr(m_Value(Op0), m_Value(Op1)))
-                 : match(I, m_LogicalAnd(m_Value(Op0), m_Value(Op1)))) {
-          if (Visited.insert(Op1).second)
-            DFT.push_back(Op1);
-          if (Visited.insert(Op0).second)
-            DFT.push_back(Op0);
-
-          continue;
-        }
-
-        // Try to match the current instruction
-        if (matchInstruction(I, IsEq))
-          // Match succeed, continue the loop
-          continue;
-      }
-
-      // One element of the sequence of || (or &&) could not be match as a
-      // comparison against the same value as the others.
-      // We allow only one "Extra" case to be checked before the switch
-      if (!Extra) {
-        Extra = V;
-        continue;
-      }
-      // Failed to parse a proper sequence, abort now
-      CompValue = nullptr;
-      break;
-    }
-  }
-};
-
-} // end anonymous namespace
-
 static void eraseTerminatorAndDCECond(Instruction *TI,
                                       MemorySSAUpdater *MSSAU = nullptr) {
   Instruction *Cond = nullptr;

>From 98c0e7e4449b18c370d783e308aaf9329f7154f4 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 01:12:20 -0800
Subject: [PATCH 11/17] [NFC] Move the logic for generating a bit-extraction
 sequence to ValueTracking::ConstantComparesGatherer

---
 llvm/include/llvm/Analysis/ValueTracking.h |  5 +++++
 llvm/lib/Analysis/ValueTracking.cpp        | 26 ++++++++++++++++++++++
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp  | 22 ++----------------
 3 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index ae99f153bc1fa..867be2ed56fcd 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -19,6 +19,7 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/FMF.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/Intrinsics.h"
@@ -1077,6 +1078,10 @@ struct ConstantComparesGatherer {
   ConstantComparesGatherer &
   operator=(const ConstantComparesGatherer &) = delete;
 
+  static Value *createBitMapSeq(ConstantInt *BitMap, Value *Index,
+                                IRBuilder<> &Builder,
+                                IntegerType *BitMapElementTy);
+
 private:
   bool setValueOnce(Value *NewVal);
   bool matchInstruction(Instruction *I, bool isEQ);
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1ec29ea64361c..f6df0a733732f 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -51,6 +51,7 @@
 #include "llvm/IR/GlobalAlias.h"
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
@@ -10820,3 +10821,28 @@ void ConstantComparesGatherer::gather(Value *V) {
     break;
   }
 }
+
+Value *ConstantComparesGatherer::createBitMapSeq(ConstantInt *BitMap,
+                                                 Value *Index,
+                                                 IRBuilder<> &Builder,
+                                                 IntegerType *BitMapElementTy) {
+  // Type of the bitmap (e.g. i59).
+  IntegerType *MapTy = BitMap->getIntegerType();
+
+  // Cast Index to the same type as the bitmap.
+  // Note: The Index is <= the number of elements in the table, so
+  // truncating it to the width of the bitmask is safe.
+  Value *ShiftAmt = Builder.CreateZExtOrTrunc(Index, MapTy, "switch.cast");
+
+  // Multiply the shift amount by the element width. NUW/NSW can always be
+  // set, because wouldFitInRegister guarantees Index * ShiftAmt is in
+  // BitMap's bit width.
+  ShiftAmt = Builder.CreateMul(
+      ShiftAmt, ConstantInt::get(MapTy, BitMapElementTy->getBitWidth()),
+      "switch.shiftamt", /*HasNUW =*/true, /*HasNSW =*/true);
+
+  // Shift down.
+  Value *DownShifted = Builder.CreateLShr(BitMap, ShiftAmt, "switch.downshift");
+  // Mask off.
+  return Builder.CreateTrunc(DownShifted, BitMapElementTy, "switch.masked");
+}
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 2d7a00a146d9c..3df7cd4e180ef 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6660,26 +6660,8 @@ Value *SwitchReplacement::replaceSwitch(Value *Index, IRBuilder<> &Builder,
   }
   case BitMapKind: {
     ++NumBitMaps;
-    // Type of the bitmap (e.g. i59).
-    IntegerType *MapTy = BitMap->getIntegerType();
-
-    // Cast Index to the same type as the bitmap.
-    // Note: The Index is <= the number of elements in the table, so
-    // truncating it to the width of the bitmask is safe.
-    Value *ShiftAmt = Builder.CreateZExtOrTrunc(Index, MapTy, "switch.cast");
-
-    // Multiply the shift amount by the element width. NUW/NSW can always be
-    // set, because wouldFitInRegister guarantees Index * ShiftAmt is in
-    // BitMap's bit width.
-    ShiftAmt = Builder.CreateMul(
-        ShiftAmt, ConstantInt::get(MapTy, BitMapElementTy->getBitWidth()),
-        "switch.shiftamt",/*HasNUW =*/true,/*HasNSW =*/true);
-
-    // Shift down.
-    Value *DownShifted =
-        Builder.CreateLShr(BitMap, ShiftAmt, "switch.downshift");
-    // Mask off.
-    return Builder.CreateTrunc(DownShifted, BitMapElementTy, "switch.masked");
+    return ConstantComparesGatherer::createBitMapSeq(BitMap, Index, Builder,
+                                                     BitMapElementTy);
   }
   case LookupTableKind: {
     ++NumLookupTables;

>From fe363e93c6c1e075cbb757d257c40f6219b8088a Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 01:15:16 -0800
Subject: [PATCH 12/17] [NFC] Update
 ConstantComparesGatherer::createBitMapSeq() to take an IRBuilderBase*

Different Builder object may be passed depending on that pass
---
 llvm/include/llvm/Analysis/ValueTracking.h |  2 +-
 llvm/lib/Analysis/ValueTracking.cpp        | 11 ++++++-----
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp  |  2 +-
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 867be2ed56fcd..03408809a2f62 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -1079,7 +1079,7 @@ struct ConstantComparesGatherer {
   operator=(const ConstantComparesGatherer &) = delete;
 
   static Value *createBitMapSeq(ConstantInt *BitMap, Value *Index,
-                                IRBuilder<> &Builder,
+                                IRBuilderBase *Builder,
                                 IntegerType *BitMapElementTy);
 
 private:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f6df0a733732f..774b6e5046e2d 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10824,7 +10824,7 @@ void ConstantComparesGatherer::gather(Value *V) {
 
 Value *ConstantComparesGatherer::createBitMapSeq(ConstantInt *BitMap,
                                                  Value *Index,
-                                                 IRBuilder<> &Builder,
+                                                 IRBuilderBase *Builder,
                                                  IntegerType *BitMapElementTy) {
   // Type of the bitmap (e.g. i59).
   IntegerType *MapTy = BitMap->getIntegerType();
@@ -10832,17 +10832,18 @@ Value *ConstantComparesGatherer::createBitMapSeq(ConstantInt *BitMap,
   // Cast Index to the same type as the bitmap.
   // Note: The Index is <= the number of elements in the table, so
   // truncating it to the width of the bitmask is safe.
-  Value *ShiftAmt = Builder.CreateZExtOrTrunc(Index, MapTy, "switch.cast");
+  Value *ShiftAmt = Builder->CreateZExtOrTrunc(Index, MapTy, "switch.cast");
 
   // Multiply the shift amount by the element width. NUW/NSW can always be
   // set, because wouldFitInRegister guarantees Index * ShiftAmt is in
   // BitMap's bit width.
-  ShiftAmt = Builder.CreateMul(
+  ShiftAmt = Builder->CreateMul(
       ShiftAmt, ConstantInt::get(MapTy, BitMapElementTy->getBitWidth()),
       "switch.shiftamt", /*HasNUW =*/true, /*HasNSW =*/true);
 
   // Shift down.
-  Value *DownShifted = Builder.CreateLShr(BitMap, ShiftAmt, "switch.downshift");
+  Value *DownShifted =
+      Builder->CreateLShr(BitMap, ShiftAmt, "switch.downshift");
   // Mask off.
-  return Builder.CreateTrunc(DownShifted, BitMapElementTy, "switch.masked");
+  return Builder->CreateTrunc(DownShifted, BitMapElementTy, "switch.masked");
 }
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 3df7cd4e180ef..e6bf6fb2c9141 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6660,7 +6660,7 @@ Value *SwitchReplacement::replaceSwitch(Value *Index, IRBuilder<> &Builder,
   }
   case BitMapKind: {
     ++NumBitMaps;
-    return ConstantComparesGatherer::createBitMapSeq(BitMap, Index, Builder,
+    return ConstantComparesGatherer::createBitMapSeq(BitMap, Index, &Builder,
                                                      BitMapElementTy);
   }
   case LookupTableKind: {

>From 77c3a4b9e1749fd03887c76920ce6c6ef409568d Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 01:17:28 -0800
Subject: [PATCH 13/17] [NFC] Specify whether ConstantComparesGatherer is being
 used in InstCombine pass

If part of InstCombine pass, will want to require all matches have a single use.
---
 llvm/include/llvm/Analysis/ValueTracking.h |  7 +++---
 llvm/lib/Analysis/ValueTracking.cpp        | 27 ++++++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 03408809a2f62..6713c5a4e4247 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -1072,7 +1072,8 @@ struct ConstantComparesGatherer {
   bool MultipleMatches = false;
 
   /// Construct and compute the result for the comparison instruction Cond
-  ConstantComparesGatherer(Instruction *Cond, const DataLayout &DL);
+  ConstantComparesGatherer(Instruction *Cond, const DataLayout &DL,
+                           const bool InstCombine = false);
 
   ConstantComparesGatherer(const ConstantComparesGatherer &) = delete;
   ConstantComparesGatherer &
@@ -1084,8 +1085,8 @@ struct ConstantComparesGatherer {
 
 private:
   bool setValueOnce(Value *NewVal);
-  bool matchInstruction(Instruction *I, bool isEQ);
-  void gather(Value *V);
+  bool matchInstruction(Instruction *I, bool isEQ, const bool InstCombine);
+  void gather(Value *V, const bool InstCombine);
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 774b6e5046e2d..7a4034f10b2b8 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10588,16 +10588,17 @@ ConstantInt *llvm::getConstantInt(Value *V, const DataLayout &DL) {
 
 /// Construct and compute the result for the comparison instruction Cond
 ConstantComparesGatherer::ConstantComparesGatherer(Instruction *Cond,
-                                                   const DataLayout &DL)
+                                                   const DataLayout &DL,
+                                                   const bool InstCombine)
     : DL(DL) {
-  gather(Cond);
+  gather(Cond, InstCombine);
   if (CompValue || !MultipleMatches)
     return;
   Extra = nullptr;
   Vals.clear();
   UsedICmps = 0;
   IgnoreFirstMatch = true;
-  gather(Cond);
+  gather(Cond, InstCombine);
 }
 
 /// Try to set the current value used for the comparison, it succeeds only if
@@ -10622,9 +10623,13 @@ bool ConstantComparesGatherer::setValueOnce(Value *NewVal) {
 /// against is placed in CompValue.
 /// If CompValue is already set, the function is expected to fail if a match
 /// is found but the value compared to is different.
-bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ) {
-  if (match(I, m_Not(m_Instruction(I))))
+bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ,
+                                                const bool InstCombine) {
+  if (match(I, m_Not(m_Instruction(I)))) {
     isEQ = !isEQ;
+    if (InstCombine && !I->hasOneUse())
+      return false;
+  }
 
   Value *Val;
   if (match(I, m_NUWTrunc(m_Value(Val)))) {
@@ -10744,6 +10749,8 @@ bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ) {
   if (match(I->getOperand(0), m_Add(m_Value(RHSVal), m_APInt(RHSC)))) {
     Span = Span.subtract(*RHSC);
     CandidateVal = RHSVal;
+    if (InstCombine && !CandidateVal->hasOneUse())
+      return false;
   }
 
   // If this is an and/!= check, then we are looking to build the set of
@@ -10776,7 +10783,9 @@ bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ) {
 /// the value being compared, and stick the list constants into the Vals
 /// vector.
 /// One "Extra" case is allowed to differ from the other.
-void ConstantComparesGatherer::gather(Value *V) {
+void ConstantComparesGatherer::gather(Value *V, const bool InstCombine) {
+  if (InstCombine && !V->hasOneUse())
+    return;
   Value *Op0, *Op1;
   if (match(V, m_LogicalOr(m_Value(Op0), m_Value(Op1))))
     IsEq = true;
@@ -10790,6 +10799,10 @@ void ConstantComparesGatherer::gather(Value *V) {
 
   while (!DFT.empty()) {
     V = DFT.pop_back_val();
+    if (InstCombine && !V->hasOneUse()) {
+      CompValue = nullptr;
+      return;
+    }
 
     if (Instruction *I = dyn_cast<Instruction>(V)) {
       // If it is a || (or && depending on isEQ), process the operands.
@@ -10804,7 +10817,7 @@ void ConstantComparesGatherer::gather(Value *V) {
       }
 
       // Try to match the current instruction
-      if (matchInstruction(I, IsEq))
+      if (matchInstruction(I, IsEq, InstCombine))
         // Match succeed, continue the loop
         continue;
     }

>From 9b88a1203582d291b2ef7298a33f116be6e86ade Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 01:19:00 -0800
Subject: [PATCH 14/17] [InstCombine] Convert series of And-ICmp.ne to bit
 extraction

The following:
define i1 @or_icmp(i32 signext %type) {
entry:
  %cmp = icmp ne i32 %type, 6
  %cmp1 = icmp ne i32 %type, 0
  %and.cond = and i1 %cmp, %cmp1
  %cmp2 = icmp ne i32 %type, 15
  %and.cond1 = and i1 %cmp2, %and.cond
  ret i1 %and.cond1
}

Can more optimally lower to:
define i1 @or_icmp(i32 signext %type) {
entry:
 %trunc = trunc i32 %type to i16
 %lshr = lshr i16 -32703, %trunc
 %mask = trunc i16 %lshr to i1
 %bounds = icmp ult i32 %type, 16
 %and = select i1 %bounds, i1 %mask, i1 false
 %not = xor i1 %and, true
 ret i1 %not
}

and_icmp_all_neq: https://alive2.llvm.org/ce/z/DQdDec
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 54 +++++++++++++++++++
 .../test/Transforms/InstCombine/flag_check.ll | 11 ++--
 2 files changed, 60 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ba5568b00441b..ce9471f4670b0 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2410,6 +2410,57 @@ Value *InstCombinerImpl::reassociateBooleanAndOr(Value *LHS, Value *X, Value *Y,
   return nullptr;
 }
 
+static Value *combineAndOrOfImmCmpToBitExtract(Instruction &Or,
+                                               InstCombiner::BuilderTy &Builder,
+                                               const DataLayout &DL) {
+  ConstantComparesGatherer ConstantCompare(&Or, DL, /*OneUse=*/true);
+  // Unpack the result
+  SmallVectorImpl<ConstantInt *> &Values = ConstantCompare.Vals;
+  Value *Index = ConstantCompare.CompValue;
+
+  // TODO: Handle ConstantCompare.Extra case
+  if (!Index || !isGuaranteedNotToBeUndefOrPoison(Index) ||
+      ConstantCompare.UsedICmps < 3 || ConstantCompare.Extra)
+    return nullptr;
+
+  unsigned MaxRegWidth = DL.getLargestLegalIntTypeSizeInBits();
+  unsigned MaxVal = 0;
+  // TODO: Handle case where some values are too large for map but some are not.
+  for (auto *CI : Values) {
+    unsigned Val = CI->getValue().getLimitedValue();
+    if (Val >= MaxRegWidth)
+      return nullptr;
+    if (Val > MaxVal)
+      MaxVal = Val;
+  }
+  LLVMContext &Context = Or.getContext();
+  APInt BitMapAP(MaxVal + 1, 0);
+  for (auto *CI : Values) {
+    unsigned Val = CI->getValue().getLimitedValue();
+    BitMapAP.setBit(Val);
+  }
+  ConstantInt *BitMap = ConstantInt::get(Context, BitMapAP);
+  Value *Result = ConstantComparesGatherer::createBitMapSeq(
+      BitMap, Index, &Builder, /*BitMapElementTy=*/Type::getInt1Ty(Context));
+
+  // If the maximum value in the bitmap is larger than can be stored
+  // in Index, don't have to worry about overflow on the shift
+  unsigned IndexBits = dyn_cast<IntegerType>(Index->getType())->getBitWidth();
+  if (MaxVal >= (1u << IndexBits)) {
+    Value *MaxValue = ConstantInt::get(Context, APInt(IndexBits, MaxVal + 1));
+    // %icmp = icmp ult %Index, %max_value
+    Value *BoundsCheck =
+        Builder.CreateICmp(ICmpInst::ICMP_ULT, Index, MaxValue);
+
+    Result = Builder.CreateSelect(BoundsCheck, Result,
+                                  ConstantInt::getFalse(Context));
+  }
+
+  if (!ConstantCompare.IsEq)
+    Result = Builder.CreateNot(Result);
+  return Result;
+}
+
 // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
 // here. We should standardize that construct where it is needed or choose some
 // other way to ensure that commutated variants of patterns are not missed.
@@ -2445,6 +2496,9 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   if (Value *V = foldUsingDistributiveLaws(I))
     return replaceInstUsesWith(I, V);
 
+  if (Value *V = combineAndOrOfImmCmpToBitExtract(I, Builder, DL))
+    return replaceInstUsesWith(I, V);
+
   if (Instruction *R = foldBinOpShiftWithShift(I))
     return R;
 
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index 761078cdf3339..e0a1143210abc 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -61,11 +61,12 @@ define i1 @and_icmp_all_neq(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @and_icmp_all_neq(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[TYPE]], 0
-; CHECK-NEXT:    [[AND_COND:%.*]] = and i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[TYPE]], 15
-; CHECK-NEXT:    [[AND_COND1:%.*]] = and i1 [[CMP2]], [[AND_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[TYPE]] to i16
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -32703, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 16
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
+; CHECK-NEXT:    [[AND_COND1:%.*]] = xor i1 [[TMP1]], true
 ; CHECK-NEXT:    ret i1 [[AND_COND1]]
 ;
 entry:

>From a7acc4d92df315c1169c6be79f37b0b026ca4093 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 01:19:53 -0800
Subject: [PATCH 15/17] [InstCombine] Allow ConstantComparesGatherer to match
 `ult` comparisons with no limit

If converting to switch statement want to limit the number of cases, but don't have that
concern in InstCombine since we know we are converting to a bitmap.

and_icmp_ugt: https://alive2.llvm.org/ce/z/LE7_kz
---
 llvm/lib/Analysis/ValueTracking.cpp            |  7 ++++++-
 llvm/test/Transforms/InstCombine/flag_check.ll | 11 ++++++-----
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 7a4034f10b2b8..0e9352bce1141 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10760,7 +10760,12 @@ bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ,
     Span = Span.inverse();
 
   // If there are a ton of values, we don't want to make a ginormous switch.
-  if (Span.isSizeLargerThan(8) || Span.isEmptySet()) {
+  // In the InstCombine case, we know this will be convered to bitmask so
+  // there is no added cost of having more values. Limit to the max register
+  // size since that's the largest BitMap we can handle anyways
+  if (Span.isSizeLargerThan(InstCombine ? DL.getLargestLegalIntTypeSizeInBits()
+                                        : 8) ||
+      Span.isEmptySet()) {
     return false;
   }
 
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index e0a1143210abc..b74f78280f44a 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -82,11 +82,12 @@ define i1 @and_icmp_ugt(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @and_icmp_ugt(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[TYPE]], 22
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ugt i32 [[TYPE]], 11
-; CHECK-NEXT:    [[AND_COND:%.*]] = and i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[TYPE]], 15
-; CHECK-NEXT:    [[AND_COND1:%.*]] = and i1 [[CMP2]], [[AND_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[TYPE]] to i23
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i23 -4157441, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i23 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 23
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
+; CHECK-NEXT:    [[AND_COND1:%.*]] = xor i1 [[TMP1]], true
 ; CHECK-NEXT:    ret i1 [[AND_COND1]]
 ;
 entry:

>From c200d703876f5b8a9429dcd279839f044b0603fc Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 01:21:46 -0800
Subject: [PATCH 16/17] [InstCombine] Convert series of Or-ICmp.eq to bit
 extraction

The following:
define i1 @or_icmp(i32 signext %type) {
entry:
  %cmp = icmp eq i32 %type, 6
  %cmp1 = icmp eq i32 %type, 0
  %or.cond = or i1 %cmp, %cmp1
  %cmp2 = icmp eq i32 %type, 15
  %or.cond1 = or i1 %cmp2, %or.cond
  ret i1 %or.cond1
}

Can more optimally lower to:
define i1 @or_icmp(i32 signext %type) {
entry:
  %0 = trunc i32 %type to i16
  %1 = lshr i16 -32703, %0
  %2 = icmp ult i32 %type, 16
  %3 = trunc i16 %1 to i1
  %or.cond1 = select i1 %2, i1 %3, i1 false
  ret i1 %or.cond1
}

or_icmp_ltu: https://alive2.llvm.org/ce/z/tcNHDo

or_icmp_3: https://alive2.llvm.org/ce/z/oA-aNe

or_icmp_i64: https://alive2.llvm.org/ce/z/Lnxq4C

or_icmp_i128: https://alive2.llvm.org/ce/z/2cf6fH
---
 .../InstCombine/InstCombineAndOrXor.cpp       |  3 ++
 .../test/Transforms/InstCombine/flag_check.ll | 50 +++++++++----------
 2 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ce9471f4670b0..8e56cef7e61b8 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4104,6 +4104,9 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
   if (Instruction *X = foldIntegerPackFromVector(I, Builder, DL))
     return X;
 
+  if (Value *V = combineAndOrOfImmCmpToBitExtract(I, Builder, DL))
+    return replaceInstUsesWith(I, V);
+
   // (A & B) | (C & D) -> A ^ D where A == ~C && B == ~D
   // (A & B) | (C & D) -> A ^ C where A == ~D && B == ~C
   if (Value *V = foldOrOfInversions(I, Builder))
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index b74f78280f44a..6ae4fd64af5e4 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -23,11 +23,11 @@ define i1 @or_icmp_3(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_3(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[TYPE]] to i16
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -32703, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 16
+; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]
 ;
 entry:
@@ -103,11 +103,11 @@ define i1 @or_icmp_ltu(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_ltu(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[TYPE]], 4
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[TYPE]] to i16
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -32689, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 16
+; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]
 ;
 entry:
@@ -123,11 +123,11 @@ define i1 @or_icmp_7(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_7(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TYPE]], 17
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[TYPE]] to i18
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i18 -131007, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i18 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 18
+; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
 ; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[TYPE]], 3
 ; CHECK-NEXT:    [[OR_COND2:%.*]] = or i1 [[CMP3]], [[OR_COND1]]
 ; CHECK-NEXT:    [[CMP4:%.*]] = icmp eq i32 [[TYPE]], 31
@@ -215,11 +215,11 @@ define i1 @or_icmp_i64(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_i64(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i64 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i64 [[TYPE]] to i16
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -32703, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i64 [[TYPE]], 16
+; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]
 ;
 entry:
@@ -464,11 +464,11 @@ define i1 @or_icmp_i128(i128 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_i128(
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i128 [[TYPE]], 6
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 0
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i128 [[TYPE]], 15
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[CMP2]], [[OR_COND]]
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i128 [[TYPE]] to i16
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -32703, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i128 [[TYPE]], 16
+; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
 ; CHECK-NEXT:    ret i1 [[OR_COND1]]
 ;
 entry:

>From 756fc50c951991a51b29c394d9aba6063ef80f64 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Fri, 5 Dec 2025 14:13:53 -0800
Subject: [PATCH 17/17] [InstCombine] Update
 ConstantComparesGatherer::matchInstruction() to recognize existing bit-flag
 sequence

or_icmp_7: https://alive2.llvm.org/ce/z/srzpuF

or_icmp_expand: https://alive2.llvm.org/ce/z/V-iP4Y

or_icmp_expand_trunc_type_shr: https://alive2.llvm.org/ce/z/y7U2t7

or_icmp_expand_zext_cmp: https://alive2.llvm.org/ce/z/jTBP-_

or_icmp_expand_128: https://alive2.llvm.org/ce/z/y7U2t7
---
 llvm/include/llvm/Analysis/ValueTracking.h    |  3 +
 llvm/lib/Analysis/ValueTracking.cpp           | 34 ++++++++++
 .../InstCombine/InstCombineAndOrXor.cpp       |  4 +-
 .../test/Transforms/InstCombine/flag_check.ll | 64 +++++++------------
 4 files changed, 64 insertions(+), 41 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 6713c5a4e4247..ac91ff2df744f 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -1067,6 +1067,9 @@ struct ConstantComparesGatherer {
   /// If the elements in Vals matches the comparisons
   bool IsEq = false;
 
+  // At least on match was of an existing bit extract sequence
+  bool ExpansionCase = false;
+
   // Used to check if the first matched CompValue shall be the Extra check.
   bool IgnoreFirstMatch = false;
   bool MultipleMatches = false;
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 0e9352bce1141..193cdc9792f3f 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10640,6 +10640,40 @@ bool ConstantComparesGatherer::matchInstruction(Instruction *I, bool isEQ,
     Vals.push_back(ConstantInt::get(cast<IntegerType>(Val->getType()), isEQ));
     return true;
   }
+
+  // Expand an already existing BitMap sequence
+  // Match: (or (%BitMapSeq(X)), (icmp eq X, Imm))
+  ConstantInt *BitMap, *Bound;
+  if (match(I, m_Select(m_OneUse(m_SpecificICmp(ICmpInst::ICMP_ULT,
+                                                m_ZExtOrSelf(m_Value(Val)),
+                                                m_ConstantInt(Bound))),
+                        m_Trunc(m_OneUse(
+                            m_Shr(m_ConstantInt(BitMap),
+                                  m_ZExtOrTruncOrSelf(m_Deferred(Val))))),
+                        m_Zero()))) {
+
+    // If Or-tree, then cannot have leading Not
+    // If And-tree, must have leading Not
+    if (!isEQ)
+      return false;
+
+    // If we already have a value for the switch, it has to match!
+    if (!setValueOnce(Val))
+      return false;
+
+    APInt BitMapAP = BitMap->getValue();
+    APInt BoundAP = Bound->getValue();
+    unsigned BitMapWidth = BitMapAP.getBitWidth();
+    if (BoundAP != BitMapWidth)
+      return false;
+    for (unsigned I = 0; I < BitMapWidth; ++I)
+      if ((BitMapAP.lshr(I) & APInt(BitMapWidth, 1)).isOne())
+        Vals.push_back(ConstantInt::get(cast<IntegerType>(Val->getType()), I));
+    ++UsedICmps;
+    ExpansionCase = true;
+    return true;
+  }
+
   // If this is an icmp against a constant, handle this as one of the cases.
   ICmpInst *ICI;
   ConstantInt *C;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 8e56cef7e61b8..c15ba3e133e9d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2419,8 +2419,10 @@ static Value *combineAndOrOfImmCmpToBitExtract(Instruction &Or,
   Value *Index = ConstantCompare.CompValue;
 
   // TODO: Handle ConstantCompare.Extra case
+  // If expanding an existing case, only adding one extra case is still good
   if (!Index || !isGuaranteedNotToBeUndefOrPoison(Index) ||
-      ConstantCompare.UsedICmps < 3 || ConstantCompare.Extra)
+      (ConstantCompare.UsedICmps + ConstantCompare.ExpansionCase) < 3 ||
+      ConstantCompare.Extra)
     return nullptr;
 
   unsigned MaxRegWidth = DL.getLargestLegalIntTypeSizeInBits();
diff --git a/llvm/test/Transforms/InstCombine/flag_check.ll b/llvm/test/Transforms/InstCombine/flag_check.ll
index 6ae4fd64af5e4..5fdf29eb9f355 100644
--- a/llvm/test/Transforms/InstCombine/flag_check.ll
+++ b/llvm/test/Transforms/InstCombine/flag_check.ll
@@ -123,20 +123,11 @@ define i1 @or_icmp_7(i32 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_7(
 ; CHECK-SAME: i32 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[TYPE]] to i18
-; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i18 -131007, [[SWITCH_CAST]]
-; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i18 [[SWITCH_DOWNSHIFT]] to i1
-; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 18
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i32 -1878900663, [[TYPE]]
+; CHECK-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i32 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[TYPE]], 32
 ; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[TMP0]], i1 [[SWITCH_MASKED]], i1 false
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[TYPE]], 3
-; CHECK-NEXT:    [[OR_COND2:%.*]] = or i1 [[CMP3]], [[OR_COND1]]
-; CHECK-NEXT:    [[CMP4:%.*]] = icmp eq i32 [[TYPE]], 31
-; CHECK-NEXT:    [[OR_COND3:%.*]] = or i1 [[CMP4]], [[OR_COND2]]
-; CHECK-NEXT:    [[CMP5:%.*]] = icmp eq i32 [[TYPE]], 14
-; CHECK-NEXT:    [[OR_COND4:%.*]] = or i1 [[CMP5]], [[OR_COND3]]
-; CHECK-NEXT:    [[CMP6:%.*]] = icmp eq i32 [[TYPE]], 28
-; CHECK-NEXT:    [[OR_COND5:%.*]] = or i1 [[CMP6]], [[OR_COND4]]
-; CHECK-NEXT:    ret i1 [[OR_COND5]]
+; CHECK-NEXT:    ret i1 [[OR_COND1]]
 ;
 entry:
   %cmp = icmp eq i32 %type, 6
@@ -277,14 +268,12 @@ define i1 @or_icmp_expand(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i64 [[TYPE]] to i7
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i7 27, [[TYPE_T]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 7
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i7 [[SHR]] to i1
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i64 [[TYPE]] to i36
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i36 -34359738341, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i36 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 36
 ; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
+; CHECK-NEXT:    ret i1 [[AND]]
 ;
 entry:
   %type.t = trunc i64 %type to i7
@@ -417,14 +406,12 @@ define i1 @or_icmp_expand_trunc_type_shr(i128 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand_trunc_type_shr(
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE_T]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 64
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i128 [[TYPE]] to i36
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i36 -34359738303, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i36 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 36
 ; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[AND]]
 ;
 entry:
   %type.t = trunc i128 %type to i64
@@ -441,13 +428,12 @@ define i1 @or_icmp_expand_zext_cmp(i64 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand_zext_cmp(
 ; CHECK-SAME: i64 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 64
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i64 [[TYPE]] to i36
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i36 -34359738303, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i36 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TYPE]], 36
 ; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[AND]]
 ;
 entry:
   %type.e = zext i64 %type to i128
@@ -484,14 +470,12 @@ define i1 @or_icmp_expand_128(i128 signext noundef %type) {
 ; CHECK-LABEL: define i1 @or_icmp_expand_128(
 ; CHECK-SAME: i128 noundef signext [[TYPE:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TYPE_T:%.*]] = trunc i128 [[TYPE]] to i64
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 65, [[TYPE_T]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 64
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i1
+; CHECK-NEXT:    [[SWITCH_CAST:%.*]] = trunc i128 [[TYPE]] to i36
+; CHECK-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i36 -34359738303, [[SWITCH_CAST]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i36 [[SWITCH_DOWNSHIFT]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i128 [[TYPE]], 36
 ; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP]], i1 [[TRUNC]], i1 false
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i128 [[TYPE]], 35
-; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[AND]], [[CMP1]]
-; CHECK-NEXT:    ret i1 [[OR_COND1]]
+; CHECK-NEXT:    ret i1 [[AND]]
 ;
 entry:
   %type.t = trunc i128 %type to i64



More information about the llvm-commits mailing list