diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -1077,7 +1077,8 @@
 
   /// Try to simplify a floating-point binary operation into 1 of its operands
   /// or a constant.
-  SDValue simplifyFPBinop(unsigned Opcode, SDValue X, SDValue Y);
+  SDValue simplifyFPBinop(unsigned Opcode, SDValue X, SDValue Y,
+                          SDNodeFlags Flags);
 
   /// VAArg produces a result and token chain, and takes a pointer
   /// and a source value as input.
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -12220,6 +12220,9 @@
   const TargetOptions &Options = DAG.getTarget().Options;
   const SDNodeFlags Flags = N->getFlags();
 
+  if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
+    return R;
+
   // fold vector ops
   if (VT.isVector())
     if (SDValue FoldedVOp = SimplifyVBinOp(N))
@@ -12401,6 +12404,9 @@
   const TargetOptions &Options = DAG.getTarget().Options;
   const SDNodeFlags Flags = N->getFlags();
 
+  if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
+    return R;
+
   // fold vector ops
   if (VT.isVector())
     if (SDValue FoldedVOp = SimplifyVBinOp(N))
@@ -12499,6 +12505,9 @@
   const TargetOptions &Options = DAG.getTarget().Options;
   const SDNodeFlags Flags = N->getFlags();
 
+  if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
+    return R;
+
   // fold vector ops
   if (VT.isVector()) {
     // This just handles C1 * C2 for vectors. Other vector folds are below.
@@ -12831,6 +12840,9 @@
   const TargetOptions &Options = DAG.getTarget().Options;
   SDNodeFlags Flags = N->getFlags();
 
+  if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
+    return R;
+
   // fold vector ops
   if (VT.isVector())
     if (SDValue FoldedVOp = SimplifyVBinOp(N))
@@ -12931,6 +12943,10 @@
   ConstantFPSDNode *N0CFP = dyn_cast<ConstantFPSDNode>(N0);
   ConstantFPSDNode *N1CFP = dyn_cast<ConstantFPSDNode>(N1);
   EVT VT = N->getValueType(0);
+  SDNodeFlags Flags = N->getFlags();
+
+  if (SDValue R = DAG.simplifyFPBinop(N->getOpcode(), N0, N1, Flags))
+    return R;
 
   // fold (frem c1, c2) -> fmod(c1,c2)
   if (N0CFP && N1CFP)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -5229,7 +5229,7 @@
     assert(VT.isFloatingPoint() && "This operator only applies to FP types!");
     assert(N1.getValueType() == N2.getValueType() &&
            N1.getValueType() == VT && "Binary operator types must match!");
-    if (SDValue V = simplifyFPBinop(Opcode, N1, N2))
+    if (SDValue V = simplifyFPBinop(Opcode, N1, N2, Flags))
       return V;
     break;
   case ISD::FCOPYSIGN:   // N1 and result must match.  N1/N2 need not match.
@@ -7301,9 +7301,24 @@
   return SDValue();
 }
 
