[flang-commits] [clang] [flang] [llvm] Option to control signaling NaN support (PR #193055)

Serge Pavlov via flang-commits flang-commits at lists.llvm.org
Mon Jun 15 01:14:34 PDT 2026


https://github.com/spavloff updated https://github.com/llvm/llvm-project/pull/193055

>From f562a238622815b796866e45d4dfcbafae093c9d Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Wed, 15 Apr 2026 01:08:44 +0700
Subject: [PATCH 01/10] Option to control signaling NaN support

This change implements the Clang command-line option `-fsignaling-nans`,
which is a counterpart of the GCC option with the same name. It allows a
user to control support for signaling NaNs. This option instructs the
compiler that signaling NaNs are to be treated according to IEEE 754:
they are quieted in arithmetic operations and raise `Invalid`
floating-point exception. The opposite option, `-fno-signaling-nans`,
does the reverse, - it indicates that signaling NaNs are handled
identically to quiet NaNs. If neither of these options is specified, no
signaling NaNs support is assumed, except for functions that have
`strictfp` attribute.

At the IR level, signaling NaN support is represented by the function
attribute "signaling-nans". It is set by Clang when it generates code in
cases when signaling NaNs are supported. If the target architecture does
not support signaling NaNs, Clang does not set this attribute.

The primary motivation for this change is the optimization of strictfp
code. When signaling NaN support is present, it can hinder optimization
in some cases. For example, the transformation `x * 1.0 -> x` is not
valid. Additionally, many intrinsic functions, like `trunc`, do not
raise floating-point exceptions other than those caused by signaling
NaNs, which can be beneficial for optimizations. The previous model
assumed that signaling NaNs are always supported in strictfp functions.
However, some hardware do not implement full support of signaling NaNs
and do not distinguish between quiet and signaling NaNs. It also can be
profitable to the skip signaling NaN support to enable more optimizations,
if the user can guarantee that signaling NaNs cannot be encountered in
their program.
---
 clang/docs/ReleaseNotes.rst                   |   2 +
 clang/docs/UsersManual.rst                    |  30 +++
 clang/include/clang/Basic/CodeGenOptions.def  |   6 +
 clang/include/clang/Basic/TargetInfo.h        |   4 +
 clang/include/clang/Options/Options.td        |  10 +-
 clang/lib/Basic/TargetInfo.cpp                |   1 +
 clang/lib/Basic/Targets/AArch64.cpp           |   1 +
 clang/lib/Basic/Targets/PPC.h                 |   1 +
 clang/lib/Basic/Targets/RISCV.h               |   1 +
 clang/lib/Basic/Targets/SystemZ.h             |   1 +
 clang/lib/Basic/Targets/X86.h                 |   1 +
 clang/lib/CodeGen/CodeGenFunction.cpp         |  11 +
 clang/lib/Driver/ToolChains/Clang.cpp         |  17 +-
 clang/lib/Frontend/InitPreprocessor.cpp       |   3 +
 clang/test/CodeGen/attr-signaling-nans.c      |  14 ++
 .../cl20-device-side-enqueue-attributes.cl    |   2 +-
 clang/test/Driver/clang_f_opts.c              |  19 +-
 clang/test/Driver/fast-math.c                 |   4 +-
 llvm/docs/LangRef.rst                         |  19 ++
 llvm/docs/ReleaseNotes.md                     |   6 +
 llvm/include/llvm/Analysis/SimplifyQuery.h    |   8 +
 llvm/include/llvm/IR/FPEnv.h                  |   6 -
 llvm/lib/Analysis/InstructionSimplify.cpp     |  16 +-
 .../Transforms/InstSimplify/strictfp-fadd.ll  | 113 ++++++++++-
 .../Transforms/InstSimplify/strictfp-fsub.ll  | 189 +++++++++++++++++-
 25 files changed, 463 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-signaling-nans.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cf16e40d026c3..8e9d41d8b6121 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -308,6 +308,8 @@ New Compiler Flags
   reduced BMI only for a C++20 importable module unit. Previously the users
   can only generate the reduced BMI as a by-product, e.g, an object files or
   a full BMI.
+- New option ``-f[no-]signaling-nans`` added to control yhe support of
+  signaling NaNs.
 
 - New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
   enable parsing of the experimental ``overflow_behavior`` type attribute and
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 4c4c4c4aa9706..2e8530656a204 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1568,6 +1568,7 @@ describes the various floating point semantic modes and the corresponding option
   "fallow-approximate-fns", "{on, off}"
   "fassociative-math", "{on, off}"
   "fcomplex-arithmetic", "{basic, improved, full, promoted}"
+  "fsignaling-nans", "{on, off}"
 
 This table describes the option settings that correspond to the three
 floating point semantic models: precise (the default), strict, and fast.
@@ -1674,6 +1675,8 @@ for more details.
 
    * ``-f[no-]math-errno``
 
+   * ``-fno-signaling-nans``
+
    There is ambiguity about how ``-ffp-contract``, ``-ffast-math``,
    and ``-fno-fast-math`` behave when combined. To keep the value of
    ``-ffp-contract`` consistent, we define this set of rules:
@@ -1871,6 +1874,33 @@ for more details.
    - The option ``-fno-rounding-math`` allows the compiler to assume that the rounding mode is set to ``FE_TONEAREST``.  This is the default.
    - The option ``-frounding-math`` forces the compiler to honor the dynamically-set rounding mode.  This prevents optimizations which might affect results if the rounding mode changes or is different from the default; for example, it prevents floating-point operations from being reordered across most calls and prevents constant-folding when the result is not exactly representable.
 
+.. option:: -f[no-]signaling-nans
+
+   Informs the compiler whether signaling NaNs behave according to IEEE 754.
+
+   IEEE 754 defines signaling NaNs (SNaNs) as a subset of Not-a-Numbers (NaNs),
+   which possesses following properties:
+
+   * Floating-point operations, in which an SNaN is an operand, raise the
+     ``Invalid`` exception,
+   * Floating-point operations do not produce SNaNs, only quiet NaN can be a
+     result. Some target architectures do not support SNaNs; only a quiet NaN
+     can be a result.
+
+   The option ``-fsignaling-nans`` specifies IEEE 754 compliant behavior for
+   signaling NaNs. It has no effect if the target architecture does not
+   implements IEEE 754 signaling NaN behavior. This option causes the
+   preprocessor macro ``__SUPPORT_SNAN__`` to be defined.
+
+   The option ``-fno-signaling-nans`` specifies that signaling NaNs are treated
+   in the same way as quiet NaNs. This is the only option allowed if the target
+   architecture does not implement signaling NaNs according to IEEE-754. On
+   supporting architectures, it can enable additional optimization opportunities.
+
+   If more than one option is specified, the last one takes effect. If none is
+   specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
+   compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
+
 .. option:: -ffp-model=<value>
 
    Specify floating point behavior. ``-ffp-model`` is an umbrella
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index aa36de6edecbf..df2463227aacb 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -532,6 +532,12 @@ ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
 /// Adds attributes that prevent outlining (`-mno-outline`)
 CODEGENOPT(DisableOutlining, 1, 0, Benign)
 
+/// Whether compiler should treat signaling NaNs according to IEEE 754. Use
+/// separate options for positive and negative settings, this allows us to
+/// represent "unspecified" setting.
+CODEGENOPT(SignalingNans, 1, 0, Benign)
+CODEGENOPT(NoSignalingNans, 1, 0, Benign)
+
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
 
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index cc226403877e2..686c22ef4f156 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -249,6 +249,7 @@ class TargetInfo : public TransferrableTargetInfo,
   bool HasLongDouble;
   bool HasFPReturn;
   bool HasStrictFP;
+  bool HasSignalingNaNs;
 
   unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
   std::string DataLayoutString;
@@ -742,6 +743,9 @@ class TargetInfo : public TransferrableTargetInfo,
   /// Determine whether constrained floating point is supported on this target.
   virtual bool hasStrictFP() const { return HasStrictFP; }
 
+  /// Determine whether signaling NaNs are supported on this target.
+  virtual bool hasSignalingNaNs() const { return HasSignalingNaNs; }
+
   /// Return the alignment that is the largest alignment ever used for any
   /// scalar/SIMD data type on the target machine you are compiling for
   /// (including types with an extended alignment requirement).
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 753e3ac1b74a5..84fbc2f8b6709 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3002,6 +3002,15 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   " Default is 'fast' for CUDA, 'fast-honor-pragmas' for HIP, and 'on' otherwise.">,
   HelpText<"Form fused FP ops (e.g. FMAs)">,
   Values<"fast,on,off,fast-honor-pragmas">;
+def fsignaling_nans : Flag<["-"], "fsignaling-nans">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<CodeGenOpts<"SignalingNans">>,
+  HelpText<"Assume signaling NaNs behave according to IEEE rules and can"
+           "raise floating-point exceptions">;
+def fno_signaling_nans : Flag<["-"], "fno-signaling-nans">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<CodeGenOpts<"NoSignalingNans">>,
+  HelpText<"Assume signaling NaNs are treated identically to quiet NaNs">;
 
 defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
   CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
@@ -7432,7 +7441,6 @@ defm ripa : BooleanFFlag<"ripa">, Group<clang_ignored_f_Group>;
 defm schedule_insns : BooleanFFlag<"schedule-insns">, Group<clang_ignored_gcc_optimization_f_Group>;
 defm schedule_insns2 : BooleanFFlag<"schedule-insns2">, Group<clang_ignored_gcc_optimization_f_Group>;
 defm see : BooleanFFlag<"see">, Group<clang_ignored_f_Group>;
-defm signaling_nans : BooleanFFlag<"signaling-nans">, Group<clang_ignored_gcc_optimization_f_Group>;
 defm single_precision_constant : BooleanFFlag<"single-precision-constant">,
     Group<clang_ignored_gcc_optimization_f_Group>;
 defm spec_constr_count : BooleanFFlag<"spec-constr-count">, Group<clang_ignored_f_Group>;
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index ad083242d9e3e..7609123addf19 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -76,6 +76,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
   HasLongDouble = true;
   HasFPReturn = true;
   HasStrictFP = false;
+  HasSignalingNaNs = false;
   PointerWidth = PointerAlign = 32;
   BoolWidth = BoolAlign = 8;
   ShortWidth = ShortAlign = 16;
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index 9afe6cb10729d..3ec6dda5df3be 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -151,6 +151,7 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
   HalfArgsAndReturns = true;
   HasFloat16 = true;
   HasStrictFP = true;
+  HasSignalingNaNs = true;
 
   if (Triple.isArch64Bit())
     LongWidth = LongAlign = PointerWidth = PointerAlign = 64;
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index a9f49aa3aebe1..6812c65f85e98 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -84,6 +84,7 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
     LongDoubleWidth = LongDoubleAlign = 128;
     LongDoubleFormat = &llvm::APFloat::PPCDoubleDouble();
     HasStrictFP = true;
+    HasSignalingNaNs = true;
     HasIbm128 = true;
     HasUnalignedAccess = true;
   }
diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h
index 619d491d379d3..643797f4686f3 100644
--- a/clang/lib/Basic/Targets/RISCV.h
+++ b/clang/lib/Basic/Targets/RISCV.h
@@ -49,6 +49,7 @@ class RISCVTargetInfo : public TargetInfo {
     MCountName = "_mcount";
     HasFloat16 = true;
     HasStrictFP = true;
+    HasSignalingNaNs = true;
   }
 
   bool setCPU(const std::string &Name) override {
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index 00f7d7a055b24..4ec379e5a2eb5 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -103,6 +103,7 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
     HasFastHalfType = false;
 
     HasStrictFP = true;
+    HasSignalingNaNs = true;
   }
 
   unsigned getMinGlobalAlign(uint64_t Size, bool HasNonWeakDef) const override;
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index c8c5d280754b4..c02715e036957 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -195,6 +195,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
     LongDoubleFormat = &llvm::APFloat::x87DoubleExtended();
     AddrSpaceMap = &X86AddrSpaceMap;
     HasStrictFP = true;
