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

Serge Pavlov via cfe-commits cfe-commits at lists.llvm.org
Sat May 16 10:08:07 PDT 2026


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

>From dc6b50a77509456ff4967e43f05c07e1c6742aa5 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 1/4] 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                     |   3 +
 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, 460 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 e6bcae8256365..76983668020cc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -209,6 +209,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 cb3042d437e97..91a59bd06c21a 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 0cd8f35339cf7..e6f9a6d24ab5b 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -524,6 +524,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 9f7d2a17a0f8a..ebde3739d9bce 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 46e97256a9e77..e8977a1a48892 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3012,6 +3012,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,
@@ -7418,7 +7427,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 e6ae89e0948c5..1c2746b92798d 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 9b951e69cce33..29c675ad70a57 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 c7afcc7c86053..9addbc45b837f 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 1ad7307fd6c30..cac300eacec6f 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 1ccd74314f373..f6aab310fa02f 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1317,6 +1317,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 42630375aeb8f..84d96a7fe1961 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 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 c73d03dcb6ad9..b81e1674444ba 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2574,6 +2574,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>`_
@@ -4129,6 +4137,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 8bd8463a2ad24..bccc2d1626c15 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -69,6 +69,9 @@ Changes to the LLVM IR
 * The `"nooutline"` attribute is now writen as `nooutline`. Existing IR and
   bitcode will be automatically updated.
 
+* The function attribute "signaling-nans" was added to control the support of
+  signaling NaNs.
+
 Changes to LLVM infrastructure
 ------------------------------
 
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index d1d34f22a2fc5..8c2d61d4747b6 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -120,6 +120,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 4c88388885426..9f64145b22475 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);
@@ -5934,14 +5938,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;
@@ -5996,14 +6000,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;
@@ -6011,13 +6015,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 d75c00e04c4eb..dbe0414e3e0bc 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 60cf2ab038fbce9114ffd466daebe4d441db1836 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 2/4] 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/docs/ReleaseNotes.md                     |   2 +-
 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 +-----------------
 22 files changed, 146 insertions(+), 339 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 76983668020cc..f0850501980fc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -209,7 +209,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 91a59bd06c21a..dc027f62bb236 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 ebde3739d9bce..8ac25dd369ae6 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 cac300eacec6f..6af09e8f89e6c 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 8dbb477be4c5d..030d13ff04832 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -327,3 +327,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 b81e1674444ba..d11f2fb2fa650 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2574,14 +2574,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/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index bccc2d1626c15..e74725e5872ea 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -69,7 +69,7 @@ Changes to the LLVM IR
 * The `"nooutline"` attribute is now writen as `nooutline`. Existing IR and
   bitcode will be automatically updated.
 
-* The function attribute "signaling-nans" was added to control the support of
+* The function attribute "signaling_nans" was added to control the support of
   signaling NaNs.
 
 Changes to LLVM infrastructure
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index 8c2d61d4747b6..d1d34f22a2fc5 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -120,14 +120,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 9162754bbfe1a..078c6e7b7cdf7 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -816,6 +816,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 9f64145b22475..4c88388885426 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);
@@ -5938,14 +5934,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;
@@ -6000,14 +5996,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;
@@ -6015,13 +6011,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 fa7a3b214e463..ee5c2647c60b3 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2300,6 +2300,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 71d5a8bc98a4b..e1285d6a44eb4 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -965,6 +965,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 dbe0414e3e0bc..d75c00e04c4eb 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 b2d9a21f5cbb5d88e4daa40764ab95d3335cdf82 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 3/4] 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 84d96a7fe1961..bd09a2ced29d1 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 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 d11f2fb2fa650..f7f1a9308fd45 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2578,12 +2578,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 222484bdd8e367a4931247f75dbcf09c26c9c191 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 4/4] 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 f0850501980fc..4d9c0c8e8d7ee 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -40,6 +40,9 @@ Potentially Breaking Changes
 C/C++ Language Potentially Breaking Changes
 -------------------------------------------
 
+- 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 dc027f62bb236..5e8da971f6544 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 e6f9a6d24ab5b..29f214a5e0329 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -524,11 +524,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 e8977a1a48892..29bd8e030feb4 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3012,15 +3012,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 6af09e8f89e6c..46006f754a63c 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 bd09a2ced29d1..42630375aeb8f 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 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 030d13ff04832..9e480e15d541a 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -332,6 +332,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__



More information about the cfe-commits mailing list