-// TODO: Use fast-math-flags to enable more simplifications.
-SDValue SelectionDAG::simplifyFPBinop(unsigned Opcode, SDValue X, SDValue Y) {
+SDValue SelectionDAG::simplifyFPBinop(unsigned Opcode, SDValue X, SDValue Y,
+                                      SDNodeFlags Flags) {
+  // If this operation has 'nnan' or 'ninf' and at least 1 disallowed operand
+  // (an undef operand can be chosen to be Nan/Inf), then the result of this
+  // operation is poison. That result can be relaxed to undef.
+  ConstantFPSDNode *XC = isConstOrConstSplatFP(X, /* AllowUndefs */ true);
   ConstantFPSDNode *YC = isConstOrConstSplatFP(Y, /* AllowUndefs */ true);
+  bool HasNan = (XC && XC->getValueAPF().isNaN()) ||
+                (YC && YC->getValueAPF().isNaN());
+  bool HasInf = (XC && XC->getValueAPF().isInfinity()) ||
+                (YC && YC->getValueAPF().isInfinity());
+
+  if (Flags.hasNoNaNs() && (HasNan || X.isUndef() || Y.isUndef()))
+    return getUNDEF(X.getValueType());
+
+  if (Flags.hasNoInfs() && (HasInf || X.isUndef() || Y.isUndef()))
+    return getUNDEF(X.getValueType());
+
   if (!YC)
     return SDValue();
 
diff --git a/llvm/test/CodeGen/AArch64/fp-const-fold.ll b/llvm/test/CodeGen/AArch64/fp-const-fold.ll
--- a/llvm/test/CodeGen/AArch64/fp-const-fold.ll
+++ b/llvm/test/CodeGen/AArch64/fp-const-fold.ll
@@ -76,9 +76,6 @@
 define double @fdiv_nnan_nan_op0(double %x) {
 ; CHECK-LABEL: fdiv_nnan_nan_op0:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov x8, #-2251799813685248
-; CHECK-NEXT:    fmov d1, x8
-; CHECK-NEXT:    fdiv d0, d1, d0
 ; CHECK-NEXT:    ret
   %r = fdiv nnan double 0xfff8000000000000, %x
   ret double %r
@@ -87,14 +84,14 @@
 define double @fmul_nnan_nan_op1(double %x) {
 ; CHECK-LABEL: fmul_nnan_nan_op1:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov x8, #9221120237041090560
-; CHECK-NEXT:    fmov d1, x8
-; CHECK-NEXT:    fmul d0, d0, d1
 ; CHECK-NEXT:    ret
   %r = fmul nnan double %x, 0x7ff8000000000000
   ret double %r
 }
 
+; Negative test - nan is ok.
+; TODO: Should simplify to nan.
+
 define double @fdiv_ninf_nan_op0(double %x) {
 ; CHECK-LABEL: fdiv_ninf_nan_op0:
 ; CHECK:       // %bb.0:
@@ -106,6 +103,9 @@
   ret double %r
 }
 
+; Negative test - nan is ok.
+; TODO: Should simplify to nan.
+
 define double @fadd_ninf_nan_op1(double %x) {
 ; CHECK-LABEL: fadd_ninf_nan_op1:
 ; CHECK:       // %bb.0:
@@ -120,9 +120,6 @@
 define double @fdiv_ninf_inf_op0(double %x) {
 ; CHECK-LABEL: fdiv_ninf_inf_op0:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov x8, #9218868437227405312
-; CHECK-NEXT:    fmov d1, x8
-; CHECK-NEXT:    fdiv d0, d1, d0
 ; CHECK-NEXT:    ret
   %r = fdiv ninf double 0x7ff0000000000000, %x
   ret double %r
@@ -131,14 +128,14 @@
 define double @fadd_ninf_inf_op1(double %x) {
 ; CHECK-LABEL: fadd_ninf_inf_op1:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov x8, #-4503599627370496
-; CHECK-NEXT:    fmov d1, x8
-; CHECK-NEXT:    fadd d0, d0, d1
 ; CHECK-NEXT:    ret
   %r = fadd ninf double %x, 0xfff0000000000000
   ret double %r
 }
 
+; Negative test - inf is ok.
+; TODO: Should simplify to inf.
+
 define double @fsub_nnan_inf_op0(double %x) {
 ; CHECK-LABEL: fsub_nnan_inf_op0:
 ; CHECK:       // %bb.0:
@@ -150,6 +147,9 @@
   ret double %r
 }
 
+; Negative test - inf is ok.
+; TODO: Should simplify to -inf.
+
 define double @fmul_nnan_inf_op1(double %x) {
 ; CHECK-LABEL: fmul_nnan_inf_op1:
 ; CHECK:       // %bb.0:
@@ -161,6 +161,8 @@
   ret double %r
 }
 
+; TODO: Should simplify to undef
+
 define double @fdiv_nnan_undef_op0(double %x) {
 ; CHECK-LABEL: fdiv_nnan_undef_op0:
 ; CHECK:       // %bb.0:
@@ -171,6 +173,8 @@
   ret double %r
 }
 
+; TODO: Should simplify to undef
+
 define double @fdiv_nnan_undef_op1(double %x) {
 ; CHECK-LABEL: fdiv_nnan_undef_op1:
 ; CHECK:       // %bb.0:
@@ -181,6 +185,8 @@
   ret double %r
 }
 
+; TODO: Should simplify to undef
+
 define double @fdiv_ninf_undef_op0(double %x) {
 ; CHECK-LABEL: fdiv_ninf_undef_op0:
 ; CHECK:       // %bb.0:
@@ -191,6 +197,8 @@
   ret double %r
 }
 
+; TODO: Should simplify to undef
+
 define double @fdiv_ninf_undef_op1(double %x) {
 ; CHECK-LABEL: fdiv_ninf_undef_op1:
 ; CHECK:       // %bb.0: