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

Serge Pavlov via flang-commits flang-commits at lists.llvm.org
Thu Jul 16 02:02:57 PDT 2026


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

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

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

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

The primary motivation for this change is the optimization of strictfp
code. When signaling NaN support is present, it can hinder optimization
in some cases. For example, the transformation `x * 1.0 -> x` is not
valid. Additionally, many intrinsic functions, like `trunc`, do not
raise floating-point exceptions other than those caused by signaling
NaNs, which can be beneficial for optimizations. The previous model
assumed that signaling NaNs are always supported in strictfp functions.
However, some hardware do not implement full support of signaling NaNs
and do not distinguish between quiet and signaling NaNs. It also can be
profitable to the skip signaling NaN support to enable more optimizations,
if the user can guarantee that signaling NaNs cannot be encountered in
their program.
---
 clang/docs/ReleaseNotes.md                    |   3 +
 clang/docs/UsersManual.md                     |  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/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 +++++++++++++++++-
 24 files changed, 441 insertions(+), 23 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-signaling-nans.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index cda3b1ee62f2b..8e1060d98daea 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -113,6 +113,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 ### New Compiler Flags
 
+- New option `-f[no-]signaling-nans` added to control the support of
+  signaling NaNs.
+
 ### Deprecated Compiler Flags
 
 ### Modified Compiler Flags
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 756f2784dc076..7b82171a81eea 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -1760,6 +1760,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:
@@ -1986,7 +1988,33 @@ for more details.
    - 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.
 ```
 
-```{eval-rst}
+.. 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 7e54e75752f39..59256cf19ff8a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -539,6 +539,12 @@ CODEGENOPT(DisableOutlining, 1, 0, Benign)
 /// Controlls the emission of function name in PPA1 on z/OS.
 CODEGENOPT(ZOSPPA1Name, 1, 1, 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 3aba4d261a651..823b55280aacc 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;
@@ -745,6 +746,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 e6868e6b9a4ba..18899888f033d 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3056,6 +3056,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,
@@ -7544,7 +7553,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 46b5bdecb9c40..fd65f00ee2ea3 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 debf3ecf589e3..d940281da346c 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 22880e5a04a3f..3a9d475e43f0a 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 73287c5d2d58d..7bc0c797e7df7 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(StringRef Name) override {
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index c06b142200d75..db5f337f88e26 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -105,6 +105,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 e305d9017d897..45a61692e7fd1 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 7629b42dd313d..15de124833249 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 c2b44919c1305..cdde5b5253780 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2937,6 +2937,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
@@ -2988,6 +2989,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
@@ -3151,6 +3153,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 =
@@ -3441,6 +3449,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())
@@ -3450,7 +3464,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 8b6ff844d0daa..3eca060fe6eb3 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1346,6 +1346,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
   else
     Builder.defineMacro("__FINITE_MATH_ONLY__", "0");
 
+  if (CGOpts.SignalingNans)
+    Builder.defineMacro("__SUPPORT_SNAN__");
+
   if (LangOpts.GNUCVersion) {
     if (LangOpts.GNUInline || LangOpts.CPlusPlus)
       Builder.defineMacro("__GNUC_GNU_INLINE__");
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
new file mode 100644
index 0000000000000..eef64875f04a6
--- /dev/null
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+
+float func_01(float x) {
+  return x / 11.0F;
+}
+
+// CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
+
+
+// SIGNALING: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
+// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index 962ac4afa4991..e42105b699e39 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
 //.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "signaling-nans" "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index 5871f1580d6b7..eb78ddf84d6b7 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -395,7 +395,6 @@
 // CHECK-WARNING-DAG: optimization flag '-fprofile-correction' is not supported
 // CHECK-WARNING-DAG: optimization flag '-fprofile-values' is not supported
 // CHECK-WARNING-DAG: optimization flag '-fschedule-insns' is not supported
-// CHECK-WARNING-DAG: optimization flag '-fsignaling-nans' is not supported
 // CHECK-WARNING-DAG: optimization flag '-fstrength-reduce' is not supported
 // CHECK-WARNING-DAG: optimization flag '-ftracer' is not supported
 // CHECK-WARNING-DAG: optimization flag '-funroll-all-loops' is not supported
@@ -651,3 +650,21 @@
 // RUN: %clang -### --target=x86_64-pc-windows-msvc -fno-strict-aliasing %s 2>&1 | FileCheck -check-prefix=CHECK-NO-STRICT-ALIASING %s
 // CHECK-STRICT-ALIASING-NOT: -relaxed-aliasing
 // CHECK-NO-STRICT-ALIASING: -relaxed-aliasing
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
+// CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
+// CHECK-SIGNALING-NANS: "-cc1"
+// CHECK-SIGNALING-NANS: "-fsignaling-nans"
+// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
+// CHECK-NO-SIGNALING-NANS: "-cc1"
+// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
+// CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fast-math.c b/clang/test/Driver/fast-math.c
index ffd081948914d..7d25f9a4dd5b3 100644
--- a/clang/test/Driver/fast-math.c
+++ b/clang/test/Driver/fast-math.c
@@ -143,11 +143,11 @@
 // RUN: %clang -### -fno-fast-math -ffast-math -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,REASSOC,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
 // RUN: %clang -### -funsafe-math-optimizations -ffinite-math-only \
-// RUN:     -fno-math-errno -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \
+// RUN:     -fno-math-errno -ffp-contract=fast -fno-rounding-math -fno-signaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
 // RUN: %clang -### -fno-honor-infinities -fno-honor-nans -fno-math-errno \
 // RUN:     -fassociative-math -freciprocal-math -fno-signed-zeros -fapprox-func \
-// RUN:     -fno-trapping-math -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \
+// RUN:     -fno-trapping-math -ffp-contract=fast -fno-rounding-math -fno-signaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
 //
 // RUN: %clang -### -ffast-math -fno-fast-math -c %s 2>&1 \
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index af037473e3653..26998d8785b10 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -54,6 +54,9 @@ Makes programs 10x faster by doing Special New Thing.
 
 ### Changes to the LLVM IR
 
+* The function attribute "signaling-nans" was added to control the support of
+  signaling NaNs.
+
 ### Changes to LLVM infrastructure
 
 ### Changes to building LLVM
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index b81b1dae27471..42427a475c283 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -126,6 +126,14 @@ struct SimplifyQuery {
   /// Otherwise always return false.
   LLVM_ABI bool isUndefValue(Value *V) const;
 
+  bool hasSignalingNaNs() const {
+    if (CxtI)
+      if (const BasicBlock *BB = CxtI->getParent())
+        if (const Function *F = BB->getParent())
+          return F->hasFnAttribute("signaling-nans");
+    return false;
+  }
+
   SimplifyQuery getWithoutDomCondCache() const {
     SimplifyQuery Copy(*this);
     Copy.DC = nullptr;
diff --git a/llvm/include/llvm/IR/FPEnv.h b/llvm/include/llvm/IR/FPEnv.h
index 38395b15c8c09..a3b7d1c26972d 100644
--- a/llvm/include/llvm/IR/FPEnv.h
+++ b/llvm/include/llvm/IR/FPEnv.h
@@ -80,11 +80,5 @@ LLVM_ABI Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr);
 inline bool canRoundingModeBe(RoundingMode RM, RoundingMode QRM) {
   return RM == QRM || RM == RoundingMode::Dynamic;
 }
-
-/// Returns true if the possibility of a signaling NaN can be safely
-/// ignored.
-inline bool canIgnoreSNaN(fp::ExceptionBehavior EB, FastMathFlags FMF) {
-  return (EB == fp::ebIgnore || FMF.noNaNs());
-}
 }
 #endif
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ddaa44f43e631..78cdd7217648c 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);
@@ -5986,14 +5990,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;
@@ -6048,14 +6052,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;
@@ -6063,13 +6067,13 @@ simplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF,
   // fsub -0.0, (fsub -0.0, X) ==> X
   // fsub -0.0, (fneg X) ==> X
   Value *X;
-  if (canIgnoreSNaN(ExBehavior, FMF))
+  if (canIgnoreSNaN(Q, FMF))
     if (match(Op0, m_NegZeroFP()) && match(Op1, m_FNeg(m_Value(X))))
       return X;
 
   // fsub 0.0, (fsub 0.0, X) ==> X if signed zeros are ignored.
   // fsub 0.0, (fneg X) ==> X if signed zeros are ignored.
-  if (canIgnoreSNaN(ExBehavior, FMF))
+  if (canIgnoreSNaN(Q, FMF))
     if (FMF.noSignedZeros() && match(Op0, m_AnyZeroFP()) &&
         (match(Op1, m_FSub(m_AnyZeroFP(), m_Value(X))) ||
          match(Op1, m_FNeg(m_Value(X)))))
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
index bfc7c038322d4..d36fe0b6f86d0 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
@@ -11,6 +11,15 @@
 
 define float @fadd_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %ret
+}
+
+define float @fadd_x_n0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fadd_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -19,6 +28,15 @@ define float @fadd_x_n0_defaultenv(float %a) #0 {
 
 define <2 x float> @fadd_vec_x_n0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %ret
+}
+
+define <2 x float> @fadd_vec_x_n0_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fadd_vec_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -27,7 +45,7 @@ define <2 x float> @fadd_vec_x_n0_defaultenv(<2 x float> %a) #0 {
 
 define float @fadd_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_ebmaytrap(
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]]
 ; CHECK-NEXT:    ret float [[RET]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0
@@ -101,6 +119,15 @@ define <2 x float> @fadd_vec_x_n0_dynamic(<2 x float> %a) #0 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define float @fadd_x_n0_towardzero(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_towardzero(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
+  ret float %ret
+}
+
+define float @fadd_x_n0_towardzero_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fadd_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -111,6 +138,15 @@ define float @fadd_x_n0_towardzero(float %a) #0 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define <2 x float> @fadd_vec_x_n0_towardzero(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_towardzero(
+; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %ret
+}
+
+define <2 x float> @fadd_vec_x_n0_towardzero_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fadd_vec_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -173,6 +209,15 @@ define <2 x float> @fadd_vec_ninf_x_n0_ebstrict(<2 x float> %a) #0 {
 
 define float @fadd_n0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_n0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float -0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %ret
+}
+
+define float @fadd_n0_x_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fadd_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -181,6 +226,15 @@ define float @fadd_n0_x_defaultenv(float %a) #0 {
 
 define <2 x float> @fadd_vec_n0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_n0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> splat (float -0.000000e+00), <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %ret
+}
+
+define <2 x float> @fadd_vec_n0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fadd_vec_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -213,6 +267,15 @@ define <2 x float> @fadd_vec_n0_x_ebmaytrap(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_x_0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %add
+}
+
+define float @fold_fadd_nsz_x_0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -221,6 +284,15 @@ define float @fold_fadd_nsz_x_0_defaultenv(float %a) #0 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %add
+}
+
+define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -229,6 +301,15 @@ define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_x_0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
+  ret float %add
+}
+
+define float @fold_fadd_nsz_x_0_neginf_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -237,6 +318,15 @@ define float @fold_fadd_nsz_x_0_neginf(float %a) #0 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_neginf(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf(
+; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %add
+}
+
+define <2 x float> @fold_fadd_vec_nsz_x_0_neginf_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -315,6 +405,15 @@ define <2 x float> @fold_fadd_vec_nsz_nnan_x_0_ebstrict(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret float [[A]]
+;
+  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret float %add
+}
+
+define float @fold_fadd_nsz_0_x_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -323,6 +422,15 @@ define float @fold_fadd_nsz_0_x_defaultenv(float %a) #0 {
 
 define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
+; CHECK-NEXT:    ret <2 x float> [[A]]
+;
+  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
+  ret <2 x float> %add
+}
+
+define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
+; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -459,4 +567,5 @@ define float @fold_fadd_snan_qnan_ebstrict() #0 {
 declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) #0
 declare <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float>, <2 x float>, metadata, metadata) #0
 
-attributes #0 = { strictfp }
+attributes #0 = { strictfp "signaling-nans" }
+attributes #1 = { strictfp }
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
index b55519e8374b6..195df793f90ab 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
@@ -11,6 +11,15 @@
 
 define float @fsub_x_p0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_x_p0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_x_p0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -27,6 +36,14 @@ define float @fsub_x_p0_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_x_p0_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_x_p0_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %ret