+    HasSignalingNaNs = true;
     HasUnalignedAccess = true;
 
     bool IsWinCOFF =
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b920266b59808..8f4ef4f45548e 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1004,6 +1004,17 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
   if (D && D->hasAttr<HybridPatchableAttr>())
     Fn->addFnAttr(llvm::Attribute::HybridPatchable);
 
+  if (D) {
+    if (CGM.getCodeGenOpts().SignalingNans) {
+      if (getContext().getTargetInfo().hasSignalingNaNs())
+        Fn->addFnAttr("signaling-nans");
+    } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
+      // Both options are absent, - calculate default setting.
+      if (D->hasAttr<StrictFPAttr>())
+        Fn->addFnAttr("signaling-nans");
+    }
+  }
+
   if (D) {
     // Function attributes take precedence over command line flags.
     if (auto *A = D->getAttr<FunctionReturnThunksAttr>()) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 8d8e00bbaf7d0..ffe9180034a39 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2802,6 +2802,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   bool TrappingMathPresent = false; // Is trapping-math in args, and not
                                     // overriden by ffp-exception-behavior?
   bool RoundingFPMath = false;
+  std::optional<bool> SignalingNaNs;
   // -ffp-model values: strict, fast, precise
   StringRef FPModel = "";
   // -ffp-exception-behavior options: strict, maytrap, ignore
@@ -2853,6 +2854,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
     FPExceptionBehavior = "";
     FPContract = "fast";
     SeenUnsafeMathModeOption = true;
+    SignalingNaNs = false;
   };
 
   // Lambda to consolidate common handling for fp-contract
@@ -3016,6 +3018,12 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
       TrappingMathPresent = true;
       FPExceptionBehavior = "strict";
       break;
+    case options::OPT_fsignaling_nans:
+      SignalingNaNs = true;
+      break;
+    case options::OPT_fno_signaling_nans:
+      SignalingNaNs = false;
+      break;
     case options::OPT_fveclib:
       VecLibArg = A;
       NoMathErrnoWasImpliedByVecLib =
@@ -3306,6 +3314,12 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   if (!BFloat16ExcessPrecision.empty())
     CmdArgs.push_back(Args.MakeArgString("-fbfloat16-excess-precision=" +
                                          BFloat16ExcessPrecision));
+  if (SignalingNaNs) {
+    if (*SignalingNaNs)
+      CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
+    else
+      CmdArgs.push_back(Args.MakeArgString("-fno-signaling-nans"));
+  }
 
   StringRef Recip = parseMRecipOption(D.getDiags(), Args);
   if (!Recip.empty())
@@ -3315,7 +3329,8 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   // individual features enabled by -ffast-math instead of the option itself as
   // that's consistent with gcc's behaviour.
   if (!HonorINFs && !HonorNaNs && !MathErrno && AssociativeMath && ApproxFunc &&
-      ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath)
+      ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath &&
+      (SignalingNaNs.has_value() && !SignalingNaNs.value()))
     CmdArgs.push_back("-ffast-math");
 
   // Handle __FINITE_MATH_ONLY__ similarly.
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 3f0468a938149..7e0283868d87e 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1323,6 +1323,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
   else
     Builder.defineMacro("__FINITE_MATH_ONLY__", "0");
 
+  if (CGOpts.SignalingNans)
+    Builder.defineMacro("__SUPPORT_SNAN__");
+
   if (LangOpts.GNUCVersion) {
     if (LangOpts.GNUInline || LangOpts.CPlusPlus)
       Builder.defineMacro("__GNUC_GNU_INLINE__");
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
new file mode 100644
index 0000000000000..eef64875f04a6
--- /dev/null
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+
+float func_01(float x) {
+  return x / 11.0F;
+}
+
+// CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
+
+
+// SIGNALING: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
+// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index 962ac4afa4991..e42105b699e39 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
 //.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "signaling-nans" "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index 5871f1580d6b7..eb78ddf84d6b7 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -395,7 +395,6 @@
 // CHECK-WARNING-DAG: optimization flag '-fprofile-correction' is not supported
 // CHECK-WARNING-DAG: optimization flag '-fprofile-values' is not supported
 // CHECK-WARNING-DAG: optimization flag '-fschedule-insns' is not supported
-// CHECK-WARNING-DAG: optimization flag '-fsignaling-nans' is not supported
 // CHECK-WARNING-DAG: optimization flag '-fstrength-reduce' is not supported
 // CHECK-WARNING-DAG: optimization flag '-ftracer' is not supported
 // CHECK-WARNING-DAG: optimization flag '-funroll-all-loops' is not supported
@@ -651,3 +650,21 @@
 // RUN: %clang -### --target=x86_64-pc-windows-msvc -fno-strict-aliasing %s 2>&1 | FileCheck -check-prefix=CHECK-NO-STRICT-ALIASING %s
 // CHECK-STRICT-ALIASING-NOT: -relaxed-aliasing
 // CHECK-NO-STRICT-ALIASING: -relaxed-aliasing
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
+// CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
+// CHECK-SIGNALING-NANS: "-cc1"
+// CHECK-SIGNALING-NANS: "-fsignaling-nans"
+// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
+// CHECK-NO-SIGNALING-NANS: "-cc1"
+// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
+// CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fast-math.c b/clang/test/Driver/fast-math.c
index ffd081948914d..7d25f9a4dd5b3 100644
--- a/clang/test/Driver/fast-math.c
+++ b/clang/test/Driver/fast-math.c
@@ -143,11 +143,11 @@
 // RUN: %clang -### -fno-fast-math -ffast-math -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,REASSOC,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
 // RUN: %clang -### -funsafe-math-optimizations -ffinite-math-only \
-// RUN:     -fno-math-errno -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \
+// RUN:     -fno-math-errno -ffp-contract=fast -fno-rounding-math -fno-signaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
 // RUN: %clang -### -fno-honor-infinities -fno-honor-nans -fno-math-errno \
 // RUN:     -fassociative-math -freciprocal-math -fno-signed-zeros -fapprox-func \
-// RUN:     -fno-trapping-math -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \
+// RUN:     -fno-trapping-math -ffp-contract=fast -fno-rounding-math -fno-signaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
 //
 // RUN: %clang -### -ffast-math -fno-fast-math -c %s 2>&1 \
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 81694139fcea2..e5ebea118b1e3 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2586,6 +2586,14 @@ For example:
 ``sanitize_alloc_token``
     This attribute indicates that implicit allocation token instrumentation
     is enabled for this function.
+``signaling-nans``
+    If a function has this attribute, signaling NaNs are assumed to be treated
+    according to IEEE 754 rules. That is, signaling NaN values are quieted in
+    arithmetic operations, and the floating-point exception ``Invalid`` is
+    raised if an operand of such an operation is a signaling NaN. If this
+    attribute is absent, signaling NaNs are assumed to be treated identically to
+    quiet NaNs. This attribute cannot be set if the target architecture does not
+    support IEEE 754 compatible signaling NaN handling.
 ``speculative_load_hardening``
     This attribute indicates that
     `Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
@@ -4145,6 +4153,17 @@ specification on some architectures:
   LLVM does not correctly represent this. See `issue #60796
   <https://github.com/llvm/llvm-project/issues/60796>`_.
 
+The behavior of signaling NaNs in runtime is determined by the underlying
+hardware capabilities and the specified function attributes. If the target
+hardware implements IEEE 754 and the function has the "signaling-nans"
+attribute, signaling NaNs are handled according to the specification. They
+undergo conversion to quiet NaNs in arithmetic operations, which raise the
+``Invalid`` exception. If the attribute is absent, signaling NaNs are handled in
+the same way as quiet NaNs. The attribute may be absent even on supporting
+hardware, as it can enable additional optimization opportunities. If the
+hardware does not support signaling NaNs, the "signaling-nans" attribute
+cannot be applied.
+
 .. _floatsem:
 
 Floating-Point Semantics
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index aa0b158c8a73b..97c8700109626 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -68,6 +68,12 @@ Makes programs 10x faster by doing Special New Thing.
   null pointers. Such constants may print as `splat (ptr null)` instead of
   `zeroinitializer`.
 
+* The function attribute "signaling-nans" was added to control the support of
+  signaling NaNs.
+
+Changes to LLVM infrastructure
+------------------------------
+
 * LLVM IR floating-point literals have greatly changed:
 
   * The old hexadecimal bitwise representation is deprecated and will be removed
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index b81b1dae27471..42427a475c283 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -126,6 +126,14 @@ struct SimplifyQuery {
   /// Otherwise always return false.
   LLVM_ABI bool isUndefValue(Value *V) const;
 
+  bool hasSignalingNaNs() const {
+    if (CxtI)
+      if (const BasicBlock *BB = CxtI->getParent())
+        if (const Function *F = BB->getParent())
+          return F->hasFnAttribute("signaling-nans");
+    return false;
+  }
+
   SimplifyQuery getWithoutDomCondCache() const {
     SimplifyQuery Copy(*this);
     Copy.DC = nullptr;
diff --git a/llvm/include/llvm/IR/FPEnv.h b/llvm/include/llvm/IR/FPEnv.h
index 38395b15c8c09..a3b7d1c26972d 100644
--- a/llvm/include/llvm/IR/FPEnv.h
+++ b/llvm/include/llvm/IR/FPEnv.h
@@ -80,11 +80,5 @@ LLVM_ABI Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr);
 inline bool canRoundingModeBe(RoundingMode RM, RoundingMode QRM) {
   return RM == QRM || RM == RoundingMode::Dynamic;
 }
-
-/// Returns true if the possibility of a signaling NaN can be safely
-/// ignored.
-inline bool canIgnoreSNaN(fp::ExceptionBehavior EB, FastMathFlags FMF) {
-  return (EB == fp::ebIgnore || FMF.noNaNs());
-}
 }
 #endif
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 4780c18bec421..6b02ce0c329b1 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -94,6 +94,10 @@ static Constant *getFalse(Type *Ty) { return ConstantInt::getFalse(Ty); }
 /// with every element true.
 static Constant *getTrue(Type *Ty) { return ConstantInt::getTrue(Ty); }
 
+inline bool canIgnoreSNaN(const SimplifyQuery &Q, FastMathFlags FMF) {
+  return !Q.hasSignalingNaNs() || FMF.noNaNs();
+}
+
 /// isSameCompare - Is V equivalent to the comparison "LHS Pred RHS"?
 static bool isSameCompare(Value *V, CmpPredicate Pred, Value *LHS, Value *RHS) {
   CmpInst *Cmp = dyn_cast<CmpInst>(V);
@@ -5935,14 +5939,14 @@ simplifyFAddInst(Value *Op0, Value *Op1, FastMathFlags FMF,
   // not simplify to Op0:
   // fadd SNaN, -0.0 --> QNaN
   // fadd +0.0, -0.0 --> -0.0 (but only with round toward negative)
-  if (canIgnoreSNaN(ExBehavior, FMF) &&
+  if (canIgnoreSNaN(Q, FMF) &&
       (!canRoundingModeBe(Rounding, RoundingMode::TowardNegative) ||
        FMF.noSignedZeros()))
     if (match(Op1, m_NegZeroFP()))
       return Op0;
 
   // fadd X, 0 ==> X, when we know X is not -0
-  if (canIgnoreSNaN(ExBehavior, FMF))
+  if (canIgnoreSNaN(Q, FMF))
     if (match(Op1, m_PosZeroFP()) &&
         (FMF.noSignedZeros() || cannotBeNegativeZero(Op0, Q)))
       return Op0;
@@ -5997,14 +6001,14 @@ simplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF,
     return C;
 
   // fsub X, +0 ==> X
-  if (canIgnoreSNaN(ExBehavior, FMF) &&
+  if (canIgnoreSNaN(Q, FMF) &&
       (!canRoundingModeBe(Rounding, RoundingMode::TowardNegative) ||
        FMF.noSignedZeros()))
     if (match(Op1, m_PosZeroFP()))
       return Op0;
 
   // fsub X, -0 ==> X, when we know X is not -0
-  if (canIgnoreSNaN(ExBehavior, FMF))
+  if (canIgnoreSNaN(Q, FMF))
     if (match(Op1, m_NegZeroFP()) &&
         (FMF.noSignedZeros() || cannotBeNegativeZero(Op0, Q)))
       return Op0;
@@ -6012,13 +6016,13 @@ simplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF,
   // fsub -0.0, (fsub -0.0, X) ==> X
   // fsub -0.0, (fneg X) ==> X
   Value *X;
-  if (canIgnoreSNaN(ExBehavior, FMF))
+  if (canIgnoreSNaN(Q, FMF))
     if (match(Op0, m_NegZeroFP()) && match(Op1, m_FNeg(m_Value(X))))
       return X;
 
   // fsub 0.0, (fsub 0.0, X) ==> X if signed zeros are ignored.
   // fsub 0.0, (fneg X) ==> X if signed zeros are ignored.
-  if (canIgnoreSNaN(ExBehavior, FMF))
+  if (canIgnoreSNaN(Q, FMF))
     if (FMF.noSignedZeros() && match(Op0, m_AnyZeroFP()) &&
         (match(Op1, m_FSub(m_AnyZeroFP(), m_Value(X))) ||
          match(Op1, m_FNeg(m_Value(X)))))
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
index bfc7c038322d4..d36fe0b6f86d0 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
@@ -11,6 +11,15 @@
 
 define float @fadd_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %ret
+}
+
+define float @fadd_x_n0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fadd_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -19,6 +28,15 @@ define float @fadd_x_n0_defaultenv(float %a) #0 {
 
 define <2 x float> @fadd_vec_x_n0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %ret
+}
+
+define <2 x float> @fadd_vec_x_n0_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fadd_vec_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -27,7 +45,7 @@ define <2 x float> @fadd_vec_x_n0_defaultenv(<2 x float> %a) #0 {
 
 define float @fadd_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_ebmaytrap(
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]]
 ; CHECK-NEXT:    ret float [[RET]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0
@@ -101,6 +119,15 @@ define <2 x float> @fadd_vec_x_n0_dynamic(<2 x float> %a) #0 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define float @fadd_x_n0_towardzero(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_towardzero(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
+  ret float %ret
+}
+
+define float @fadd_x_n0_towardzero_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fadd_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -111,6 +138,15 @@ define float @fadd_x_n0_towardzero(float %a) #0 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define <2 x float> @fadd_vec_x_n0_towardzero(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_towardzero(
+; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %ret
+}
+
+define <2 x float> @fadd_vec_x_n0_towardzero_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fadd_vec_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -173,6 +209,15 @@ define <2 x float> @fadd_vec_ninf_x_n0_ebstrict(<2 x float> %a) #0 {
 
 define float @fadd_n0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_n0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float -0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %ret
+}
+
+define float @fadd_n0_x_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fadd_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -181,6 +226,15 @@ define float @fadd_n0_x_defaultenv(float %a) #0 {
 
 define <2 x float> @fadd_vec_n0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_n0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> splat (float -0.000000e+00), <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %ret
+}
+
+define <2 x float> @fadd_vec_n0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fadd_vec_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -213,6 +267,15 @@ define <2 x float> @fadd_vec_n0_x_ebmaytrap(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_x_0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %add
+}
+
+define float @fold_fadd_nsz_x_0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -221,6 +284,15 @@ define float @fold_fadd_nsz_x_0_defaultenv(float %a) #0 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %add
+}
+
+define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -229,6 +301,15 @@ define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_x_0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
+  ret float %add
+}
+
+define float @fold_fadd_nsz_x_0_neginf_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -237,6 +318,15 @@ define float @fold_fadd_nsz_x_0_neginf(float %a) #0 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_neginf(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf(
+; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %add
+}
+
+define <2 x float> @fold_fadd_vec_nsz_x_0_neginf_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -315,6 +405,15 @@ define <2 x float> @fold_fadd_vec_nsz_nnan_x_0_ebstrict(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %add
+}
+
+define float @fold_fadd_nsz_0_x_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -323,6 +422,15 @@ define float @fold_fadd_nsz_0_x_defaultenv(float %a) #0 {
 
 define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %add
+}
+
+define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -459,4 +567,5 @@ define float @fold_fadd_snan_qnan_ebstrict() #0 {
 declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) #0
 declare <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float>, <2 x float>, metadata, metadata) #0
 
-attributes #0 = { strictfp }
+attributes #0 = { strictfp "signaling-nans" }
+attributes #1 = { strictfp }
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
index b55519e8374b6..195df793f90ab 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
@@ -11,6 +11,15 @@
 
 define float @fsub_x_p0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_x_p0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_x_p0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -27,6 +36,14 @@ define float @fsub_x_p0_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_x_p0_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_x_p0_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %ret
