[llvm] [RISCV] Lower flag check pattern to use a bitmask-shift (PR #169557)
Ryan Buchner via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 26 14:50:26 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 1/6] [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 2/6] [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 3/6] [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 4/6] [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 5/6] [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 6/6] [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
More information about the llvm-commits
mailing list