[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