+}
+
 define float @fsub_nnan_x_p0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -45,6 +62,15 @@ define float @fsub_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_x_p0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_x_p0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_nnan_x_p0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebstrict(
@@ -65,6 +91,15 @@ define float @fsub_ninf_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_ninf_x_p0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_ninf_x_p0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[RET:%.*]] = call ninf float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call ninf float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; Round to -inf and if x is zero then the result is -0.0: must not fire
 define float @fsub_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_neginf(
@@ -89,6 +124,15 @@ define float @fsub_x_p0_dynamic(float %a) #0 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_neginf(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_nsz_x_p0_neginf_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_nsz_x_p0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
@@ -98,6 +142,15 @@ define float @fsub_nsz_x_p0_neginf(float %a) #0 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_dynamic(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_dynamic(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_nsz_x_p0_dynamic_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_nsz_x_p0_dynamic_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
@@ -111,6 +164,15 @@ define float @fsub_nsz_x_p0_dynamic(float %a) #0 {
 
 define float @fold_fsub_nsz_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %sub
+}
+
+define float @fold_fsub_nsz_x_n0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -127,6 +189,14 @@ define float @fold_fsub_nsz_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %sub
+}
+
 define float @fold_fsub_nnan_nsz_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nnan_nsz_x_n0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -145,6 +215,15 @@ define float @fold_fsub_nsz_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_nsz_x_n0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[SUB:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %sub
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_nnan_x_n0_ebstrict(
@@ -162,7 +241,18 @@ define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 
 define float @fold_fsub_fabs_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[ABSA1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA1]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[ABSA]]
+;
+  %absa = call float @llvm.fabs.f32(float %a) #0
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %sub
+}
+
+define float @fold_fsub_fabs_x_n0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv_nosignaling(
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
 ; CHECK-NEXT:    ret float [[ABSA]]
 ;
   %absa = call float @llvm.fabs.f32(float %a) #0
@@ -182,6 +272,16 @@ define float @fold_fsub_fabs_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
+; CHECK-NEXT:    ret float [[ABSA]]
+;
+  %absa = call float @llvm.fabs.f32(float %a) #0
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %sub
+}
+
 define float @fold_fsub_fabs_nnan_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebmaytrap(
 ; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
@@ -204,6 +304,17 @@ define float @fold_fsub_fabs_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_fabs_x_n0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
+; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[ABSA]]
+;
+  %absa = call float @llvm.fabs.f32(float %a) #0
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %sub
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebstrict(
@@ -219,6 +330,17 @@ define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 {
 ; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv(
 ; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[FPA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[SUB]]
+;
+  %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %fpa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %sub
+}
+
+define float @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(i32 %a) #1 {
+; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(
+; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
 ; CHECK-NEXT:    ret float [[FPA]]
 ;
   %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -232,6 +354,17 @@ define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 {
 
 define float @fsub_fneg_n0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_fneg_n0_fnX_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -251,6 +384,15 @@ define float @fsub_fneg_n0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %nega = fneg float %a
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %ret
+}
+
 define float @fsub_fneg_nnan_n0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -272,6 +414,17 @@ define float @fsub_fneg_n0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_n0_fnX_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_n0_fnX_ebstrict_nosignaling(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_n0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebstrict(
@@ -354,6 +507,17 @@ define float @fsub_fsub_nnan_n0_fnX_ebstrict(float %a) #0 {
 
 define float @fsub_fneg_nsz_p0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -373,6 +537,15 @@ define float @fsub_fneg_nsz_p0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %nega = fneg float %a
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %ret
+}
+
 define float @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -394,6 +567,17 @@ define float @fsub_fneg_nsz_p0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(
@@ -722,4 +906,5 @@ declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, me
 
 declare float @llvm.experimental.constrained.sitofp.f32.i32(i32, metadata, metadata)
 
-attributes #0 = { strictfp }
+attributes #0 = { strictfp "signaling-nans" }
+attributes #1 = { strictfp }

>From 03bd2fdb14dea97373cbfe82a1b6c929aa6ae09e Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Fri, 24 Apr 2026 20:04:56 +0700
Subject: [PATCH 02/10] Update according to the feedback

- Optimizations using the new attribute are removed.
- Use a separate Attribute kind rather than string.
- Implemented merging the attribute when inlining.
- Added some tests.
- Fixed documentation.
---
 clang/docs/ReleaseNotes.rst                   |   2 +-
 clang/docs/UsersManual.rst                    |  22 +-
 clang/include/clang/Basic/TargetInfo.h        |   3 +
 clang/lib/CodeGen/CodeGenFunction.cpp         |   7 +-
 clang/lib/Driver/ToolChains/Clang.cpp         |   2 +
 clang/test/Driver/fp-model.c                  |  14 ++
 clang/test/Preprocessor/predefined-macros.c   |   8 +
 llvm/docs/LangRef.rst                         |  18 +-
 llvm/include/llvm/Analysis/SimplifyQuery.h    |   8 -
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |   1 +
 llvm/include/llvm/IR/Attributes.td            |   6 +
 llvm/include/llvm/IR/FPEnv.h                  |   6 +
 llvm/include/llvm/IR/Function.h               |   5 +
 llvm/lib/Analysis/InstructionSimplify.cpp     |  16 +-
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |   2 +
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |   2 +
 llvm/lib/IR/Attributes.cpp                    |   5 +
 llvm/test/Bitcode/attributes.ll               |   7 +
 .../Inline/inline-signaling-nans.ll           |  47 +++++
 .../Transforms/InstSimplify/strictfp-fadd.ll  | 113 +----------
 .../Transforms/InstSimplify/strictfp-fsub.ll  | 189 +-----------------
 21 files changed, 145 insertions(+), 338 deletions(-)
 create mode 100644 llvm/test/Transforms/Inline/inline-signaling-nans.ll

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8e9d41d8b6121..794a66bc4bc25 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -308,7 +308,7 @@ New Compiler Flags
   reduced BMI only for a C++20 importable module unit. Previously the users
   can only generate the reduced BMI as a by-product, e.g, an object files or
   a full BMI.
-- New option ``-f[no-]signaling-nans`` added to control yhe support of
+- New option ``-f[no-]signaling-nans`` added to control the support of
   signaling NaNs.
 
 - New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 2e8530656a204..c549d7ea2e062 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1878,23 +1878,27 @@ for more details.
 
    Informs the compiler whether signaling NaNs behave according to IEEE 754.
 
-   IEEE 754 defines signaling NaNs (SNaNs) as a subset of Not-a-Numbers (NaNs),
-   which possesses following properties:
+   IEEE 754 defines signaling NaNs (sNaNs) as a subset of Not-a-Numbers (NaNs),
+   that possess the following properties:
 
-   * Floating-point operations, in which an SNaN is an operand, raise the
-     ``Invalid`` exception,
-   * Floating-point operations do not produce SNaNs, only quiet NaN can be a
-     result. Some target architectures do not support SNaNs; only a quiet NaN
-     can be a result.
+   * Floating-point operations in which an sNaN is an operand, raise the
+     ``Invalid`` exception.
+   * Floating-point operations do not produce sNaNs; only a quiet NaN can be a
+     result.
+
+   Some target architectures do not support sNaNs. On such a target, the
+   behavior of an sNaN is same as that of a quiet NaN: it can appear as a
+   result, and the "Invalid" exception is not raised when it is an operand in
+   an operation.
 
    The option ``-fsignaling-nans`` specifies IEEE 754 compliant behavior for
    signaling NaNs. It has no effect if the target architecture does not
-   implements IEEE 754 signaling NaN behavior. This option causes the
+   implement IEEE 754 signaling NaN behavior. This option causes the
    preprocessor macro ``__SUPPORT_SNAN__`` to be defined.
 
    The option ``-fno-signaling-nans`` specifies that signaling NaNs are treated
    in the same way as quiet NaNs. This is the only option allowed if the target
-   architecture does not implement signaling NaNs according to IEEE-754. On
+   architecture does not implement signaling NaNs according to IEEE-754. On the
    supporting architectures, it can enable additional optimization opportunities.
 
    If more than one option is specified, the last one takes effect. If none is
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 686c22ef4f156..e97ebf02c3f2c 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -744,6 +744,9 @@ class TargetInfo : public TransferrableTargetInfo,
   virtual bool hasStrictFP() const { return HasStrictFP; }
 
   /// Determine whether signaling NaNs are supported on this target.
+  ///
+  /// A target can override this method if the support of signaling NaNs depends
+  /// on processor features or any other target options.
   virtual bool hasSignalingNaNs() const { return HasSignalingNaNs; }
 
   /// Return the alignment that is the largest alignment ever used for any
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 8f4ef4f45548e..69f3585e0cfe4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1004,14 +1004,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
   if (D && D->hasAttr<HybridPatchableAttr>())
     Fn->addFnAttr(llvm::Attribute::HybridPatchable);
 
-  if (D) {
+  if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
     if (CGM.getCodeGenOpts().SignalingNans) {
-      if (getContext().getTargetInfo().hasSignalingNaNs())
-        Fn->addFnAttr("signaling-nans");
+      Fn->addFnAttr(llvm::Attribute::SignalingNans);
     } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
       // Both options are absent, - calculate default setting.
       if (D->hasAttr<StrictFPAttr>())
-        Fn->addFnAttr("signaling-nans");
+        Fn->addFnAttr(llvm::Attribute::SignalingNans);
     }
   }
 
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index ffe9180034a39..062df7296d7c1 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2953,6 +2953,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
         break;
       }
       StrictFPModel = false;
+      SignalingNaNs = false;
       if (!FPModel.empty() && FPModel != Val)
         D.Diag(clang::diag::warn_drv_overriding_option)
             << Args.MakeArgString("-ffp-model=" + FPModel)
@@ -2982,6 +2983,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
         LastFpContractOverrideOption = "-ffp-model=strict";
         TrappingMath = true;
         RoundingFPMath = true;
+        SignalingNaNs = true;
         setComplexRange(D, Args.MakeArgString(A->getSpelling() + Val),
                         LangOptions::ComplexRangeKind::CX_Full,
                         LastComplexRangeOption, Range);
diff --git a/clang/test/Driver/fp-model.c b/clang/test/Driver/fp-model.c
index 9cf5d0c53b24b..62e0d2515fc9b 100644
--- a/clang/test/Driver/fp-model.c
+++ b/clang/test/Driver/fp-model.c
@@ -127,6 +127,7 @@
 // CHECK-FPM-AGGR-SAME: "-freciprocal-math"
 // CHECK-FPM-AGGR-SAME: "-ffp-contract=fast"
 // CHECK-FPM-AGGR-SAME: "-fno-rounding-math"
+// CHECK-FPM-AGGR-SAME: "-fno-signaling-nans"
 // CHECK-FPM-AGGR-SAME: "-ffast-math"
 // CHECK-FPM-AGGR-SAME: "-ffinite-math-only"
 // CHECK-FPM-AGGR-SAME: "-complex-range=basic"
@@ -143,6 +144,7 @@
 // CHECK-FPM-FAST-SAME: "-freciprocal-math"
 // CHECK-FPM-FAST-SAME: "-ffp-contract=fast"
 // CHECK-FPM-FAST-SAME: "-fno-rounding-math"
+// CHECK-FPM-FAST-SAME: "-fno-signaling-nans"
 // CHECK-FPM-FAST-NOT: "-ffast-math"
 // CHECK-FPM-FAST-NOT: "-ffinite-math-only"
 // CHECK-FPM-FAST-SAME: "-complex-range=promoted"
@@ -152,12 +154,14 @@
 // CHECK-FPM-PRECISE: "-cc1"
 // CHECK-FPM-PRECISE-SAME: "-ffp-contract=on"
 // CHECK-FPM-PRECISE-SAME: "-fno-rounding-math"
+// CHECK-FPM-PRECISE-SAME: "-fno-signaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=strict -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefix=CHECK-FPM-STRICT %s
 // CHECK-FPM-STRICT: "-cc1"
 // CHECK-FPM-STRICT-SAME: "-frounding-math"
 // CHECK-FPM-STRICT-SAME: "-ffp-exception-behavior=strict"
+// CHECK-FPM-STRICT-SAME: "-fsignaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=strict -ffp-model=fast -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefix=CHECK-NO-EXCEPT %s
@@ -252,3 +256,13 @@
 // CHECK-FASTMATH-FPM-STRICT-NOT: "-ffast-math"
 // CHECK-FASTMATH-FPM-STRICT-NOT: "-ffinite-math-only"
 // CHECK-FASTMATH-FPM-STRICT-SAME: "-complex-range=full"
+
+// RUN: %clang -### -nostdinc -fsignaling-nans -ffp-model=precise -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefixes=CHECK-SNAN-FPM-PRECISE %s
+// CHECK-SNAN-FPM-PRECISE:      "-cc1"
+// CHECK-SNAN-FPM-PRECISE-SAME: "-fno-signaling-nans"
+
+// RUN: %clang -### -nostdinc -ffp-model=precise -fsignaling-nans -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefixes=CHECK-FPM-PRECISE-SNAN %s
+// CHECK-FPM-PRECISE-SNAN:      "-cc1"
+// CHECK-FPM-PRECISE-SNAN-SAME: "-fsignaling-nans"
diff --git a/clang/test/Preprocessor/predefined-macros.c b/clang/test/Preprocessor/predefined-macros.c
index ab85cfc00b42a..d85b3c566a6eb 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -334,3 +334,11 @@
 // CHECK-HIPSTDPAR-INTERPOSE-DEV-NEG: #define __HIPSTDPAR__ 1
 // CHECK-HIPSTDPAR-INTERPOSE-DEV-NEG-NOT: #define __HIPSTDPAR_INTERPOSE_ALLOC_V1__ 1
 // CHECK-HIPSTDPAR-INTERPOSE-DEV-NEG-NOT: #define __HIPSTDPAR_INTERPOSE_ALLOC__ 1
+
+// RUN: %clang_cc1 %s -E -dM -fsignaling-nans -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-SIGNALING-NANS
+// CHECK-SIGNALING-NANS: #define __SUPPORT_SNAN__
+
+// RUN: %clang_cc1 %s -E -dM -fno-signaling-nans -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-NO-SIGNALING-NANS
+// CHECK-NO-SIGNALING-NANS-NOT: __SUPPORT_SNAN__
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e5ebea118b1e3..36bf08e14e258 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2586,14 +2586,16 @@ For example:
 ``sanitize_alloc_token``
     This attribute indicates that implicit allocation token instrumentation
     is enabled for this function.
-``signaling-nans``
-    If a function has this attribute, signaling NaNs are assumed to be treated
-    according to IEEE 754 rules. That is, signaling NaN values are quieted in
-    arithmetic operations, and the floating-point exception ``Invalid`` is
-    raised if an operand of such an operation is a signaling NaN. If this
-    attribute is absent, signaling NaNs are assumed to be treated identically to
-    quiet NaNs. This attribute cannot be set if the target architecture does not
-    support IEEE 754 compatible signaling NaN handling.
+``signaling_nans``
+    If a function has this attribute, a signaling NaN (sNaN) is assumed to be
+    treated according to IEEE 754 rules. That is, arithmetic operations never
+    return an sNaN, and the ``Invalid``  exception  is raised if an operand of
+    such an operation is an sNaN. If this attribute is absent, an sNaN is assumed
+    to be treated identically to a quiet NaN: it can be produced by an operation
+    (so that the transformation `x + 0 -> x` becomes valid), and an operation on
+    such a NaN does not raise the "Invalid" exception. This attribute cannot be
+    set if the target architecture does not support IEEE 754 compatible
+    sNaN handling.
 ``speculative_load_hardening``
     This attribute indicates that
     `Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index 42427a475c283..b81b1dae27471 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -126,14 +126,6 @@ struct SimplifyQuery {
   /// Otherwise always return false.
   LLVM_ABI bool isUndefValue(Value *V) const;
 
-  bool hasSignalingNaNs() const {
-    if (CxtI)
-      if (const BasicBlock *BB = CxtI->getParent())
-        if (const Function *F = BB->getParent())
-          return F->hasFnAttribute("signaling-nans");
-    return false;
-  }
-
   SimplifyQuery getWithoutDomCondCache() const {
     SimplifyQuery Copy(*this);
     Copy.DC = nullptr;
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 95787c595dff7..93ca44c585177 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -820,6 +820,7 @@ enum AttributeKindCodes {
   ATTR_KIND_DENORMAL_FPENV = 106,
   ATTR_KIND_NOOUTLINE = 107,
   ATTR_KIND_FLATTEN = 108,
+  ATTR_KIND_SIGNALING_NANS = 109,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 34032e341d85e..6284faf530456 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -420,6 +420,11 @@ def MarkedForWindowsSecureHotPatching
 def AllowDirectAccessInHotPatchFunction
     : StrBoolAttr<"allow_direct_access_in_hot_patch_function">;
 
+/// Signaling NaNs should be handled according to IEEE 754 semantics: they
+/// cannot be the result of a floating-point operation, and using them as
+/// operands raises an "Invalid" exception.
+def SignalingNans : EnumAttr<"signaling_nans", IntersectCustom, [FnAttr]>;
+
 /// Target-independent string attributes.
 def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">;
 def NoSignedZerosFPMath : StrBoolAttr<"no-signed-zeros-fp-math">;
@@ -491,3 +496,4 @@ def : MergeRule<"adjustCallerStackProbeSize">;
 def : MergeRule<"adjustMinLegalVectorWidth">;
 def : MergeRule<"adjustNullPointerValidAttr">;
 def : MergeRule<"setAND<MustProgressAttr>">;
+def : MergeRule<"setOR<SignalingNansAttr>">;
diff --git a/llvm/include/llvm/IR/FPEnv.h b/llvm/include/llvm/IR/FPEnv.h
index a3b7d1c26972d..38395b15c8c09 100644
--- a/llvm/include/llvm/IR/FPEnv.h
+++ b/llvm/include/llvm/IR/FPEnv.h
@@ -80,5 +80,11 @@ LLVM_ABI Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr);
 inline bool canRoundingModeBe(RoundingMode RM, RoundingMode QRM) {
   return RM == QRM || RM == RoundingMode::Dynamic;
 }
+
+/// Returns true if the possibility of a signaling NaN can be safely
+/// ignored.
+inline bool canIgnoreSNaN(fp::ExceptionBehavior EB, FastMathFlags FMF) {
+  return (EB == fp::ebIgnore || FMF.noNaNs());
+}
 }
 #endif
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index f39fe509a49a4..84fcf628cc8ce 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -722,6 +722,11 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
   /// Return the representational value of the denormal_fpenv attribute.
   DenormalFPEnv getDenormalFPEnv() const;
 
+  /// Determine if signaling NaNs are supported in this function.
+  bool hasSignalingNans() const {
+    return hasFnAttribute(Attribute::SignalingNans);
+  }
+
   /// copyAttributesFrom - copy all additional attributes (those not needed to
   /// create a Function) from the Function Src to this one.
   void copyAttributesFrom(const Function *Src);
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 6b02ce0c329b1..4780c18bec421 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -94,10 +94,6 @@ static Constant *getFalse(Type *Ty) { return ConstantInt::getFalse(Ty); }
 /// with every element true.
 static Constant *getTrue(Type *Ty) { return ConstantInt::getTrue(Ty); }
 
-inline bool canIgnoreSNaN(const SimplifyQuery &Q, FastMathFlags FMF) {
-  return !Q.hasSignalingNaNs() || FMF.noNaNs();
-}
-
 /// isSameCompare - Is V equivalent to the comparison "LHS Pred RHS"?
 static bool isSameCompare(Value *V, CmpPredicate Pred, Value *LHS, Value *RHS) {
   CmpInst *Cmp = dyn_cast<CmpInst>(V);
@@ -5939,14 +5935,14 @@ simplifyFAddInst(Value *Op0, Value *Op1, FastMathFlags FMF,
   // not simplify to Op0:
   // fadd SNaN, -0.0 --> QNaN
   // fadd +0.0, -0.0 --> -0.0 (but only with round toward negative)
-  if (canIgnoreSNaN(Q, FMF) &&
+  if (canIgnoreSNaN(ExBehavior, FMF) &&
       (!canRoundingModeBe(Rounding, RoundingMode::TowardNegative) ||
        FMF.noSignedZeros()))
     if (match(Op1, m_NegZeroFP()))
       return Op0;
 
   // fadd X, 0 ==> X, when we know X is not -0
-  if (canIgnoreSNaN(Q, FMF))
+  if (canIgnoreSNaN(ExBehavior, FMF))
     if (match(Op1, m_PosZeroFP()) &&
         (FMF.noSignedZeros() || cannotBeNegativeZero(Op0, Q)))
       return Op0;
@@ -6001,14 +5997,14 @@ simplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF,
     return C;
 
   // fsub X, +0 ==> X
-  if (canIgnoreSNaN(Q, FMF) &&
+  if (canIgnoreSNaN(ExBehavior, FMF) &&
       (!canRoundingModeBe(Rounding, RoundingMode::TowardNegative) ||
        FMF.noSignedZeros()))
     if (match(Op1, m_PosZeroFP()))
       return Op0;
 
   // fsub X, -0 ==> X, when we know X is not -0
-  if (canIgnoreSNaN(Q, FMF))
+  if (canIgnoreSNaN(ExBehavior, FMF))
     if (match(Op1, m_NegZeroFP()) &&
         (FMF.noSignedZeros() || cannotBeNegativeZero(Op0, Q)))
       return Op0;
@@ -6016,13 +6012,13 @@ simplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF,
   // fsub -0.0, (fsub -0.0, X) ==> X
   // fsub -0.0, (fneg X) ==> X
   Value *X;
-  if (canIgnoreSNaN(Q, FMF))
+  if (canIgnoreSNaN(ExBehavior, FMF))
     if (match(Op0, m_NegZeroFP()) && match(Op1, m_FNeg(m_Value(X))))
       return X;
 
   // fsub 0.0, (fsub 0.0, X) ==> X if signed zeros are ignored.
   // fsub 0.0, (fneg X) ==> X if signed zeros are ignored.
-  if (canIgnoreSNaN(Q, FMF))
+  if (canIgnoreSNaN(ExBehavior, FMF))
     if (FMF.noSignedZeros() && match(Op0, m_AnyZeroFP()) &&
         (match(Op1, m_FSub(m_AnyZeroFP(), m_Value(X))) ||
          match(Op1, m_FNeg(m_Value(X)))))
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 3e863f4786e1a..2abc72a9e3094 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2302,6 +2302,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::DenormalFPEnv;
   case bitc::ATTR_KIND_NOOUTLINE:
     return Attribute::NoOutline;
+  case bitc::ATTR_KIND_SIGNALING_NANS:
+    return Attribute::SignalingNans;
   }
 }
 
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index ed7f95701ea65..9b7ae90be825c 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1008,6 +1008,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_DENORMAL_FPENV;
   case Attribute::NoOutline:
     return bitc::ATTR_KIND_NOOUTLINE;
+  case Attribute::SignalingNans:
+    return bitc::ATTR_KIND_SIGNALING_NANS;
   case Attribute::EndAttrKinds:
     llvm_unreachable("Can not encode end-attribute kinds marker.");
   case Attribute::None:
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 4087b25951a1c..9920b891e3593 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -1150,6 +1150,11 @@ AttributeSet::intersectWith(LLVMContext &C, AttributeSet Other) const {
         if (!NewRange.isFullSet())
           Intersected.addRangeAttr(NewRange);
       } break;
+      case Attribute::SignalingNans:
+        if (Attr0.hasAttribute(Attribute::SignalingNans) ||
+            Attr1.hasAttribute(Attribute::SignalingNans))
+          Intersected.addAttribute(Attribute::SignalingNans);
+        break;
       default:
         llvm_unreachable("Unknown attribute with custom intersection rule");
       }
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index 21712fae7eecd..362ce160e4e26 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -557,6 +557,12 @@ define void @f94() nodivergencesource {
   ret void;
 }
 
+; CHECK: define float @f95(float %x) [[SIGNALING_NANS:#[0-9]+]]
+define float @f95(float %x) signaling_nans {
+  %r = fadd float %x, 2.0
+  ret float %r
+}
+
 ; CHECK: define range(i32 -1, 42) i32 @range_attribute(<4 x i32> range(i32 -1, 42) %a)
 define range(i32 -1, 42) i32 @range_attribute(<4 x i32> range(i32 -1, 42) %a) {
   ret i32 0
@@ -654,4 +660,5 @@ define void @dead_on_return_sized(ptr dead_on_return(4) %p) {
 ; CHECK: attributes [[SKIPPROFILE]] = { skipprofile }
 ; CHECK: attributes [[OPTDEBUG]] = { optdebug }
 ; CHECK: attributes [[NODIVERGENCESOURCE]] = { nodivergencesource }
+; CHECK: attributes [[SIGNALING_NANS]] = { signaling_nans }
 ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }
diff --git a/llvm/test/Transforms/Inline/inline-signaling-nans.ll b/llvm/test/Transforms/Inline/inline-signaling-nans.ll
new file mode 100644
index 0000000000000..c36f1a0710f64
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline-signaling-nans.ll
@@ -0,0 +1,47 @@
+; RUN: opt < %s -passes=inline -S | FileCheck %s
+
+; Check the inliner combines signaling_nans attribute so that the calling
+; function has this attribute if it or the called functions have it.
+ 
+define internal float @not_signaling(float %x, float %y) #0 {
+  %sum = fadd float %x, %y
+  ret float %sum
+}
+
+define internal float @signaling(float %x, float %y) #1 {
+  %mul = fmul float %x, %y
+  ret float %mul
+}
+
+define float @all_not_signaling(float %x, float %y, float %z) #0 {
+  %v = call float @not_signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @all_not_signaling({{.*}}) [[NOT_SIGNALING:#[0-9]+]] {
+
+define float @all_signaling(float %x, float %y, float %z) #1 {
+  %v = call float @signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @all_signaling({{.*}}) [[SIGNALING:#[0-9]+]] {
+
+define float @to_not_signaling(float %x, float %y, float %z) #0 {
+  %v = call float @signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @to_not_signaling({{.*}}) [[SIGNALING:#[0-9]+]] {
+
+define float @to_signaling(float %x, float %y, float %z) #1 {
+  %v = call float @not_signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @to_signaling({{.*}}) [[SIGNALING:#[0-9]+]] {
+
+attributes #0 = { nounwind }
+; CHECK: attributes [[NOT_SIGNALING]] = { nounwind }
+attributes #1 = { nounwind signaling_nans }
+; CHECK: attributes [[SIGNALING]] = { nounwind signaling_nans }
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
index d36fe0b6f86d0..bfc7c038322d4 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
@@ -11,15 +11,6 @@
 
 define float @fadd_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %ret
-}
-
-define float @fadd_x_n0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fadd_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -28,15 +19,6 @@ define float @fadd_x_n0_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fadd_vec_x_n0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %ret
-}
-
-define <2 x float> @fadd_vec_x_n0_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fadd_vec_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -45,7 +27,7 @@ define <2 x float> @fadd_vec_x_n0_defaultenv_nosignaling(<2 x float> %a) #1 {
 
 define float @fadd_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_ebmaytrap(
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret float [[RET]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0
@@ -119,15 +101,6 @@ define <2 x float> @fadd_vec_x_n0_dynamic(<2 x float> %a) #0 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define float @fadd_x_n0_towardzero(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_towardzero(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
-  ret float %ret
-}
-
-define float @fadd_x_n0_towardzero_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fadd_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -138,15 +111,6 @@ define float @fadd_x_n0_towardzero_nosignaling(float %a) #1 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define <2 x float> @fadd_vec_x_n0_towardzero(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_towardzero(
-; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %ret
-}
-
-define <2 x float> @fadd_vec_x_n0_towardzero_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fadd_vec_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -209,15 +173,6 @@ define <2 x float> @fadd_vec_ninf_x_n0_ebstrict(<2 x float> %a) #0 {
 
 define float @fadd_n0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_n0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float -0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %ret
-}
-
-define float @fadd_n0_x_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fadd_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -226,15 +181,6 @@ define float @fadd_n0_x_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fadd_vec_n0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_n0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> splat (float -0.000000e+00), <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %ret
-}
-
-define <2 x float> @fadd_vec_n0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fadd_vec_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -267,15 +213,6 @@ define <2 x float> @fadd_vec_n0_x_ebmaytrap(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_x_0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %add
-}
-
-define float @fold_fadd_nsz_x_0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -284,15 +221,6 @@ define float @fold_fadd_nsz_x_0_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %add
-}
-
-define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -301,15 +229,6 @@ define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(<2 x float> %a)
 
 define float @fold_fadd_nsz_x_0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
-  ret float %add
-}
-
-define float @fold_fadd_nsz_x_0_neginf_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -318,15 +237,6 @@ define float @fold_fadd_nsz_x_0_neginf_nosignaling(float %a) #1 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_neginf(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf(
-; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %add
-}
-
-define <2 x float> @fold_fadd_vec_nsz_x_0_neginf_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -405,15 +315,6 @@ define <2 x float> @fold_fadd_vec_nsz_nnan_x_0_ebstrict(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %add
-}
-
-define float @fold_fadd_nsz_0_x_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -422,15 +323,6 @@ define float @fold_fadd_nsz_0_x_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %add
-}
-
-define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -567,5 +459,4 @@ define float @fold_fadd_snan_qnan_ebstrict() #0 {
 declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) #0
 declare <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float>, <2 x float>, metadata, metadata) #0
 
-attributes #0 = { strictfp "signaling-nans" }
-attributes #1 = { strictfp }
+attributes #0 = { strictfp }
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
index 195df793f90ab..b55519e8374b6 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
@@ -11,15 +11,6 @@
 
 define float @fsub_x_p0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_x_p0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_x_p0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -36,14 +27,6 @@ define float @fsub_x_p0_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_x_p0_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_x_p0_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %ret
-}
-
 define float @fsub_nnan_x_p0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -62,15 +45,6 @@ define float @fsub_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_x_p0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_x_p0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_nnan_x_p0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebstrict(
@@ -91,15 +65,6 @@ define float @fsub_ninf_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_ninf_x_p0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_ninf_x_p0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[RET:%.*]] = call ninf float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call ninf float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; Round to -inf and if x is zero then the result is -0.0: must not fire
 define float @fsub_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_neginf(
@@ -124,15 +89,6 @@ define float @fsub_x_p0_dynamic(float %a) #0 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_neginf(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_nsz_x_p0_neginf_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_nsz_x_p0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
@@ -142,15 +98,6 @@ define float @fsub_nsz_x_p0_neginf_nosignaling(float %a) #1 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_dynamic(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_dynamic(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_nsz_x_p0_dynamic_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_nsz_x_p0_dynamic_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
@@ -164,15 +111,6 @@ define float @fsub_nsz_x_p0_dynamic_nosignaling(float %a) #1 {
 
 define float @fold_fsub_nsz_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %sub
-}
-
-define float @fold_fsub_nsz_x_n0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -189,14 +127,6 @@ define float @fold_fsub_nsz_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %sub
-}
-
 define float @fold_fsub_nnan_nsz_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nnan_nsz_x_n0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -215,15 +145,6 @@ define float @fold_fsub_nsz_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_nsz_x_n0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[SUB:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %sub
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_nnan_x_n0_ebstrict(
@@ -241,18 +162,7 @@ define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 
 define float @fold_fsub_fabs_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv(
-; CHECK-NEXT:    [[ABSA1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA1]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[ABSA]]
-;
-  %absa = call float @llvm.fabs.f32(float %a) #0
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %sub
-}
-
-define float @fold_fsub_fabs_x_n0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv_nosignaling(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret float [[ABSA]]
 ;
   %absa = call float @llvm.fabs.f32(float %a) #0
@@ -272,16 +182,6 @@ define float @fold_fsub_fabs_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
-; CHECK-NEXT:    ret float [[ABSA]]
-;
-  %absa = call float @llvm.fabs.f32(float %a) #0
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %sub
-}
-
 define float @fold_fsub_fabs_nnan_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebmaytrap(
 ; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
@@ -304,17 +204,6 @@ define float @fold_fsub_fabs_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_fabs_x_n0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
-; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[ABSA]]
-;
-  %absa = call float @llvm.fabs.f32(float %a) #0
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %sub
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebstrict(
@@ -330,17 +219,6 @@ define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 {
 ; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv(
 ; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[FPA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[SUB]]
-;
-  %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %fpa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %sub
-}
-
-define float @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(i32 %a) #1 {
-; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(
-; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
 ; CHECK-NEXT:    ret float [[FPA]]
 ;
   %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -354,17 +232,6 @@ define float @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(i32 %a) #1 {
 
 define float @fsub_fneg_n0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_fneg_n0_fnX_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -384,15 +251,6 @@ define float @fsub_fneg_n0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %nega = fneg float %a
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %ret
-}
-
 define float @fsub_fneg_nnan_n0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -414,17 +272,6 @@ define float @fsub_fneg_n0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_n0_fnX_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_n0_fnX_ebstrict_nosignaling(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_n0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebstrict(
@@ -507,17 +354,6 @@ define float @fsub_fsub_nnan_n0_fnX_ebstrict(float %a) #0 {
 
 define float @fsub_fneg_nsz_p0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -537,15 +373,6 @@ define float @fsub_fneg_nsz_p0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %nega = fneg float %a
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %ret
-}
-
 define float @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -567,17 +394,6 @@ define float @fsub_fneg_nsz_p0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(
@@ -906,5 +722,4 @@ declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, me
 
 declare float @llvm.experimental.constrained.sitofp.f32.i32(i32, metadata, metadata)
 
-attributes #0 = { strictfp "signaling-nans" }
-attributes #1 = { strictfp }
+attributes #0 = { strictfp }

>From 2155c0c146f4eb207df5c83413cc74a6f468f732 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 27 Apr 2026 23:08:31 +0700
Subject: [PATCH 03/10] Fix tests and documentation

Also changes intersect behavior to IntersectPreserve, because
IntersectCustom is implemented only forIntAttr.
---
 clang/test/CodeGen/attr-signaling-nans.c             |  4 ++--
 clang/test/CodeGen/fp-floatcontrol-stack.cpp         | 12 ++++++------
 .../cl20-device-side-enqueue-attributes.cl           |  2 +-
 clang/test/Options/Gis.hlsl                          |  2 +-
 llvm/docs/LangRef.rst                                | 12 ++++++------
 llvm/include/llvm/IR/Attributes.td                   |  3 ++-
 llvm/lib/IR/Attributes.cpp                           |  5 -----
 7 files changed, 18 insertions(+), 22 deletions(-)

diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index eef64875f04a6..2cfbfc5b9b6f5 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -10,5 +10,5 @@ float func_01(float x) {
 // CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
 
 
-// SIGNALING: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
-// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
+// SIGNALING: attributes #[[ATTR]] = {{{.*}} signaling_nans {{.*}}}
+// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} signaling_nans {{.*}}}
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 237c9d4f9a37e..098bd9a73eaa1 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
   (float z) { return n * z + n; }
 
 // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
 // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
 #pragma float_control(except, on)
 #endif
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     float exc_on FUN(2)
 //CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
 #if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
 
 #pragma float_control(pop)
     // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     float exc_pop FUN(5)
@@ -76,7 +76,7 @@ float fun_default FUN(1)
 
 #pragma float_control(except, off)
         float exc_off FUN(5)
-//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}
+//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}{
 #if DEFAULT
 //CHECK-DDEFAULT: call float @llvm.fmuladd{{.*}}
 #endif
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index e42105b699e39..a05327e1aa322 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
 //.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "signaling-nans" "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp signaling_nans "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Options/Gis.hlsl b/clang/test/Options/Gis.hlsl
index c60fd1854bdc9..4191236889d39 100644
--- a/clang/test/Options/Gis.hlsl
+++ b/clang/test/Options/Gis.hlsl
@@ -1,7 +1,7 @@
 // RUN: %clang_dxc -T lib_6_4 -Gis %s 2>&1 -### | FileCheck -check-prefix=Gis %s
 // RUN: %clang_dxc -T lib_6_4 %s 2>&1 -### | FileCheck -check-prefix=NO_Gis %s
 
-// Gis: "-ffp-contract=off" "-frounding-math" "-ffp-exception-behavior=strict" "-complex-range=full"
+// Gis: "-ffp-contract=off" "-frounding-math" "-ffp-exception-behavior=strict" "-fsignaling-nans" "-complex-range=full"
 // assert expected floating point options are present
 // NO_Gis-NOT: "-ffp-contract=off" 
 // NO_Gis-NOT: "-frounding-math" 
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 36bf08e14e258..8cf4a1f4f386b 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2590,12 +2590,12 @@ For example:
     If a function has this attribute, a signaling NaN (sNaN) is assumed to be
     treated according to IEEE 754 rules. That is, arithmetic operations never
     return an sNaN, and the ``Invalid``  exception  is raised if an operand of
-    such an operation is an sNaN. If this attribute is absent, an sNaN is assumed
-    to be treated identically to a quiet NaN: it can be produced by an operation
-    (so that the transformation `x + 0 -> x` becomes valid), and an operation on
-    such a NaN does not raise the "Invalid" exception. This attribute cannot be
-    set if the target architecture does not support IEEE 754 compatible
-    sNaN handling.
+    such an operation is an sNaN and strictfp is also enabled. If this attribute
+    is absent, an sNaN is assumed to be treated identically to a quiet NaN: it
+    can be produced by an operation (so that the transformation `x + 0 -> x`
+    becomes valid), and an operation on such a NaN does not raise the "Invalid"
+    exception. This attribute cannot be set if the target architecture does not
+    support IEEE 754 compatible sNaN handling.
 ``speculative_load_hardening``
     This attribute indicates that
     `Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 6284faf530456..9e347a6c53ef2 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -423,7 +423,8 @@ def AllowDirectAccessInHotPatchFunction
 /// Signaling NaNs should be handled according to IEEE 754 semantics: they
 /// cannot be the result of a floating-point operation, and using them as
 /// operands raises an "Invalid" exception.
-def SignalingNans : EnumAttr<"signaling_nans", IntersectCustom, [FnAttr]>;
+/// NB: Could be IntersectCustom with "or" handling.
+def SignalingNans : EnumAttr<"signaling_nans", IntersectPreserve, [FnAttr]>;
 
 /// Target-independent string attributes.
 def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">;
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 9920b891e3593..4087b25951a1c 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -1150,11 +1150,6 @@ AttributeSet::intersectWith(LLVMContext &C, AttributeSet Other) const {
         if (!NewRange.isFullSet())
           Intersected.addRangeAttr(NewRange);
       } break;
-      case Attribute::SignalingNans:
-        if (Attr0.hasAttribute(Attribute::SignalingNans) ||
-            Attr1.hasAttribute(Attribute::SignalingNans))
-          Intersected.addAttribute(Attribute::SignalingNans);
-        break;
       default:
         llvm_unreachable("Unknown attribute with custom intersection rule");
       }

>From 1c9601c52e8f396d024e4c3032047dce87b288b8 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Sat, 16 May 2026 01:15:54 +0700
Subject: [PATCH 04/10] Remove default setting signaling_nan attribute for
 strictfp functions

We cannot describe such behavior in Clang User Manual, strictfp is not
visible for a user.
---
 clang/docs/ReleaseNotes.rst                     |  3 +++
 clang/docs/UsersManual.rst                      |  3 +--
 clang/include/clang/Basic/CodeGenOptions.def    |  5 +----
 clang/include/clang/Options/Options.td          | 17 ++++++++---------
 clang/lib/CodeGen/CodeGenFunction.cpp           |  7 +------
 clang/lib/Driver/ToolChains/Clang.cpp           | 12 ++++--------
 clang/test/CodeGen/attr-signaling-nans.c        |  2 +-
 clang/test/CodeGen/fp-floatcontrol-stack.cpp    | 10 +++++-----
 .../cl20-device-side-enqueue-attributes.cl      |  2 +-
 clang/test/Driver/clang_f_opts.c                |  3 ---
 clang/test/Driver/fp-model.c                    |  8 ++++----
 clang/test/Preprocessor/predefined-macros.c     |  2 +-
 12 files changed, 30 insertions(+), 44 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 794a66bc4bc25..821a5142788b7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -71,6 +71,9 @@ C/C++ Language Potentially Breaking Changes
   Clang would previously ``break`` out of the ``while`` loop, whereas GCC (since version 9) would
   ``break`` out of the ``for`` loop here. Now, Clang and GCC both break out of the ``for`` loop.
 
+- Clang now requires the option ``-fsignaling-nans`` whenever support for
+  signaling NaNs is required.
+
 C++ Specific Potentially Breaking Changes
 -----------------------------------------
 
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index c549d7ea2e062..35e6f79c2c31e 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1902,8 +1902,7 @@ for more details.
    supporting architectures, it can enable additional optimization opportunities.
 
    If more than one option is specified, the last one takes effect. If none is
-   specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
-   compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
+   specified, the compiler assumes ``-fno-signaling-nans``.
 
 .. option:: -ffp-model=<value>
 
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index df2463227aacb..b8e33d9ca04d3 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -532,11 +532,8 @@ ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
 /// Adds attributes that prevent outlining (`-mno-outline`)
 CODEGENOPT(DisableOutlining, 1, 0, Benign)
 
-/// Whether compiler should treat signaling NaNs according to IEEE 754. Use
-/// separate options for positive and negative settings, this allows us to
-/// represent "unspecified" setting.
+/// Whether compiler should treat signaling NaNs according to IEEE 754.
 CODEGENOPT(SignalingNans, 1, 0, Benign)
-CODEGENOPT(NoSignalingNans, 1, 0, Benign)
 
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 84fbc2f8b6709..0a702d2be1103 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3002,15 +3002,14 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   " Default is 'fast' for CUDA, 'fast-honor-pragmas' for HIP, and 'on' otherwise.">,
   HelpText<"Form fused FP ops (e.g. FMAs)">,
   Values<"fast,on,off,fast-honor-pragmas">;
-def fsignaling_nans : Flag<["-"], "fsignaling-nans">,
-  Visibility<[ClangOption, CC1Option]>,
-  MarshallingInfoFlag<CodeGenOpts<"SignalingNans">>,
-  HelpText<"Assume signaling NaNs behave according to IEEE rules and can"
-           "raise floating-point exceptions">;
-def fno_signaling_nans : Flag<["-"], "fno-signaling-nans">,
-  Visibility<[ClangOption, CC1Option]>,
-  MarshallingInfoFlag<CodeGenOpts<"NoSignalingNans">>,
-  HelpText<"Assume signaling NaNs are treated identically to quiet NaNs">;
+defm signaling_nans
+    : BoolFOption<"signaling-nans", CodeGenOpts<"SignalingNans">, DefaultFalse,
+                  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+                          "Assume signaling NaNs behave according to IEEE rules "
+                          "and can raise floating-point exceptions">,
+                  NegFlag<SetFalse, [], [ClangOption],
+                          "Assume signaling NaNs are treated identically to "
+                          "quiet NaNs">>;
 
 defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
   CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 69f3585e0cfe4..92d305e209664 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1005,13 +1005,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
     Fn->addFnAttr(llvm::Attribute::HybridPatchable);
 
   if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
-    if (CGM.getCodeGenOpts().SignalingNans) {
+    if (CGM.getCodeGenOpts().SignalingNans)
       Fn->addFnAttr(llvm::Attribute::SignalingNans);
-    } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
-      // Both options are absent, - calculate default setting.
-      if (D->hasAttr<StrictFPAttr>())
-        Fn->addFnAttr(llvm::Attribute::SignalingNans);
-    }
   }
 
   if (D) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 062df7296d7c1..3b027d2308417 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2802,7 +2802,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   bool TrappingMathPresent = false; // Is trapping-math in args, and not
                                     // overriden by ffp-exception-behavior?
   bool RoundingFPMath = false;