+}
+
 define float @fsub_nnan_x_p0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -45,6 +62,15 @@ define float @fsub_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_x_p0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_x_p0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_nnan_x_p0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebstrict(
@@ -65,6 +91,15 @@ define float @fsub_ninf_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_ninf_x_p0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_ninf_x_p0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[RET:%.*]] = call ninf float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call ninf float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; Round to -inf and if x is zero then the result is -0.0: must not fire
 define float @fsub_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_neginf(
@@ -89,6 +124,15 @@ define float @fsub_x_p0_dynamic(float %a) #0 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_neginf(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_nsz_x_p0_neginf_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_nsz_x_p0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
@@ -98,6 +142,15 @@ define float @fsub_nsz_x_p0_neginf(float %a) #0 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_dynamic(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_dynamic(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_nsz_x_p0_dynamic_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_nsz_x_p0_dynamic_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
@@ -111,6 +164,15 @@ define float @fsub_nsz_x_p0_dynamic(float %a) #0 {
 
 define float @fold_fsub_nsz_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv(
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %sub
+}
+
+define float @fold_fsub_nsz_x_n0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -127,6 +189,14 @@ define float @fold_fsub_nsz_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %sub
+}
+
 define float @fold_fsub_nnan_nsz_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nnan_nsz_x_n0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -145,6 +215,15 @@ define float @fold_fsub_nsz_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_nsz_x_n0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[SUB:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %sub
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_nnan_x_n0_ebstrict(
@@ -162,7 +241,18 @@ define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 
 define float @fold_fsub_fabs_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[ABSA1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA1]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[ABSA]]
+;
+  %absa = call float @llvm.fabs.f32(float %a) #0
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %sub
+}
+
+define float @fold_fsub_fabs_x_n0_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv_nosignaling(
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
 ; CHECK-NEXT:    ret float [[ABSA]]
 ;
   %absa = call float @llvm.fabs.f32(float %a) #0
@@ -182,6 +272,16 @@ define float @fold_fsub_fabs_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
+; CHECK-NEXT:    ret float [[ABSA]]
+;
+  %absa = call float @llvm.fabs.f32(float %a) #0
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %sub
+}
+
 define float @fold_fsub_fabs_nnan_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebmaytrap(
 ; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
@@ -204,6 +304,17 @@ define float @fold_fsub_fabs_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
+define float @fold_fsub_fabs_x_n0_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebstrict_nosignaling(
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
+; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[ABSA]]
+;
+  %absa = call float @llvm.fabs.f32(float %a) #0
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %sub
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebstrict(
@@ -219,6 +330,17 @@ define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 {
 ; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv(
 ; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[FPA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[SUB]]
+;
+  %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  %sub = call float @llvm.experimental.constrained.fsub.f32(float %fpa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %sub
+}
+
+define float @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(i32 %a) #1 {
+; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(
+; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
 ; CHECK-NEXT:    ret float [[FPA]]
 ;
   %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -232,6 +354,17 @@ define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 {
 
 define float @fsub_fneg_n0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_fneg_n0_fnX_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -251,6 +384,15 @@ define float @fsub_fneg_n0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %nega = fneg float %a
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %ret
+}
+
 define float @fsub_fneg_nnan_n0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -272,6 +414,17 @@ define float @fsub_fneg_n0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_n0_fnX_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_n0_fnX_ebstrict_nosignaling(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_n0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebstrict(
@@ -354,6 +507,17 @@ define float @fsub_fsub_nnan_n0_fnX_ebstrict(float %a) #0 {
 
 define float @fsub_fneg_nsz_p0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
+; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
+  ret float %ret
+}
+
+define float @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -373,6 +537,15 @@ define float @fsub_fneg_nsz_p0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(
+; CHECK-NEXT:    ret float [[A:%.*]]
+;
+  %nega = fneg float %a
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
+  ret float %ret
+}
+
 define float @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -394,6 +567,17 @@ define float @fsub_fneg_nsz_p0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
+define float @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(float %a) #1 {
+; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(
+; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
+; CHECK-NEXT:    ret float [[A]]
+;
+  %nega = fneg float %a
+  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %ret
+}
+
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(
@@ -722,4 +906,5 @@ declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, me
 
 declare float @llvm.experimental.constrained.sitofp.f32.i32(i32, metadata, metadata)
 
-attributes #0 = { strictfp }
+attributes #0 = { strictfp "signaling-nans" }
+attributes #1 = { strictfp }

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

- Optimizations using the new attribute are removed.
- Use a separate Attribute kind rather than string.
- Implemented merging the attribute when inlining.
- Added some tests.
- Fixed documentation.
---
 clang/docs/UsersManual.md                     |  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/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 +-----------------
 19 files changed, 134 insertions(+), 329 deletions(-)
 create mode 100644 llvm/test/Transforms/Inline/inline-signaling-nans.ll

diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 7b82171a81eea..688a7afd7489b 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -1992,23 +1992,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 823b55280aacc..3a8b28078db0b 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -747,6 +747,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 15de124833249..ebaeb3a7d2274 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 cdde5b5253780..1db61fb2532c2 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3088,6 +3088,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)
@@ -3117,6 +3118,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 dc7a884ec8ed8..ec7dafce4cfbe 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -358,3 +358,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/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index 42427a475c283..b81b1dae27471 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -126,14 +126,6 @@ struct SimplifyQuery {
   /// Otherwise always return false.
   LLVM_ABI bool isUndefValue(Value *V) const;
 
-  bool hasSignalingNaNs() const {
-    if (CxtI)
-      if (const BasicBlock *BB = CxtI->getParent())
-        if (const Function *F = BB->getParent())
-          return F->hasFnAttribute("signaling-nans");
-    return false;
-  }
-
   SimplifyQuery getWithoutDomCondCache() const {
     SimplifyQuery Copy(*this);
     Copy.DC = nullptr;
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 358f9a65a80af..6115a0526e1c1 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -826,6 +826,7 @@ enum AttributeKindCodes {
   ATTR_KIND_NOOUTLINE = 107,
   ATTR_KIND_FLATTEN = 108,
   ATTR_KIND_NOIPA = 109,
+  ATTR_KIND_SIGNALING_NANS = 110,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 4e45100b54d38..4b706a78cd42b 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -423,6 +423,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">;
@@ -494,3 +499,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 bd28f0d9902da..3963635b97ab5 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -696,6 +696,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 78cdd7217648c..ddaa44f43e631 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);
@@ -5990,14 +5986,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;
@@ -6052,14 +6048,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;
@@ -6067,13 +6063,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 c57323ed41525..91ed235bb943b 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2334,6 +2334,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::NoOutline;
   case bitc::ATTR_KIND_NOIPA:
     return Attribute::NoIPA;
+  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 f4ce522c23b8e..880aea50c8fb7 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1014,6 +1014,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_NOOUTLINE;
   case Attribute::NoIPA:
     return bitc::ATTR_KIND_NOIPA;
+  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 f696f2dd12323..8dc4f05b0b7d5 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -597,6 +597,12 @@ define void @noipa() noipa {
   ret void
 }
 
+; CHECK: define float @signaling_nans(float %x) [[SIGNALING_NANS:#[0-9]+]]
+define float @signaling_nans(float %x) signaling_nans {
+  %r = fadd float %x, 2.0
+  ret float %r
+}
+
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { memory(none) }
@@ -660,4 +666,5 @@ define void @noipa() noipa {
 ; CHECK: attributes [[OPTDEBUG]] = { optdebug }
 ; CHECK: attributes [[NODIVERGENCESOURCE]] = { nodivergencesource }
 ; CHECK: attributes [[NOIPA]] = { noipa }
+; CHECK: attributes [[SIGNALING_NANS]] = { signaling_nans }
 ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }
diff --git a/llvm/test/Transforms/Inline/inline-signaling-nans.ll b/llvm/test/Transforms/Inline/inline-signaling-nans.ll
new file mode 100644
index 0000000000000..c36f1a0710f64
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline-signaling-nans.ll
@@ -0,0 +1,47 @@
+; RUN: opt < %s -passes=inline -S | FileCheck %s
+
+; Check the inliner combines signaling_nans attribute so that the calling
+; function has this attribute if it or the called functions have it.
+ 
+define internal float @not_signaling(float %x, float %y) #0 {
+  %sum = fadd float %x, %y
+  ret float %sum
+}
+
+define internal float @signaling(float %x, float %y) #1 {
+  %mul = fmul float %x, %y
+  ret float %mul
+}
+
+define float @all_not_signaling(float %x, float %y, float %z) #0 {
+  %v = call float @not_signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @all_not_signaling({{.*}}) [[NOT_SIGNALING:#[0-9]+]] {
+
+define float @all_signaling(float %x, float %y, float %z) #1 {
+  %v = call float @signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @all_signaling({{.*}}) [[SIGNALING:#[0-9]+]] {
+
+define float @to_not_signaling(float %x, float %y, float %z) #0 {
+  %v = call float @signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @to_not_signaling({{.*}}) [[SIGNALING:#[0-9]+]] {
+
+define float @to_signaling(float %x, float %y, float %z) #1 {
+  %v = call float @not_signaling(float %x, float %y)
+  %res = fmul float %v, %z
+  ret float %res
+}
+; CHECK: define float @to_signaling({{.*}}) [[SIGNALING:#[0-9]+]] {
+
+attributes #0 = { nounwind }
+; CHECK: attributes [[NOT_SIGNALING]] = { nounwind }
+attributes #1 = { nounwind signaling_nans }
+; CHECK: attributes [[SIGNALING]] = { nounwind signaling_nans }
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
index d36fe0b6f86d0..bfc7c038322d4 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fadd.ll
@@ -11,15 +11,6 @@
 
 define float @fadd_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %ret
-}
-
-define float @fadd_x_n0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fadd_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -28,15 +19,6 @@ define float @fadd_x_n0_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fadd_vec_x_n0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %ret
-}
-
-define <2 x float> @fadd_vec_x_n0_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fadd_vec_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -45,7 +27,7 @@ define <2 x float> @fadd_vec_x_n0_defaultenv_nosignaling(<2 x float> %a) #1 {
 
 define float @fadd_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_ebmaytrap(
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]]
+; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret float [[RET]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0
@@ -119,15 +101,6 @@ define <2 x float> @fadd_vec_x_n0_dynamic(<2 x float> %a) #0 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define float @fadd_x_n0_towardzero(float %a) #0 {
 ; CHECK-LABEL: @fadd_x_n0_towardzero(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
-  ret float %ret
-}
-
-define float @fadd_x_n0_towardzero_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fadd_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float %a, float -0.0, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -138,15 +111,6 @@ define float @fadd_x_n0_towardzero_nosignaling(float %a) #1 {
 ; Test one of the remaining rounding modes and the rest will be fine.
 define <2 x float> @fadd_vec_x_n0_towardzero(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_x_n0_towardzero(
-; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> splat (float -0.000000e+00), metadata !"round.towardzero", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %ret
-}
-
-define <2 x float> @fadd_vec_x_n0_towardzero_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fadd_vec_x_n0_towardzero_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float><float -0.0, float -0.0>, metadata !"round.towardzero", metadata !"fpexcept.ignore") #0
@@ -209,15 +173,6 @@ define <2 x float> @fadd_vec_ninf_x_n0_ebstrict(<2 x float> %a) #0 {
 
 define float @fadd_n0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fadd_n0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float -0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %ret
-}
-
-define float @fadd_n0_x_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fadd_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fadd.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -226,15 +181,6 @@ define float @fadd_n0_x_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fadd_vec_n0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fadd_vec_n0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> splat (float -0.000000e+00), <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %ret
-}
-
-define <2 x float> @fadd_vec_n0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fadd_vec_n0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %ret = call <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float><float -0.0, float -0.0>, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -267,15 +213,6 @@ define <2 x float> @fadd_vec_n0_x_ebmaytrap(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_x_0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %add
-}
-
-define float @fold_fadd_nsz_x_0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fadd_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -284,15 +221,6 @@ define float @fold_fadd_nsz_x_0_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %add
-}
-
-define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -301,15 +229,6 @@ define <2 x float> @fold_fadd_vec_nsz_x_0_defaultenv_nosignaling(<2 x float> %a)
 
 define float @fold_fadd_nsz_x_0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
-  ret float %add
-}
-
-define float @fold_fadd_nsz_x_0_neginf_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fadd_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -318,15 +237,6 @@ define float @fold_fadd_nsz_x_0_neginf_nosignaling(float %a) #1 {
 
 define <2 x float> @fold_fadd_vec_nsz_x_0_neginf(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf(
-; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> [[A1:%.*]], <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %add
-}
-
-define <2 x float> @fold_fadd_vec_nsz_x_0_neginf_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fold_fadd_vec_nsz_x_0_neginf_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> %a, <2 x float> zeroinitializer, metadata !"round.downward", metadata !"fpexcept.ignore") #0
@@ -405,15 +315,6 @@ define <2 x float> @fold_fadd_vec_nsz_nnan_x_0_ebstrict(<2 x float> %a) #0 {
 
 define float @fold_fadd_nsz_0_x_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.000000e+00, float [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret float [[A]]
-;
-  %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret float %add
-}
-
-define float @fold_fadd_nsz_0_x_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fadd_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %add = call nsz float @llvm.experimental.constrained.fadd.f32(float 0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -422,15 +323,6 @@ define float @fold_fadd_nsz_0_x_defaultenv_nosignaling(float %a) #1 {
 
 define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv(<2 x float> %a) #0 {
 ; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> [[A1:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]]
-; CHECK-NEXT:    ret <2 x float> [[A]]
-;
-  %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
-  ret <2 x float> %add
-}
-
-define <2 x float> @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(<2 x float> %a) #1 {
-; CHECK-LABEL: @fold_fadd_vec_nsz_0_x_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
 ;
   %add = call nsz <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float> zeroinitializer, <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0
@@ -567,5 +459,4 @@ define float @fold_fadd_snan_qnan_ebstrict() #0 {
 declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) #0
 declare <2 x float> @llvm.experimental.constrained.fadd.v2f32(<2 x float>, <2 x float>, metadata, metadata) #0
 
-attributes #0 = { strictfp "signaling-nans" }
-attributes #1 = { strictfp }
+attributes #0 = { strictfp }
diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
index 195df793f90ab..b55519e8374b6 100644
--- a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
+++ b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll
@@ -11,15 +11,6 @@
 
 define float @fsub_x_p0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_x_p0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_x_p0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -36,14 +27,6 @@ define float @fsub_x_p0_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_x_p0_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_x_p0_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %ret
-}
-
 define float @fsub_nnan_x_p0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -62,15 +45,6 @@ define float @fsub_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_x_p0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_x_p0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_nnan_x_p0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_nnan_x_p0_ebstrict(
@@ -91,15 +65,6 @@ define float @fsub_ninf_x_p0_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_ninf_x_p0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_ninf_x_p0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[RET:%.*]] = call ninf float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call ninf float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; Round to -inf and if x is zero then the result is -0.0: must not fire
 define float @fsub_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_x_p0_neginf(
@@ -124,15 +89,6 @@ define float @fsub_x_p0_dynamic(float %a) #0 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_neginf(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_neginf(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_nsz_x_p0_neginf_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_nsz_x_p0_neginf_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.downward", metadata !"fpexcept.ignore")
@@ -142,15 +98,6 @@ define float @fsub_nsz_x_p0_neginf_nosignaling(float %a) #1 {
 ; With nsz we don't have to worry about -0.0 so the transform is valid.
 define float @fsub_nsz_x_p0_dynamic(float %a) #0 {
 ; CHECK-LABEL: @fsub_nsz_x_p0_dynamic(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_nsz_x_p0_dynamic_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_nsz_x_p0_dynamic_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.dynamic", metadata !"fpexcept.ignore")
@@ -164,15 +111,6 @@ define float @fsub_nsz_x_p0_dynamic_nosignaling(float %a) #1 {
 
 define float @fold_fsub_nsz_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv(
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A1:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %sub
-}
-
-define float @fold_fsub_nsz_x_n0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -189,14 +127,6 @@ define float @fold_fsub_nsz_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %sub
-}
-
 define float @fold_fsub_nnan_nsz_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nnan_nsz_x_n0_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -215,15 +145,6 @@ define float @fold_fsub_nsz_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_nsz_x_n0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[SUB:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %sub
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_nsz_nnan_x_n0_ebstrict(
@@ -241,18 +162,7 @@ define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 {
 
 define float @fold_fsub_fabs_x_n0_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv(
-; CHECK-NEXT:    [[ABSA1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA1]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[ABSA]]
-;
-  %absa = call float @llvm.fabs.f32(float %a) #0
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %sub
-}
-
-define float @fold_fsub_fabs_x_n0_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv_nosignaling(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
+; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret float [[ABSA]]
 ;
   %absa = call float @llvm.fabs.f32(float %a) #0
@@ -272,16 +182,6 @@ define float @fold_fsub_fabs_x_n0_ebmaytrap(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebmaytrap_nosignaling(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
-; CHECK-NEXT:    ret float [[ABSA]]
-;
-  %absa = call float @llvm.fabs.f32(float %a) #0
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %sub
-}
-
 define float @fold_fsub_fabs_nnan_x_n0_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebmaytrap(
 ; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
@@ -304,17 +204,6 @@ define float @fold_fsub_fabs_x_n0_ebstrict(float %a) #0 {
   ret float %sub
 }
 
-define float @fold_fsub_fabs_x_n0_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebstrict_nosignaling(
-; CHECK-NEXT:    [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) #[[ATTR0]]
-; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[ABSA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[ABSA]]
-;
-  %absa = call float @llvm.fabs.f32(float %a) #0
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %sub
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebstrict(
@@ -330,17 +219,6 @@ define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 {
 define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 {
 ; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv(
 ; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[FPA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[SUB]]
-;
-  %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  %sub = call float @llvm.experimental.constrained.fsub.f32(float %fpa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %sub
-}
-
-define float @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(i32 %a) #1 {
-; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(
-; CHECK-NEXT:    [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
 ; CHECK-NEXT:    ret float [[FPA]]
 ;
   %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
@@ -354,17 +232,6 @@ define float @fold_fsub_sitofp_x_n0_defaultenv_nosignaling(i32 %a) #1 {
 
 define float @fsub_fneg_n0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_fneg_n0_fnX_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -384,15 +251,6 @@ define float @fsub_fneg_n0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_n0_fnX_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %nega = fneg float %a
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %ret
-}
-
 define float @fsub_fneg_nnan_n0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -414,17 +272,6 @@ define float @fsub_fneg_n0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_n0_fnX_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_n0_fnX_ebstrict_nosignaling(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_n0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebstrict(
@@ -507,17 +354,6 @@ define float @fsub_fsub_nnan_n0_fnX_ebstrict(float %a) #0 {
 
 define float @fsub_fneg_nsz_p0_fnX_defaultenv(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A1:%.*]]
-; CHECK-NEXT:    [[A:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore")
-  ret float %ret
-}
-
-define float @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_defaultenv_nosignaling(
 ; CHECK-NEXT:    ret float [[A:%.*]]
 ;
   %nega = fneg float %a
@@ -537,15 +373,6 @@ define float @fsub_fneg_nsz_p0_fnX_ebmaytrap(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebmaytrap_nosignaling(
-; CHECK-NEXT:    ret float [[A:%.*]]
-;
-  %nega = fneg float %a
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap")
-  ret float %ret
-}
-
 define float @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nsz_nnan_p0_fnX_ebmaytrap(
 ; CHECK-NEXT:    ret float [[A:%.*]]
@@ -567,17 +394,6 @@ define float @fsub_fneg_nsz_p0_fnX_ebstrict(float %a) #0 {
   ret float %ret
 }
 
-define float @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(float %a) #1 {
-; CHECK-LABEL: @fsub_fneg_nsz_p0_fnX_ebstrict_nosignaling(
-; CHECK-NEXT:    [[NEGA:%.*]] = fneg float [[A:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict")
-; CHECK-NEXT:    ret float [[A]]
-;
-  %nega = fneg float %a
-  %ret = call nsz float @llvm.experimental.constrained.fsub.f32(float 0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %ret
-}
-
 ; The instruction is expected to remain, but the result isn't used.
 define float @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(float %a) #0 {
 ; CHECK-LABEL: @fsub_fneg_nnan_nsz_p0_fnX_ebstrict(
@@ -906,5 +722,4 @@ declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, me
 
 declare float @llvm.experimental.constrained.sitofp.f32.i32(i32, metadata, metadata)
 
-attributes #0 = { strictfp "signaling-nans" }
-attributes #1 = { strictfp }
+attributes #0 = { strictfp }

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

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

diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index eef64875f04a6..2cfbfc5b9b6f5 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -10,5 +10,5 @@ float func_01(float x) {
 // CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
 
 
-// SIGNALING: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
-// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
+// SIGNALING: attributes #[[ATTR]] = {{{.*}} signaling_nans {{.*}}}
+// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} signaling_nans {{.*}}}
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 237c9d4f9a37e..098bd9a73eaa1 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
   (float z) { return n * z + n; }
 
 // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
 // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
 #pragma float_control(except, on)
 #endif
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     float exc_on FUN(2)
 //CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
 #if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
 
 #pragma float_control(pop)
     // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     float exc_pop FUN(5)
@@ -76,7 +76,7 @@ float fun_default FUN(1)
 
 #pragma float_control(except, off)
         float exc_off FUN(5)
-//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}
+//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}{
 #if DEFAULT
 //CHECK-DDEFAULT: call float @llvm.fmuladd{{.*}}
 #endif
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index e42105b699e39..a05327e1aa322 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
 //.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "signaling-nans" "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp signaling_nans "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Options/Gis.hlsl b/clang/test/Options/Gis.hlsl
index c60fd1854bdc9..4191236889d39 100644
--- a/clang/test/Options/Gis.hlsl
+++ b/clang/test/Options/Gis.hlsl
@@ -1,7 +1,7 @@
 // RUN: %clang_dxc -T lib_6_4 -Gis %s 2>&1 -### | FileCheck -check-prefix=Gis %s
 // RUN: %clang_dxc -T lib_6_4 %s 2>&1 -### | FileCheck -check-prefix=NO_Gis %s
 
-// Gis: "-ffp-contract=off" "-frounding-math" "-ffp-exception-behavior=strict" "-complex-range=full"
+// Gis: "-ffp-contract=off" "-frounding-math" "-ffp-exception-behavior=strict" "-fsignaling-nans" "-complex-range=full"
 // assert expected floating point options are present
 // NO_Gis-NOT: "-ffp-contract=off" 
 // NO_Gis-NOT: "-frounding-math" 
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 4b706a78cd42b..6178ab5fa5473 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -426,7 +426,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 39f495d76e639330b729017519774776a10f7a68 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Sat, 16 May 2026 01:15:54 +0700
Subject: [PATCH 04/10] Remove default setting signaling_nan attribute for
 strictfp functions

We cannot describe such behavior in Clang User Manual, strictfp is not
visible for a user.
---
 clang/docs/UsersManual.md                       |  3 +--
 clang/include/clang/Basic/CodeGenOptions.def    |  1 -
 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 +-
 11 files changed, 26 insertions(+), 41 deletions(-)

diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 688a7afd7489b..00135a77d0724 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -2016,8 +2016,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 59256cf19ff8a..c135a5b95374e 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -543,7 +543,6 @@ CODEGENOPT(ZOSPPA1Name, 1, 1, Benign)
 /// separate options for positive and negative settings, this allows us to
 /// represent "unspecified" setting.
 CODEGENOPT(SignalingNans, 1, 0, Benign)
-CODEGENOPT(NoSignalingNans, 1, 0, Benign)
 
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 18899888f033d..a78746716da0f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3056,15 +3056,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 ebaeb3a7d2274..89f0d7a9d088f 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 1db61fb2532c2..9cfd5fb23ae20 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2937,7 +2937,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
@@ -3451,12 +3451,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())
@@ -3467,7 +3463,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   // that's consistent with gcc's behaviour.
   if (!HonorINFs && !HonorNaNs && !MathErrno && AssociativeMath && ApproxFunc &&
       ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath &&
-      (SignalingNaNs.has_value() && !SignalingNaNs.value()))
+      !SignalingNaNs)
     CmdArgs.push_back("-ffast-math");
 
   // Handle __FINITE_MATH_ONLY__ similarly.
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index 2cfbfc5b9b6f5..c2715f20a88d0 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// Here msp430 is used as a target that do not support signaling NaNs.
 // RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 
 float func_01(float x) {
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 098bd9a73eaa1..5bd7409f4b933 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
   (float z) { return n * z + n; }
 
 // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
 // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
 #pragma float_control(except, on)
 #endif
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
-    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
     float exc_on FUN(2)
 //CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
 #if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
 
 #pragma float_control(pop)
     // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     float exc_pop FUN(5)
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index a05327e1aa322..962ac4afa4991 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
 //.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp signaling_nans "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
 // STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index eb78ddf84d6b7..cd843e8d8f7b9 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -654,16 +654,13 @@
 // RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
 // CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
 // CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
-// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
 // CHECK-SIGNALING-NANS: "-cc1"
 // CHECK-SIGNALING-NANS: "-fsignaling-nans"
-// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
 // CHECK-NO-SIGNALING-NANS: "-cc1"
-// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
 // CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fp-model.c b/clang/test/Driver/fp-model.c
index 62e0d2515fc9b..0951aae556170 100644
--- a/clang/test/Driver/fp-model.c
+++ b/clang/test/Driver/fp-model.c
@@ -127,7 +127,7 @@
 // CHECK-FPM-AGGR-SAME: "-freciprocal-math"
 // CHECK-FPM-AGGR-SAME: "-ffp-contract=fast"
 // CHECK-FPM-AGGR-SAME: "-fno-rounding-math"
-// CHECK-FPM-AGGR-SAME: "-fno-signaling-nans"
+// CHECK-FPM-AGGR-NOT: "-fsignaling-nans"
 // CHECK-FPM-AGGR-SAME: "-ffast-math"
 // CHECK-FPM-AGGR-SAME: "-ffinite-math-only"
 // CHECK-FPM-AGGR-SAME: "-complex-range=basic"
@@ -144,7 +144,7 @@
 // CHECK-FPM-FAST-SAME: "-freciprocal-math"
 // CHECK-FPM-FAST-SAME: "-ffp-contract=fast"
 // CHECK-FPM-FAST-SAME: "-fno-rounding-math"
-// CHECK-FPM-FAST-SAME: "-fno-signaling-nans"
+// CHECK-FPM-FAST-NOT: "-fsignaling-nans"
 // CHECK-FPM-FAST-NOT: "-ffast-math"
 // CHECK-FPM-FAST-NOT: "-ffinite-math-only"
 // CHECK-FPM-FAST-SAME: "-complex-range=promoted"
@@ -154,7 +154,7 @@
 // CHECK-FPM-PRECISE: "-cc1"
 // CHECK-FPM-PRECISE-SAME: "-ffp-contract=on"
 // CHECK-FPM-PRECISE-SAME: "-fno-rounding-math"
-// CHECK-FPM-PRECISE-SAME: "-fno-signaling-nans"
+// CHECK-FPM-PRECISE-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=strict -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefix=CHECK-FPM-STRICT %s
@@ -260,7 +260,7 @@
 // RUN: %clang -### -nostdinc -fsignaling-nans -ffp-model=precise -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-SNAN-FPM-PRECISE %s
 // CHECK-SNAN-FPM-PRECISE:      "-cc1"
-// CHECK-SNAN-FPM-PRECISE-SAME: "-fno-signaling-nans"
+// CHECK-SNAN-FPM-PRECISE-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=precise -fsignaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-FPM-PRECISE-SNAN %s
diff --git a/clang/test/Preprocessor/predefined-macros.c b/clang/test/Preprocessor/predefined-macros.c
index ec7dafce4cfbe..a44717721f0ad 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -363,6 +363,6 @@
 // RUN:   | FileCheck %s --check-prefix=CHECK-SIGNALING-NANS
 // CHECK-SIGNALING-NANS: #define __SUPPORT_SNAN__
 
-// RUN: %clang_cc1 %s -E -dM -fno-signaling-nans -o - \
+// RUN: %clang_cc1 %s -E -dM -o - \
 // RUN:   | FileCheck %s --check-prefix=CHECK-NO-SIGNALING-NANS
 // CHECK-NO-SIGNALING-NANS-NOT: __SUPPORT_SNAN__

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

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

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

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

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

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

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

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

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

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

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

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

diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 00135a77d0724..688a7afd7489b 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -2016,7 +2016,8 @@ for more details.
    supporting architectures, it can enable additional optimization opportunities.
 
    If more than one option is specified, the last one takes effect. If none is
-   specified, the compiler assumes ``-fno-signaling-nans``.
+   specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
+   compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
 
 .. option:: -ffp-model=<value>
 
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index c135a5b95374e..59256cf19ff8a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -543,6 +543,7 @@ CODEGENOPT(ZOSPPA1Name, 1, 1, Benign)
 /// separate options for positive and negative settings, this allows us to
 /// represent "unspecified" setting.
 CODEGENOPT(SignalingNans, 1, 0, Benign)
+CODEGENOPT(NoSignalingNans, 1, 0, Benign)
 
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index a78746716da0f..18899888f033d 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3056,14 +3056,15 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   " Default is 'fast' for CUDA, 'fast-honor-pragmas' for HIP, and 'on' otherwise.">,
   HelpText<"Form fused FP ops (e.g. FMAs)">,
   Values<"fast,on,off,fast-honor-pragmas">;
-defm signaling_nans
-    : BoolFOption<"signaling-nans", CodeGenOpts<"SignalingNans">, DefaultFalse,
-                  PosFlag<SetTrue, [], [ClangOption, CC1Option],
-                          "Assume signaling NaNs behave according to IEEE rules "
-                          "and can raise floating-point exceptions">,
-                  NegFlag<SetFalse, [], [ClangOption],
-                          "Assume signaling NaNs are treated identically to "
-                          "quiet NaNs">>;
+def fsignaling_nans : Flag<["-"], "fsignaling-nans">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<CodeGenOpts<"SignalingNans">>,
+  HelpText<"Assume signaling NaNs behave according to IEEE rules and can"
+           "raise floating-point exceptions">;
+def fno_signaling_nans : Flag<["-"], "fno-signaling-nans">,
+  Visibility<[ClangOption, CC1Option]>,
+  MarshallingInfoFlag<CodeGenOpts<"NoSignalingNans">>,
+  HelpText<"Assume signaling NaNs are treated identically to quiet NaNs">;
 
 defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
   CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 89f0d7a9d088f..ebaeb3a7d2274 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1005,8 +1005,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
     Fn->addFnAttr(llvm::Attribute::HybridPatchable);
 
   if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
-    if (CGM.getCodeGenOpts().SignalingNans)
+    if (CGM.getCodeGenOpts().SignalingNans) {
       Fn->addFnAttr(llvm::Attribute::SignalingNans);
+    } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
+      // Both options are absent, - calculate default setting.
+      if (D->hasAttr<StrictFPAttr>())
+        Fn->addFnAttr(llvm::Attribute::SignalingNans);
+    }
   }
 
   if (D) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 9cfd5fb23ae20..1db61fb2532c2 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2937,7 +2937,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   bool TrappingMathPresent = false; // Is trapping-math in args, and not
                                     // overriden by ffp-exception-behavior?
   bool RoundingFPMath = false;
-  bool SignalingNaNs = false;
+  std::optional<bool> SignalingNaNs;
   // -ffp-model values: strict, fast, precise
   StringRef FPModel = "";
   // -ffp-exception-behavior options: strict, maytrap, ignore
@@ -3451,8 +3451,12 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   if (!BFloat16ExcessPrecision.empty())
     CmdArgs.push_back(Args.MakeArgString("-fbfloat16-excess-precision=" +
                                          BFloat16ExcessPrecision));
-  if (SignalingNaNs)
-    CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
+  if (SignalingNaNs) {
+    if (*SignalingNaNs)
+      CmdArgs.push_back(Args.MakeArgString("-fsignaling-nans"));
+    else
+      CmdArgs.push_back(Args.MakeArgString("-fno-signaling-nans"));
+  }
 
   StringRef Recip = parseMRecipOption(D.getDiags(), Args);
   if (!Recip.empty())
@@ -3463,7 +3467,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   // that's consistent with gcc's behaviour.
   if (!HonorINFs && !HonorNaNs && !MathErrno && AssociativeMath && ApproxFunc &&
       ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath &&
-      !SignalingNaNs)
+      (SignalingNaNs.has_value() && !SignalingNaNs.value()))
     CmdArgs.push_back("-ffast-math");
 
   // Handle __FINITE_MATH_ONLY__ similarly.
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index 4f58936ceed65..e41aa3966090a 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
-// Here msp430 is used as a target that do not support signaling NaNs.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 // RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
 
 float func_01(float x) {
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 5bd7409f4b933..098bd9a73eaa1 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
   (float z) { return n * z + n; }
 
 // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
 // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
 float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
 #pragma float_control(except, on)
 #endif
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
-    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+    // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     float exc_on FUN(2)
 //CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
 #if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
 
 #pragma float_control(pop)
     // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+    // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
     // CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
     float exc_pop FUN(5)
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index cd843e8d8f7b9..eb78ddf84d6b7 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -654,13 +654,16 @@
 // RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
 // CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
 // CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
 // CHECK-SIGNALING-NANS: "-cc1"
 // CHECK-SIGNALING-NANS: "-fsignaling-nans"
+// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
 // CHECK-NO-SIGNALING-NANS: "-cc1"
+// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
 // CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
 
 // RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fp-model.c b/clang/test/Driver/fp-model.c
index 0951aae556170..62e0d2515fc9b 100644
--- a/clang/test/Driver/fp-model.c
+++ b/clang/test/Driver/fp-model.c
@@ -127,7 +127,7 @@
 // CHECK-FPM-AGGR-SAME: "-freciprocal-math"
 // CHECK-FPM-AGGR-SAME: "-ffp-contract=fast"
 // CHECK-FPM-AGGR-SAME: "-fno-rounding-math"
-// CHECK-FPM-AGGR-NOT: "-fsignaling-nans"
+// CHECK-FPM-AGGR-SAME: "-fno-signaling-nans"
 // CHECK-FPM-AGGR-SAME: "-ffast-math"
 // CHECK-FPM-AGGR-SAME: "-ffinite-math-only"
 // CHECK-FPM-AGGR-SAME: "-complex-range=basic"
@@ -144,7 +144,7 @@
 // CHECK-FPM-FAST-SAME: "-freciprocal-math"
 // CHECK-FPM-FAST-SAME: "-ffp-contract=fast"
 // CHECK-FPM-FAST-SAME: "-fno-rounding-math"
-// CHECK-FPM-FAST-NOT: "-fsignaling-nans"
+// CHECK-FPM-FAST-SAME: "-fno-signaling-nans"
 // CHECK-FPM-FAST-NOT: "-ffast-math"
 // CHECK-FPM-FAST-NOT: "-ffinite-math-only"
 // CHECK-FPM-FAST-SAME: "-complex-range=promoted"
@@ -154,7 +154,7 @@
 // CHECK-FPM-PRECISE: "-cc1"
 // CHECK-FPM-PRECISE-SAME: "-ffp-contract=on"
 // CHECK-FPM-PRECISE-SAME: "-fno-rounding-math"
-// CHECK-FPM-PRECISE-NOT: "-fsignaling-nans"
+// CHECK-FPM-PRECISE-SAME: "-fno-signaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=strict -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefix=CHECK-FPM-STRICT %s
@@ -260,7 +260,7 @@
 // RUN: %clang -### -nostdinc -fsignaling-nans -ffp-model=precise -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-SNAN-FPM-PRECISE %s
 // CHECK-SNAN-FPM-PRECISE:      "-cc1"
-// CHECK-SNAN-FPM-PRECISE-NOT: "-fsignaling-nans"
+// CHECK-SNAN-FPM-PRECISE-SAME: "-fno-signaling-nans"
 
 // RUN: %clang -### -nostdinc -ffp-model=precise -fsignaling-nans -c %s 2>&1 \
 // RUN:   | FileCheck --check-prefixes=CHECK-FPM-PRECISE-SNAN %s
diff --git a/clang/test/Preprocessor/predefined-macros.c b/clang/test/Preprocessor/predefined-macros.c
index a44717721f0ad..ec7dafce4cfbe 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -363,6 +363,6 @@
 // RUN:   | FileCheck %s --check-prefix=CHECK-SIGNALING-NANS
 // CHECK-SIGNALING-NANS: #define __SUPPORT_SNAN__
 
-// RUN: %clang_cc1 %s -E -dM -o - \
+// RUN: %clang_cc1 %s -E -dM -fno-signaling-nans -o - \
 // RUN:   | FileCheck %s --check-prefix=CHECK-NO-SIGNALING-NANS
 // CHECK-NO-SIGNALING-NANS-NOT: __SUPPORT_SNAN__

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

---
 clang/docs/UsersManual.md | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 688a7afd7489b..f1d99706585b2 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -1726,6 +1726,8 @@ for more details.
 
    * ``-ffp-contract=fast``
 
+   * ``-fno-signaling-nans``
+
    Note: ``-ffast-math`` causes ``crtfastmath.o`` to be linked with code unless
    ``-shared`` or ``-mno-daz-ftz`` is present. See
    :ref:`crtfastmath.o` for more details.
@@ -1760,8 +1762,6 @@ 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:
@@ -2015,9 +2015,7 @@ for more details.
    architecture does not implement signaling NaNs according to IEEE-754. On the
    supporting architectures, it can enable additional optimization opportunities.
 
-   If more than one option is specified, the last one takes effect. If none is
-   specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
-   compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
+   If more than one option is specified, the last one takes effect.
 
 .. option:: -ffp-model=<value>
 

>From 42f201c00da09aa3ed85010601835216eb1d7f9c Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Thu, 16 Jul 2026 00:36:55 +0700
Subject: [PATCH 10/10] Fix documentation after rebase

---
 clang/docs/UsersManual.md |  5 +++++
 llvm/docs/LangRef.md      | 23 +++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index f1d99706585b2..a76e69101ea4b 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -1654,6 +1654,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.
@@ -1672,6 +1673,7 @@ floating point semantic models: precise (the default), strict, and fast.
 | allow_approximate_fns | off       | off     | on        | on         |
 | allow_reassociation   | off       | off     | on        | on         |
 | complex_arithmetic    | full      | full    | promoted  | basic      |
+| signaling_nans        | off       | on      | off       | off        |
 
 The `-ffp-model` option does not modify the `fdenormal-fp-math`
 setting, but it does have an impact on whether `crtfastmath.o` is
@@ -1988,6 +1990,7 @@ for more details.
    - 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.
 ```
 
+```{eval-rst}
 .. option:: -f[no-]signaling-nans
 
    Informs the compiler whether signaling NaNs behave according to IEEE 754.
@@ -2016,7 +2019,9 @@ for more details.
    supporting architectures, it can enable additional optimization opportunities.
 
    If more than one option is specified, the last one takes effect.
+```
 
+```{eval-rst}
 .. option:: -ffp-model=<value>
 
    Specify floating point behavior. ``-ffp-model`` is an umbrella
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index f555ca543ffdc..fbc347bd619ba 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2689,6 +2689,18 @@ fn -> other_fn -> other_fn ; fn is norecurse
 :   This attribute indicates that implicit allocation token instrumentation
     is enabled for this function.
 
+`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 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. This attribute requires that the
+    ``strictfp`` attribute also be set.
+
 `speculative_load_hardening`
 :   This attribute indicates that
     [Speculative Load Hardening](https://llvm.org/docs/SpeculativeLoadHardening.html)
@@ -4261,6 +4273,17 @@ specification on some architectures:
 - Older MIPS versions use the opposite polarity for the quiet/signaling bit, and
   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



More information about the flang-commits mailing list