[llvm] Intrinsic: introduce minimumnum and maximumnum for IR and SelectionDAG (PR #96649)

YunQiang Su via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 28 07:27:19 PDT 2024


================
@@ -8531,6 +8531,94 @@ SDValue TargetLowering::expandFMINIMUM_FMAXIMUM(SDNode *N,
   return MinMax;
 }
 
+SDValue TargetLowering::expandFMINIMUMNUM_FMAXIMUMNUM(SDNode *Node,
+                                                      SelectionDAG &DAG) const {
+  SDLoc DL(Node);
+  SDValue LHS = Node->getOperand(0);
+  SDValue RHS = Node->getOperand(1);
+  unsigned Opc = Node->getOpcode();
+  EVT VT = Node->getValueType(0);
+  EVT CCVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
+  bool IsMax = Opc == ISD::FMAXIMUMNUM;
+  SDNodeFlags Flags = Node->getFlags();
+
+  unsigned NewOp =
+      Opc == ISD::FMINIMUMNUM ? ISD::FMINNUM_IEEE : ISD::FMAXNUM_IEEE;
+
+  if (isOperationLegalOrCustom(NewOp, VT)) {
+    if (!Flags.hasNoNaNs()) {
+      // Insert canonicalizes if it's possible we need to quiet to get correct
+      // sNaN behavior.
+      if (!DAG.isKnownNeverSNaN(LHS)) {
+        LHS = DAG.getNode(ISD::FCANONICALIZE, DL, VT, LHS, Node->getFlags());
+      }
+      if (!DAG.isKnownNeverSNaN(RHS)) {
+        RHS = DAG.getNode(ISD::FCANONICALIZE, DL, VT, RHS, Node->getFlags());
+      }
+    }
+
+    return DAG.getNode(NewOp, DL, VT, LHS, RHS, Flags);
+  }
+
+  // FMINIMUM/FMAXIMUM always return NaN if either operand is NaN.
+  // It has same behavior about +0.0 vs -0.0.
+  if (Flags.hasNoNaNs() ||
+      (DAG.isKnownNeverNaN(LHS) && DAG.isKnownNeverNaN(RHS))) {
+    unsigned IEEE2019Op =
+        Opc == ISD::FMINIMUMNUM ? ISD::FMINIMUM : ISD::FMAXIMUM;
+    if (isOperationLegalOrCustom(IEEE2019Op, VT))
+      return DAG.getNode(IEEE2019Op, DL, VT, LHS, RHS, Flags);
+  }
+
+  // FMINNUM/FMAXMUM returns qNaN if either operand is sNaN, and it may return
+  // either one for +0.0 vs -0.0.
+  // FIXME: maybe we need hasNoSNaNs().
+  if ((Flags.hasNoNaNs() ||
+       (DAG.isKnownNeverSNaN(LHS) && DAG.isKnownNeverSNaN(RHS))) &&
+      (Flags.hasNoSignedZeros() || DAG.isKnownNeverZeroFloat(LHS) ||
+       DAG.isKnownNeverZeroFloat(RHS))) {
+    unsigned IEEE2008Op = Opc == ISD::FMINIMUMNUM ? ISD::FMINNUM : ISD::FMAXNUM;
+    if (isOperationLegalOrCustom(IEEE2008Op, VT))
+      return DAG.getNode(IEEE2008Op, DL, VT, LHS, RHS, Flags);
+  }
+
+  SDValue MinMax;
+  // If one operand is NaN, let's move another value to it.
+  // So that if only one operand is NaN, we can have both two operands are
+  // non-NaN now.
+  SDValue LHSCompareLHS = DAG.getSetCC(DL, CCVT, LHS, LHS, ISD::SETEQ);
+  LHS = DAG.getSelect(DL, VT, LHSCompareLHS, LHS, RHS, Flags);
+  SDValue RHSCompareRHS = DAG.getSetCC(DL, CCVT, RHS, RHS, ISD::SETEQ);
+  RHS = DAG.getSelect(DL, VT, RHSCompareRHS, RHS, LHS, Flags);
+
+  SDValue Compare =
+      DAG.getSetCC(DL, CCVT, LHS, RHS, IsMax ? ISD::SETGT : ISD::SETLT);
+  MinMax = DAG.getSelect(DL, VT, Compare, LHS, RHS, Flags);
+
+  // If MinMax is NaN, let's quiet it with MinMax = MinMax + 0.
----------------
wzssyqa wrote:

Glibc does use `add` to quiet sNaNs, It can do so due that `add` will trap if either operand is sNaN, and the kernel will quiet the result, which is required by IEE754.
For embedded system without such kernel, it will not be possible.

https://github.com/llvm/llvm-project/pull/96649


More information about the llvm-commits mailing list