-  std::optional<bool> SignalingNaNs;
+  bool SignalingNaNs = false;
   // -ffp-model values: strict, fast, precise
   StringRef FPModel = "";
   // -ffp-exception-behavior options: strict, maytrap, ignore
@@ -3316,12 +3316,8 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   if (!BFloat16ExcessPrecision.empty())
     CmdArgs.push_back(Args.MakeArgString("-fbfloat16-excess-precision=" +
                                          BFloat16ExcessPrecision));
-  if (SignalingNaNs) {
-    if (*SignalingNaNs)
-      CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
-    else
-      CmdArgs.push_back(Args.MakeArgString("-fno-signaling-nans"));
-  }
+  if (SignalingNaNs)
+    CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
 
   StringRef Recip = parseMRecipOption(D.getDiags(), Args);
   if (!Recip.empty())
@@ -3332,7 +3328,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   // that's consistent with gcc's behaviour.
   if (!HonorINFs && !HonorNaNs && !MathErrno && AssociativeMath && ApproxFunc &&
       ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath &&
-      (SignalingNaNs.has_value() && !SignalingNaNs.value()))
+      !SignalingNaNs)
     CmdArgs.push_back("-ffast-math");
 
   // Handle __FINITE_MATH_ONLY__ similarly.
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index 2cfbfc5b9b6f5..c2715f20a88d0 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// Here msp430 is used as a target that do not support signaling NaNs.
 // RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 
 float func_01(float x) {
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 098bd9a73eaa1..5bd7409f4b933 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
   (float z) { return n * z + n; }
 
 // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
 // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
 #pragma float_control(except, on)
 #endif
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
-    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
     float exc_on FUN(2)
 //CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
 #if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
 
 #pragma float_control(pop)
     // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     float exc_pop FUN(5)
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index a05327e1aa322..962ac4afa4991 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
 //.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp signaling_nans "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index eb78ddf84d6b7..cd843e8d8f7b9 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -654,16 +654,13 @@
 // RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
 // CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
 // CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
-// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
 // CHECK-SIGNALING-NANS: "-cc1"
 // CHECK-SIGNALING-NANS: "-fsignaling-nans"
-// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
 // CHECK-NO-SIGNALING-NANS: "-cc1"
-// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
 // CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fp-model.c b/clang/test/Driver/fp-model.c
index 62e0d2515fc9b..0951aae556170 100644
--- a/clang/test/Driver/fp-model.c
+++ b/clang/test/Driver/fp-model.c
@@ -127,7 +127,7 @@
 // CHECK-FPM-AGGR-SAME: "-freciprocal-math"
 // CHECK-FPM-AGGR-SAME: "-ffp-contract=fast"
 // CHECK-FPM-AGGR-SAME: "-fno-rounding-math"
-// CHECK-FPM-AGGR-SAME: "-fno-signaling-nans"
+// CHECK-FPM-AGGR-NOT: "-fsignaling-nans"
 // CHECK-FPM-AGGR-SAME: "-ffast-math"
 // CHECK-FPM-AGGR-SAME: "-ffinite-math-only"
 // CHECK-FPM-AGGR-SAME: "-complex-range=basic"
@@ -144,7 +144,7 @@
 // CHECK-FPM-FAST-SAME: "-freciprocal-math"
 // CHECK-FPM-FAST-SAME: "-ffp-contract=fast"
 // CHECK-FPM-FAST-SAME: "-fno-rounding-math"
-// CHECK-FPM-FAST-SAME: "-fno-signaling-nans"
+// CHECK-FPM-FAST-NOT: "-fsignaling-nans"
 // CHECK-FPM-FAST-NOT: "-ffast-math"
 // CHECK-FPM-FAST-NOT: "-ffinite-math-only"
 // CHECK-FPM-FAST-SAME: "-complex-range=promoted"
@@ -154,7 +154,7 @@
 // CHECK-FPM-PRECISE: "-cc1"
 // CHECK-FPM-PRECISE-SAME: "-ffp-contract=on"
 // CHECK-FPM-PRECISE-SAME: "-fno-rounding-math"
-// CHECK-FPM-PRECISE-SAME: "-fno-signaling-nans"
+// CHECK-FPM-PRECISE-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=strict -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefix=CHECK-FPM-STRICT %s
@@ -260,7 +260,7 @@
 // RUN: %clang -### -nostdinc -fsignaling-nans -ffp-model=precise -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-SNAN-FPM-PRECISE %s
 // CHECK-SNAN-FPM-PRECISE:      "-cc1"
-// CHECK-SNAN-FPM-PRECISE-SAME: "-fno-signaling-nans"
+// CHECK-SNAN-FPM-PRECISE-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=precise -fsignaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-FPM-PRECISE-SNAN %s
diff --git a/clang/test/Preprocessor/predefined-macros.c b/clang/test/Preprocessor/predefined-macros.c
index d85b3c566a6eb..c1b80d37f4fec 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -339,6 +339,6 @@
 // RUN:   | FileCheck %s --check-prefix=CHECK-SIGNALING-NANS
 // CHECK-SIGNALING-NANS: #define __SUPPORT_SNAN__
 
-// RUN: %clang_cc1 %s -E -dM -fno-signaling-nans -o - \
+// RUN: %clang_cc1 %s -E -dM -o - \
 // RUN:   | FileCheck %s --check-prefix=CHECK-NO-SIGNALING-NANS
 // CHECK-NO-SIGNALING-NANS-NOT: __SUPPORT_SNAN__

>From 8632c0fdc8e6e25d5f024df1afeadeb4ab4450af Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Fri, 22 May 2026 12:39:13 +0700
Subject: [PATCH 05/10] Issue a warning if signaling-nans is requested but not
 supported

---
 clang/include/clang/Basic/DiagnosticFrontendKinds.td | 2 ++
 clang/lib/Frontend/CompilerInstance.cpp              | 5 +++++
 clang/test/CodeGen/attr-signaling-nans.c             | 4 +++-
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index b585f0d3fa9a9..42b4ec28c5d12 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -57,6 +57,8 @@ def warn_fe_backend_unsupported_fp_rounding : Warning<
 def warn_fe_backend_unsupported_fp_exceptions : Warning<
     "overriding currently unsupported use of floating point exceptions "
     "on this target">, InGroup<UnsupportedFPOpt>;
+def warn_fe_backend_unsupported_signaling_nans : Warning<
+    "signaling NaNs are unsupported on this target">, InGroup<UnsupportedFPOpt>;
 def warn_fe_backend_invalid_feature_flag : Warning<
     "feature flag '%0' must start with either '+' to enable the feature or '-'"
     " to disable it; flag ignored">, InGroup<InvalidCommandLineArgument>;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 9e88abbece7f2..e79892fa261d7 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -153,6 +153,11 @@ bool CompilerInstance::createTarget() {
     // FIXME: can we disable FEnvAccess?
   }
 
+  if (!getTarget().hasSignalingNaNs() && getCodeGenOpts().SignalingNans) {
+    getDiagnostics().Report(diag::warn_fe_backend_unsupported_signaling_nans);
+    getCodeGenOpts().SignalingNans = false;
+  }
+
   // We should do it here because target knows nothing about
   // language options when it's being created.
   if (getLangOpts().OpenCL &&
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index c2715f20a88d0..4f58936ceed65 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -1,12 +1,14 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
 // Here msp430 is used as a target that do not support signaling NaNs.
-// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 
 float func_01(float x) {
   return x / 11.0F;
 }
 
+// expected-warning@*{{signaling NaNs are unsupported on this target}}
+
 // CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
 
 

>From 5226a610b244aee0bbc380d345edb7a480ac7c87 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 25 May 2026 16:27:12 +0700
Subject: [PATCH 06/10] Fix warning

---
 llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 7ffa99878cf74..f7ff80e870959 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -1006,6 +1006,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
       case Attribute::SanitizeRealtime:
       case Attribute::SanitizeRealtimeBlocking:
       case Attribute::SanitizeAllocToken:
+      case Attribute::SignalingNans:
       case Attribute::SpeculativeLoadHardening:
       case Attribute::StackProtect:
       case Attribute::StackProtectReq:

>From 00fcb51a36799bf14c76ca02184ab2c510d41215 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 25 May 2026 19:00:15 +0700
Subject: [PATCH 07/10] Attempt to fix flang test

---
 flang/test/Driver/flang-f-opts.f90 | 2 --
 1 file changed, 2 deletions(-)

diff --git a/flang/test/Driver/flang-f-opts.f90 b/flang/test/Driver/flang-f-opts.f90
index 3f53adaf56271..897cde44a57c8 100644
--- a/flang/test/Driver/flang-f-opts.f90
+++ b/flang/test/Driver/flang-f-opts.f90
@@ -66,7 +66,6 @@
 ! RUN:     -fprofile-correction                                             \
 ! RUN:     -fprofile-values                                                 \
 ! RUN:     -fschedule-insns                                                 \
-! RUN:     -fsignaling-nans                                                 \
 ! RUN:     -fstrength-reduce                                                \
 ! RUN:     -ftracer                                                         \
 ! RUN:     -funroll-all-loops                                               \
@@ -120,7 +119,6 @@
 ! CHECK-WARNING-DAG: optimization flag '-fprofile-correction' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-fprofile-values' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-fschedule-insns' is not supported
-! CHECK-WARNING-DAG: optimization flag '-fsignaling-nans' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-fstrength-reduce' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-ftracer' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-funroll-all-loops' is not supported

>From a9c9925c9773db8035cc8baa8f9462672f7c4721 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 25 May 2026 23:03:40 +0700
Subject: [PATCH 08/10] Document that `signaling_nans` requires `strictfp`.

---
 llvm/docs/LangRef.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 8cf4a1f4f386b..ab48ace6be65e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2595,7 +2595,8 @@ For example:
     can be produced by an operation (so that the transformation `x + 0 -> x`
     becomes valid), and an operation on such a NaN does not raise the "Invalid"
     exception. This attribute cannot be set if the target architecture does not
-    support IEEE 754 compatible sNaN handling.
+    support IEEE 754 compatible sNaN handling. This attribute requires that the
+    ``strictfp`` attribute also be set.
 ``speculative_load_hardening``
     This attribute indicates that
     `Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_

>From c27a62ca89c24d3c27db846e1358733c805328c8 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 15 Jun 2026 00:14:58 +0700
Subject: [PATCH 09/10] Revert "Remove default setting signaling_nan attribute
 for strictfp functions"

Restore the previous behavior, where a strictfp function implicitly got
the `singaling_nans` attribute. Difficulty in explaining the behavior to
users is not an acceptable reason for changing the default behavior.
Previously, this behavior was also undocumented. Assuming
`signaling_nans` by default in `strictfp` functions is safer and
maintains compatibility.

This reverts commit 1c9601c52e8f396d024e4c3032047dce87b288b8.
---
 clang/docs/ReleaseNotes.rst                  |  3 ---
 clang/docs/UsersManual.rst                   |  3 ++-
 clang/include/clang/Basic/CodeGenOptions.def |  5 ++++-
 clang/include/clang/Options/Options.td       | 17 +++++++++--------
 clang/lib/CodeGen/CodeGenFunction.cpp        |  7 ++++++-
 clang/lib/Driver/ToolChains/Clang.cpp        | 12 ++++++++----
 clang/test/CodeGen/attr-signaling-nans.c     |  2 +-
 clang/test/CodeGen/fp-floatcontrol-stack.cpp | 10 +++++-----
 clang/test/Driver/clang_f_opts.c             |  3 +++
 clang/test/Driver/fp-model.c                 |  8 ++++----
 clang/test/Preprocessor/predefined-macros.c  |  2 +-
 11 files changed, 43 insertions(+), 29 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 43de0756a21ee..4a47a4431622a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -71,9 +71,6 @@ C/C++ Language Potentially Breaking Changes
   Clang would previously ``break`` out of the ``while`` loop, whereas GCC (since version 9) would
   ``break`` out of the ``for`` loop here. Now, Clang and GCC both break out of the ``for`` loop.
 
-- Clang now requires the option ``-fsignaling-nans`` whenever support for
-  signaling NaNs is required.
-
 C++ Specific Potentially Breaking Changes
 -----------------------------------------
 
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 35e6f79c2c31e..c549d7ea2e062 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1902,7 +1902,8 @@ for more details.
    supporting architectures, it can enable additional optimization opportunities.
 
    If more than one option is specified, the last one takes effect. If none is
-   specified, the compiler assumes ``-fno-signaling-nans``.
+   specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
+   compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
 
 .. option:: -ffp-model=<value>
 
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index b8e33d9ca04d3..df2463227aacb 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -532,8 +532,11 @@ ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
 /// Adds attributes that prevent outlining (`-mno-outline`)
 CODEGENOPT(DisableOutlining, 1, 0, Benign)
 
-/// Whether compiler should treat signaling NaNs according to IEEE 754.
+/// Whether compiler should treat signaling NaNs according to IEEE 754. Use
+/// separate options for positive and negative settings, this allows us to
+/// represent "unspecified" setting.
 CODEGENOPT(SignalingNans, 1, 0, Benign)
+CODEGENOPT(NoSignalingNans, 1, 0, Benign)
 
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 0a702d2be1103..84fbc2f8b6709 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3002,14 +3002,15 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   " Default is 'fast' for CUDA, 'fast-honor-pragmas' for HIP, and 'on' otherwise.">,
   HelpText<"Form fused FP ops (e.g. FMAs)">,
   Values<"fast,on,off,fast-honor-pragmas">;
-defm signaling_nans
-    : BoolFOption<"signaling-nans", CodeGenOpts<"SignalingNans">, DefaultFalse,
-                  PosFlag<SetTrue, [], [ClangOption, CC1Option],
-                          "Assume signaling NaNs behave according to IEEE rules "
-                          "and can raise floating-point exceptions">,
-                  NegFlag<SetFalse, [], [ClangOption],
-                          "Assume signaling NaNs are treated identically to "
-                          "quiet NaNs">>;
+def fsignaling_nans : Flag<["-"], "fsignaling-nans">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<CodeGenOpts<"SignalingNans">>,
+  HelpText<"Assume signaling NaNs behave according to IEEE rules and can"
+           "raise floating-point exceptions">;
+def fno_signaling_nans : Flag<["-"], "fno-signaling-nans">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<CodeGenOpts<"NoSignalingNans">>,
+  HelpText<"Assume signaling NaNs are treated identically to quiet NaNs">;
 
 defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
   CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 92d305e209664..69f3585e0cfe4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1005,8 +1005,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
     Fn->addFnAttr(llvm::Attribute::HybridPatchable);
 
   if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
-    if (CGM.getCodeGenOpts().SignalingNans)
+    if (CGM.getCodeGenOpts().SignalingNans) {
       Fn->addFnAttr(llvm::Attribute::SignalingNans);
+    } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
+      // Both options are absent, - calculate default setting.
+      if (D->hasAttr<StrictFPAttr>())
+        Fn->addFnAttr(llvm::Attribute::SignalingNans);
+    }
   }
 
   if (D) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 3b027d2308417..062df7296d7c1 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2802,7 +2802,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   bool TrappingMathPresent = false; // Is trapping-math in args, and not
                                     // overriden by ffp-exception-behavior?
   bool RoundingFPMath = false;
-  bool SignalingNaNs = false;
+  std::optional<bool> SignalingNaNs;
   // -ffp-model values: strict, fast, precise
   StringRef FPModel = "";
   // -ffp-exception-behavior options: strict, maytrap, ignore
@@ -3316,8 +3316,12 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   if (!BFloat16ExcessPrecision.empty())
     CmdArgs.push_back(Args.MakeArgString("-fbfloat16-excess-precision=" +
                                          BFloat16ExcessPrecision));
-  if (SignalingNaNs)
-    CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
+  if (SignalingNaNs) {
+    if (*SignalingNaNs)
+      CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
+    else
+      CmdArgs.push_back(Args.MakeArgString("-fno-signaling-nans"));
+  }
 
   StringRef Recip = parseMRecipOption(D.getDiags(), Args);
   if (!Recip.empty())
@@ -3328,7 +3332,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   // that's consistent with gcc's behaviour.
   if (!HonorINFs && !HonorNaNs && !MathErrno && AssociativeMath && ApproxFunc &&
       ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath &&
-      !SignalingNaNs)
+      (SignalingNaNs.has_value() && !SignalingNaNs.value()))
     CmdArgs.push_back("-ffast-math");
 
   // Handle __FINITE_MATH_ONLY__ similarly.
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index 4f58936ceed65..e41aa3966090a 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
-// Here msp430 is used as a target that do not support signaling NaNs.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 
 float func_01(float x) {
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 5bd7409f4b933..098bd9a73eaa1 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
   (float z) { return n * z + n; }
 
 // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
 // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
 #pragma float_control(except, on)
 #endif
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     float exc_on FUN(2)
 //CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
 #if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
 
 #pragma float_control(pop)
     // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     float exc_pop FUN(5)
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index cd843e8d8f7b9..eb78ddf84d6b7 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -654,13 +654,16 @@
 // RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
 // CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
 // CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
 // CHECK-SIGNALING-NANS: "-cc1"
 // CHECK-SIGNALING-NANS: "-fsignaling-nans"
+// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
 // CHECK-NO-SIGNALING-NANS: "-cc1"
+// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
 // CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fp-model.c b/clang/test/Driver/fp-model.c
index 0951aae556170..62e0d2515fc9b 100644
--- a/clang/test/Driver/fp-model.c
+++ b/clang/test/Driver/fp-model.c
@@ -127,7 +127,7 @@
 // CHECK-FPM-AGGR-SAME: "-freciprocal-math"
 // CHECK-FPM-AGGR-SAME: "-ffp-contract=fast"
 // CHECK-FPM-AGGR-SAME: "-fno-rounding-math"
-// CHECK-FPM-AGGR-NOT: "-fsignaling-nans"
+// CHECK-FPM-AGGR-SAME: "-fno-signaling-nans"
 // CHECK-FPM-AGGR-SAME: "-ffast-math"
 // CHECK-FPM-AGGR-SAME: "-ffinite-math-only"
 // CHECK-FPM-AGGR-SAME: "-complex-range=basic"
@@ -144,7 +144,7 @@
 // CHECK-FPM-FAST-SAME: "-freciprocal-math"
 // CHECK-FPM-FAST-SAME: "-ffp-contract=fast"
 // CHECK-FPM-FAST-SAME: "-fno-rounding-math"
-// CHECK-FPM-FAST-NOT: "-fsignaling-nans"
+// CHECK-FPM-FAST-SAME: "-fno-signaling-nans"
 // CHECK-FPM-FAST-NOT: "-ffast-math"
 // CHECK-FPM-FAST-NOT: "-ffinite-math-only"
 // CHECK-FPM-FAST-SAME: "-complex-range=promoted"
@@ -154,7 +154,7 @@
 // CHECK-FPM-PRECISE: "-cc1"
 // CHECK-FPM-PRECISE-SAME: "-ffp-contract=on"
 // CHECK-FPM-PRECISE-SAME: "-fno-rounding-math"
-// CHECK-FPM-PRECISE-NOT: "-fsignaling-nans"
+// CHECK-FPM-PRECISE-SAME: "-fno-signaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=strict -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefix=CHECK-FPM-STRICT %s
@@ -260,7 +260,7 @@
 // RUN: %clang -### -nostdinc -fsignaling-nans -ffp-model=precise -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-SNAN-FPM-PRECISE %s
 // CHECK-SNAN-FPM-PRECISE:      "-cc1"
-// CHECK-SNAN-FPM-PRECISE-NOT: "-fsignaling-nans"
+// CHECK-SNAN-FPM-PRECISE-SAME: "-fno-signaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=precise -fsignaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-FPM-PRECISE-SNAN %s
diff --git a/clang/test/Preprocessor/predefined-macros.c b/clang/test/Preprocessor/predefined-macros.c
index c1b80d37f4fec..d85b3c566a6eb 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -339,6 +339,6 @@
 // RUN:   | FileCheck %s --check-prefix=CHECK-SIGNALING-NANS
 // CHECK-SIGNALING-NANS: #define __SUPPORT_SNAN__
 
-// RUN: %clang_cc1 %s -E -dM -o - \
+// RUN: %clang_cc1 %s -E -dM -fno-signaling-nans -o - \
 // RUN:   | FileCheck %s --check-prefix=CHECK-NO-SIGNALING-NANS
 // CHECK-NO-SIGNALING-NANS-NOT: __SUPPORT_SNAN__

>From 736202f375d18f8cb8973084019edef4c641782b Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 15 Jun 2026 00:37:16 +0700
Subject: [PATCH 10/10] Fix documentation

---
 clang/docs/UsersManual.rst | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index c549d7ea2e062..eefb4dccce4cf 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1901,9 +1901,7 @@ for more details.
    architecture does not implement signaling NaNs according to IEEE-754. On the
    supporting architectures, it can enable additional optimization opportunities.
 
-   If more than one option is specified, the last one takes effect. If none is
-   specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
-   compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
+   If more than one option is specified, the last one takes effect.
 
 .. option:: -ffp-model=<value>
 



More information about the flang-commits mailing list