[llvm] Allow MIN/MAX signedness flip when operands are known-negative (PR #174469)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 5 11:25:40 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-x86
@llvm/pr-subscribers-llvm-selectiondag
Author: None (fbrv)
<details>
<summary>Changes</summary>
Extend the existing DAGCombine logic in visitIMINMAX so that signed and unsigned MIN/MAX can be flipped not only when both operands are known non-negative but also when both operands are known negative. This replaces the old SignBitIsZero checks with computeKnownBits and explicit tests for non-negative or negative operands while keeping all existing legality and saturation gating in place. Add regression tests to cover both the known-negative case and the known-non-negative case. Fixes #<!-- -->174325.
(I used chatgpt to check the grammar for the pr description as english is not my native language)
---
Full diff: https://github.com/llvm/llvm-project/pull/174469.diff
2 Files Affected:
- (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+28-12)
- (added) llvm/test/CodeGen/X86/dagcombine-iminmax-knownsign.ll (+52)
``````````diff
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 74d00317c3649..2c7a61385e020 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -6204,26 +6204,42 @@ SDValue DAGCombiner::visitIMINMAX(SDNode *N) {
if (SDValue RMINMAX = reassociateOps(Opcode, DL, N0, N1, N->getFlags()))
return RMINMAX;
- // Is sign bits are zero, flip between UMIN/UMAX and SMIN/SMAX.
+ // If both operands are known to have the same sign (both non-negative or both
+ // negative), flip between UMIN/UMAX and SMIN/SMAX.
// Only do this if:
// 1. The current op isn't legal and the flipped is.
// 2. The saturation pattern is broken by canonicalization in InstCombine.
bool IsOpIllegal = !TLI.isOperationLegal(Opcode, VT);
bool IsSatBroken = Opcode == ISD::UMIN && N0.getOpcode() == ISD::SMAX;
- if ((IsSatBroken || IsOpIllegal) && (N0.isUndef() || DAG.SignBitIsZero(N0)) &&
- (N1.isUndef() || DAG.SignBitIsZero(N1))) {
- unsigned AltOpcode;
- switch (Opcode) {
- case ISD::SMIN: AltOpcode = ISD::UMIN; break;
- case ISD::SMAX: AltOpcode = ISD::UMAX; break;
- case ISD::UMIN: AltOpcode = ISD::SMIN; break;
- case ISD::UMAX: AltOpcode = ISD::SMAX; break;
- default: llvm_unreachable("Unknown MINMAX opcode");
+
+ if (IsSatBroken || IsOpIllegal) {
+ auto HasKnownSameSign = [&](SDValue A, SDValue B) {
+ if (A.isUndef() || B.isUndef())
+ return true;
+
+ KnownBits KA = DAG.computeKnownBits(A);
+ KnownBits KB = DAG.computeKnownBits(B);
+
+ return (KA.isNonNegative() && KB.isNonNegative()) ||
+ (KA.isNegative() && KB.isNegative());
+ };
+
+ if (HasKnownSameSign(N0, N1)) {
+ unsigned AltOpcode;
+ switch (Opcode) {
+ case ISD::SMIN: AltOpcode = ISD::UMIN; break;
+ case ISD::SMAX: AltOpcode = ISD::UMAX; break;
+ case ISD::UMIN: AltOpcode = ISD::SMIN; break;
+ case ISD::UMAX: AltOpcode = ISD::SMAX; break;
+ default: llvm_unreachable("Unknown MINMAX opcode");
+ }
+
+ if ((IsSatBroken && IsOpIllegal) || TLI.isOperationLegal(AltOpcode, VT))
+ return DAG.getNode(AltOpcode, DL, VT, N0, N1);
}
- if ((IsSatBroken && IsOpIllegal) || TLI.isOperationLegal(AltOpcode, VT))
- return DAG.getNode(AltOpcode, DL, VT, N0, N1);
}
+
if (Opcode == ISD::SMIN || Opcode == ISD::SMAX)
if (SDValue S = PerformMinMaxFpToSatCombine(
N0, N1, N0, N1, Opcode == ISD::SMIN ? ISD::SETLT : ISD::SETGT, DAG))
diff --git a/llvm/test/CodeGen/X86/dagcombine-iminmax-knownsign.ll b/llvm/test/CodeGen/X86/dagcombine-iminmax-knownsign.ll
new file mode 100644
index 0000000000000..71298b5c38b90
--- /dev/null
+++ b/llvm/test/CodeGen/X86/dagcombine-iminmax-knownsign.ll
@@ -0,0 +1,52 @@
+; RUN: llc -mtriple=x86_64-unknown-unknown -mattr=+sse2,-sse4.1 < %s | FileCheck %s
+
+declare <16 x i8> @llvm.smax.v16i8(<16 x i8>, <16 x i8>)
+declare <16 x i8> @llvm.smin.v16i8(<16 x i8>, <16 x i8>)
+
+define <16 x i8> @smax_both_negative(<16 x i8> %a, <16 x i8> %b) {
+; CHECK-LABEL: smax_both_negative:
+; CHECK: pmaxub
+ %mask0 = insertelement <16 x i8> poison, i8 -128, i64 0
+ %mask = shufflevector <16 x i8> %mask0, <16 x i8> poison,
+ <16 x i32> zeroinitializer
+ %a1 = or <16 x i8> %a, %mask
+ %b1 = or <16 x i8> %b, %mask
+ %r = call <16 x i8> @llvm.smax.v16i8(<16 x i8> %a1, <16 x i8> %b1)
+ ret <16 x i8> %r
+}
+
+define <16 x i8> @smin_both_negative(<16 x i8> %a, <16 x i8> %b) {
+; CHECK-LABEL: smin_both_negative:
+; CHECK: pminub
+ %mask0 = insertelement <16 x i8> poison, i8 -128, i64 0
+ %mask = shufflevector <16 x i8> %mask0, <16 x i8> poison,
+ <16 x i32> zeroinitializer
+ %a1 = or <16 x i8> %a, %mask
+ %b1 = or <16 x i8> %b, %mask
+ %r = call <16 x i8> @llvm.smin.v16i8(<16 x i8> %a1, <16 x i8> %b1)
+ ret <16 x i8> %r
+}
+
+define <16 x i8> @smax_both_nonnegative(<16 x i8> %a, <16 x i8> %b) {
+; CHECK-LABEL: smax_both_nonnegative:
+; CHECK: pmaxub
+ %mask0 = insertelement <16 x i8> poison, i8 127, i64 0
+ %mask = shufflevector <16 x i8> %mask0, <16 x i8> poison,
+ <16 x i32> zeroinitializer
+ %a1 = and <16 x i8> %a, %mask
+ %b1 = and <16 x i8> %b, %mask
+ %r = call <16 x i8> @llvm.smax.v16i8(<16 x i8> %a1, <16 x i8> %b1)
+ ret <16 x i8> %r
+}
+
+define <16 x i8> @smin_both_nonnegative(<16 x i8> %a, <16 x i8> %b) {
+; CHECK-LABEL: smin_both_nonnegative:
+; CHECK: pminub
+ %mask0 = insertelement <16 x i8> poison, i8 127, i64 0
+ %mask = shufflevector <16 x i8> %mask0, <16 x i8> poison,
+ <16 x i32> zeroinitializer
+ %a1 = and <16 x i8> %a, %mask
+ %b1 = and <16 x i8> %b, %mask
+ %r = call <16 x i8> @llvm.smin.v16i8(<16 x i8> %a1, <16 x i8> %b1)
+ ret <16 x i8> %r
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/174469
More information about the llvm-commits
mailing list