[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 2 00:50:38 PDT 2026
https://github.com/spavloff updated https://github.com/llvm/llvm-project/pull/193055
>From 9f86a2fefe9c8b7e975c53729df8607329fea465 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 | 2 +
clang/docs/UsersManual.rst | 30 +++
clang/include/clang/Basic/CodeGenOptions.def | 6 +
clang/include/clang/Basic/TargetInfo.h | 4 +
clang/include/clang/Options/Options.td | 10 +-
clang/lib/Basic/TargetInfo.cpp | 1 +
clang/lib/Basic/Targets/AArch64.cpp | 1 +
clang/lib/Basic/Targets/PPC.h | 1 +
clang/lib/Basic/Targets/RISCV.h | 1 +
clang/lib/Basic/Targets/SystemZ.h | 1 +
clang/lib/Basic/Targets/X86.h | 1 +
clang/lib/CodeGen/CodeGenFunction.cpp | 11 +
clang/lib/Driver/ToolChains/Clang.cpp | 17 +-
clang/lib/Frontend/InitPreprocessor.cpp | 3 +
clang/test/CodeGen/attr-signaling-nans.c | 14 ++
.../cl20-device-side-enqueue-attributes.cl | 2 +-
clang/test/Driver/clang_f_opts.c | 19 +-
clang/test/Driver/fast-math.c | 4 +-
llvm/docs/LangRef.rst | 19 ++
llvm/docs/ReleaseNotes.md | 3 +
llvm/include/llvm/Analysis/SimplifyQuery.h | 8 +
llvm/include/llvm/IR/FPEnv.h | 6 -
llvm/lib/Analysis/InstructionSimplify.cpp | 16 +-
.../Transforms/InstSimplify/strictfp-fadd.ll | 113 ++++++++++-
.../Transforms/InstSimplify/strictfp-fsub.ll | 189 +++++++++++++++++-
25 files changed, 460 insertions(+), 22 deletions(-)
create mode 100644 clang/test/CodeGen/attr-signaling-nans.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index e4f992f6e0f08..b166816a00441 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -409,6 +409,8 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
reproducable builds. These macros can be redefined from the command line if
necessary. `/d1nodatetime-` can be used to turn this feature off if
necessary to override the common build settings.
+- New option `-f[no-]signaling-nans` added to control the support of
+ signaling NaNs.
### Deprecated Compiler Flags
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 05a1ab7c2022a..7fcc7394635eb 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1574,6 +1574,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.
@@ -1680,6 +1681,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:
@@ -1877,6 +1880,33 @@ for more details.
- The option ``-fno-rounding-math`` allows the compiler to assume that the rounding mode is set to ``FE_TONEAREST``. This is the default.
- The option ``-frounding-math`` forces the compiler to honor the dynamically-set rounding mode. This prevents optimizations which might affect results if the rounding mode changes or is different from the default; for example, it prevents floating-point operations from being reordered across most calls and prevents constant-folding when the result is not exactly representable.
+.. option:: -f[no-]signaling-nans
+
+ Informs the compiler whether signaling NaNs behave according to IEEE 754.
+
+ IEEE 754 defines signaling NaNs (SNaNs) as a subset of Not-a-Numbers (NaNs),
+ which possesses following properties:
+
+ * Floating-point operations, in which an SNaN is an operand, raise the
+ ``Invalid`` exception,
+ * Floating-point operations do not produce SNaNs, only quiet NaN can be a
+ result. Some target architectures do not support SNaNs; only a quiet NaN
+ can be a result.
+
+ The option ``-fsignaling-nans`` specifies IEEE 754 compliant behavior for
+ signaling NaNs. It has no effect if the target architecture does not
+ implements IEEE 754 signaling NaN behavior. This option causes the
+ preprocessor macro ``__SUPPORT_SNAN__`` to be defined.
+
+ The option ``-fno-signaling-nans`` specifies that signaling NaNs are treated
+ in the same way as quiet NaNs. This is the only option allowed if the target
+ architecture does not implement signaling NaNs according to IEEE-754. On
+ supporting architectures, it can enable additional optimization opportunities.
+
+ If more than one option is specified, the last one takes effect. If none is
+ specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
+ compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
+
.. option:: -ffp-model=<value>
Specify floating point behavior. ``-ffp-model`` is an umbrella
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 49374efbeb28b..201021b195dde 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -536,6 +536,12 @@ ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
/// Adds attributes that prevent outlining (`-mno-outline`)
CODEGENOPT(DisableOutlining, 1, 0, Benign)
+/// Whether compiler should treat signaling NaNs according to IEEE 754. Use
+/// separate options for positive and negative settings, this allows us to
+/// represent "unspecified" setting.
+CODEGENOPT(SignalingNans, 1, 0, Benign)
+CODEGENOPT(NoSignalingNans, 1, 0, Benign)
+
/// FIXME: Make DebugOptions its own top-level .def file.
#include "DebugOptions.def"
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index d1914d626c753..372d983f2408d 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -249,6 +249,7 @@ class TargetInfo : public TransferrableTargetInfo,
bool HasLongDouble;
bool HasFPReturn;
bool HasStrictFP;
+ bool HasSignalingNaNs;
unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
std::string DataLayoutString;
@@ -742,6 +743,9 @@ class TargetInfo : public TransferrableTargetInfo,
/// Determine whether constrained floating point is supported on this target.
virtual bool hasStrictFP() const { return HasStrictFP; }
+ /// Determine whether signaling NaNs are supported on this target.
+ virtual bool hasSignalingNaNs() const { return HasSignalingNaNs; }
+
/// Return the alignment that is the largest alignment ever used for any
/// scalar/SIMD data type on the target machine you are compiling for
/// (including types with an extended alignment requirement).
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 9f1f4a5a682ee..e960906a9f788 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3047,6 +3047,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,
@@ -7526,7 +7535,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 9a25384347073..0700985f028f1 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 d56b09553a19b..5bab39b9ae900 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 e3bf5072d932d..f3d1457c753ed 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 193f77fe68a0b..e048a654bf2f4 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 70b529e8ca854..5f181c4759475 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -103,6 +103,7 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
HasFastHalfType = false;
HasStrictFP = true;
+ HasSignalingNaNs = true;
}
unsigned getMinGlobalAlign(uint64_t Size, bool HasNonWeakDef) const override;
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index f9c39b31f5e08..86d69e47d2612 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -195,6 +195,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
LongDoubleFormat = &llvm::APFloat::x87DoubleExtended();
AddrSpaceMap = &X86AddrSpaceMap;
HasStrictFP = true;
+ HasSignalingNaNs = true;
HasUnalignedAccess = true;
bool IsWinCOFF =
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b920266b59808..8f4ef4f45548e 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1004,6 +1004,17 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (D && D->hasAttr<HybridPatchableAttr>())
Fn->addFnAttr(llvm::Attribute::HybridPatchable);
+ if (D) {
+ if (CGM.getCodeGenOpts().SignalingNans) {
+ if (getContext().getTargetInfo().hasSignalingNaNs())
+ Fn->addFnAttr("signaling-nans");
+ } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
+ // Both options are absent, - calculate default setting.
+ if (D->hasAttr<StrictFPAttr>())
+ Fn->addFnAttr("signaling-nans");
+ }
+ }
+
if (D) {
// Function attributes take precedence over command line flags.
if (auto *A = D->getAttr<FunctionReturnThunksAttr>()) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index d776f607fd709..c5dc1b22ac959 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2933,6 +2933,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
@@ -2984,6 +2985,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
@@ -3147,6 +3149,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 =
@@ -3437,6 +3445,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())
@@ -3446,7 +3460,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 ec009211ec6de..07f24397461e3 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1327,6 +1327,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
else
Builder.defineMacro("__FINITE_MATH_ONLY__", "0");
+ if (CGOpts.SignalingNans)
+ Builder.defineMacro("__SUPPORT_SNAN__");
+
if (LangOpts.GNUCVersion) {
if (LangOpts.GNUInline || LangOpts.CPlusPlus)
Builder.defineMacro("__GNUC_GNU_INLINE__");
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
new file mode 100644
index 0000000000000..eef64875f04a6
--- /dev/null
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-signaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+
+float func_01(float x) {
+ return x / 11.0F;
+}
+
+// CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
+
+
+// SIGNALING: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
+// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index 962ac4afa4991..e42105b699e39 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
// SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
// SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
//.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "signaling-nans" "stack-protector-buffer-size"="8" }
// STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
// STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
// STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index 5871f1580d6b7..eb78ddf84d6b7 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -395,7 +395,6 @@
// CHECK-WARNING-DAG: optimization flag '-fprofile-correction' is not supported
// CHECK-WARNING-DAG: optimization flag '-fprofile-values' is not supported
// CHECK-WARNING-DAG: optimization flag '-fschedule-insns' is not supported
-// CHECK-WARNING-DAG: optimization flag '-fsignaling-nans' is not supported
// CHECK-WARNING-DAG: optimization flag '-fstrength-reduce' is not supported
// CHECK-WARNING-DAG: optimization flag '-ftracer' is not supported
// CHECK-WARNING-DAG: optimization flag '-funroll-all-loops' is not supported
@@ -651,3 +650,21 @@
// RUN: %clang -### --target=x86_64-pc-windows-msvc -fno-strict-aliasing %s 2>&1 | FileCheck -check-prefix=CHECK-NO-STRICT-ALIASING %s
// CHECK-STRICT-ALIASING-NOT: -relaxed-aliasing
// CHECK-NO-STRICT-ALIASING: -relaxed-aliasing
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefix=CHECK-UNKNOWN-SIGNALING-NANS %s
+// CHECK-UNKNOWN-SIGNALING-NANS: "-cc1"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fsignaling-nans"
+// CHECK-UNKNOWN-SIGNALING-NANS-NOT: "-fno-signaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
+// CHECK-SIGNALING-NANS: "-cc1"
+// CHECK-SIGNALING-NANS: "-fsignaling-nans"
+// CHECK-SIGNALING-NANS-NOT: "-fno-signaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
+// CHECK-NO-SIGNALING-NANS: "-cc1"
+// CHECK-NO-SIGNALING-NANS: "-fno-signaling-nans"
+// CHECK-NO-SIGNALING-NANS-NOT: "-fsignaling-nans"
+
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fsignaling-nans -fno-signaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SIGNALING-NANS %s
+// RUN: %clang -### -S --target=x86_64-unknown-linux -fno-signaling-nans -fsignaling-nans %s 2>&1 | FileCheck -check-prefix=CHECK-SIGNALING-NANS %s
diff --git a/clang/test/Driver/fast-math.c b/clang/test/Driver/fast-math.c
index ffd081948914d..7d25f9a4dd5b3 100644
--- a/clang/test/Driver/fast-math.c
+++ b/clang/test/Driver/fast-math.c
@@ -143,11 +143,11 @@
// RUN: %clang -### -fno-fast-math -ffast-math -c %s 2>&1 \
// RUN: | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,REASSOC,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
// RUN: %clang -### -funsafe-math-optimizations -ffinite-math-only \
-// RUN: -fno-math-errno -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \
+// RUN: -fno-math-errno -ffp-contract=fast -fno-rounding-math -fno-signaling-nans -c %s 2>&1 \
// RUN: | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
// RUN: %clang -### -fno-honor-infinities -fno-honor-nans -fno-math-errno \
// RUN: -fassociative-math -freciprocal-math -fno-signed-zeros -fapprox-func \
-// RUN: -fno-trapping-math -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \
+// RUN: -fno-trapping-math -ffp-contract=fast -fno-rounding-math -fno-signaling-nans -c %s 2>&1 \
// RUN: | FileCheck --check-prefixes=CHECK,FAST,NINF,NNAN,FINITE-ONLY,NSZ,ARCP,AFN,CONTRACT-FAST,NO-ERRNO,NOROUNDING %s
//
// RUN: %clang -### -ffast-math -fno-fast-math -c %s 2>&1 \
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 4d880494eb449..2707ac894a0e3 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2630,6 +2630,14 @@ For example:
``sanitize_alloc_token``
This attribute indicates that implicit allocation token instrumentation
is enabled for this function.
+``signaling-nans``
+ If a function has this attribute, signaling NaNs are assumed to be treated
+ according to IEEE 754 rules. That is, signaling NaN values are quieted in
+ arithmetic operations, and the floating-point exception ``Invalid`` is
+ raised if an operand of such an operation is a signaling NaN. If this
+ attribute is absent, signaling NaNs are assumed to be treated identically to
+ quiet NaNs. This attribute cannot be set if the target architecture does not
+ support IEEE 754 compatible signaling NaN handling.
``speculative_load_hardening``
This attribute indicates that
`Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
@@ -4185,6 +4193,17 @@ specification on some architectures:
LLVM does not correctly represent this. See `issue #60796
<https://github.com/llvm/llvm-project/issues/60796>`_.
+The behavior of signaling NaNs in runtime is determined by the underlying
+hardware capabilities and the specified function attributes. If the target
+hardware implements IEEE 754 and the function has the "signaling-nans"
+attribute, signaling NaNs are handled according to the specification. They
+undergo conversion to quiet NaNs in arithmetic operations, which raise the
+``Invalid`` exception. If the attribute is absent, signaling NaNs are handled in
+the same way as quiet NaNs. The attribute may be absent even on supporting
+hardware, as it can enable additional optimization opportunities. If the
+hardware does not support signaling NaNs, the "signaling-nans" attribute
+cannot be applied.
+
.. _floatsem:
Floating-Point Semantics
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 005e0b141cb7b..0539851ef8d6e 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -68,6 +68,9 @@ Makes programs 10x faster by doing Special New Thing.
null pointers. Such constants may print as `splat (ptr null)` instead of
`zeroinitializer`.
+* The function attribute "signaling-nans" was added to control the support of
+ signaling NaNs.
+
* LLVM IR floating-point literals have greatly changed:
* The old hexadecimal bitwise representation is deprecated and will be removed
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index b81b1dae27471..42427a475c283 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -126,6 +126,14 @@ struct SimplifyQuery {
/// Otherwise always return false.
LLVM_ABI bool isUndefValue(Value *V) const;
+ bool hasSignalingNaNs() const {
+ if (CxtI)
+ if (const BasicBlock *BB = CxtI->getParent())
+ if (const Function *F = BB->getParent())
+ return F->hasFnAttribute("signaling-nans");
+ return false;
+ }
+
SimplifyQuery getWithoutDomCondCache() const {
SimplifyQuery Copy(*this);
Copy.DC = nullptr;
diff --git a/llvm/include/llvm/IR/FPEnv.h b/llvm/include/llvm/IR/FPEnv.h
index 38395b15c8c09..a3b7d1c26972d 100644
--- a/llvm/include/llvm/IR/FPEnv.h
+++ b/llvm/include/llvm/IR/FPEnv.h
@@ -80,11 +80,5 @@ LLVM_ABI Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr);
inline bool canRoundingModeBe(RoundingMode RM, RoundingMode QRM) {
return RM == QRM || RM == RoundingMode::Dynamic;
}
-
-/// Returns true if the possibility of a signaling NaN can be safely
-/// ignored.
-inline bool canIgnoreSNaN(fp::ExceptionBehavior EB, FastMathFlags FMF) {
- return (EB == fp::ebIgnore || FMF.noNaNs());
-}
}
#endif
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index fcd7f68260856..33b15d0c305f1 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);
@@ -5975,14 +5979,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;
@@ -6037,14 +6041,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;
@@ -6052,13 +6056,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 2246a5f6bd4f3c99e2edcc0bd39e3ce37bb348b9 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.rst | 22 +-
clang/include/clang/Basic/TargetInfo.h | 3 +
clang/lib/CodeGen/CodeGenFunction.cpp | 7 +-
clang/lib/Driver/ToolChains/Clang.cpp | 2 +
clang/test/Driver/fp-model.c | 14 ++
clang/test/Preprocessor/predefined-macros.c | 8 +
llvm/docs/LangRef.rst | 18 +-
llvm/include/llvm/Analysis/SimplifyQuery.h | 8 -
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 +
llvm/include/llvm/IR/Attributes.td | 6 +
llvm/include/llvm/IR/FPEnv.h | 6 +
llvm/include/llvm/IR/Function.h | 5 +
llvm/lib/Analysis/InstructionSimplify.cpp | 16 +-
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 +
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 +
llvm/lib/IR/Attributes.cpp | 5 +
llvm/test/Bitcode/attributes.ll | 7 +
.../Inline/inline-signaling-nans.ll | 47 +++++
.../Transforms/InstSimplify/strictfp-fadd.ll | 113 +----------
.../Transforms/InstSimplify/strictfp-fsub.ll | 189 +-----------------
20 files changed, 144 insertions(+), 337 deletions(-)
create mode 100644 llvm/test/Transforms/Inline/inline-signaling-nans.ll
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 7fcc7394635eb..d47fa91b2d63a 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1884,23 +1884,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 372d983f2408d..4dab4ce48f5aa 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -744,6 +744,9 @@ class TargetInfo : public TransferrableTargetInfo,
virtual bool hasStrictFP() const { return HasStrictFP; }
/// Determine whether signaling NaNs are supported on this target.
+ ///
+ /// A target can override this method if the support of signaling NaNs depends
+ /// on processor features or any other target options.
virtual bool hasSignalingNaNs() const { return HasSignalingNaNs; }
/// Return the alignment that is the largest alignment ever used for any
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 8f4ef4f45548e..69f3585e0cfe4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1004,14 +1004,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (D && D->hasAttr<HybridPatchableAttr>())
Fn->addFnAttr(llvm::Attribute::HybridPatchable);
- if (D) {
+ if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
if (CGM.getCodeGenOpts().SignalingNans) {
- if (getContext().getTargetInfo().hasSignalingNaNs())
- Fn->addFnAttr("signaling-nans");
+ Fn->addFnAttr(llvm::Attribute::SignalingNans);
} else if (!CGM.getCodeGenOpts().NoSignalingNans) {
// Both options are absent, - calculate default setting.
if (D->hasAttr<StrictFPAttr>())
- Fn->addFnAttr("signaling-nans");
+ Fn->addFnAttr(llvm::Attribute::SignalingNans);
}
}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index c5dc1b22ac959..7e73253a15721 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3084,6 +3084,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)
@@ -3113,6 +3114,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 2dd4b78238a7b..727650be1a154 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -349,3 +349,11 @@
// CHECK-HIPSTDPAR-INTERPOSE-DEV-NEG: #define __HIPSTDPAR__ 1
// CHECK-HIPSTDPAR-INTERPOSE-DEV-NEG-NOT: #define __HIPSTDPAR_INTERPOSE_ALLOC_V1__ 1
// CHECK-HIPSTDPAR-INTERPOSE-DEV-NEG-NOT: #define __HIPSTDPAR_INTERPOSE_ALLOC__ 1
+
+// RUN: %clang_cc1 %s -E -dM -fsignaling-nans -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-SIGNALING-NANS
+// CHECK-SIGNALING-NANS: #define __SUPPORT_SNAN__
+
+// RUN: %clang_cc1 %s -E -dM -fno-signaling-nans -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-NO-SIGNALING-NANS
+// CHECK-NO-SIGNALING-NANS-NOT: __SUPPORT_SNAN__
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 2707ac894a0e3..3006ab0dc8596 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2630,14 +2630,16 @@ For example:
``sanitize_alloc_token``
This attribute indicates that implicit allocation token instrumentation
is enabled for this function.
-``signaling-nans``
- If a function has this attribute, signaling NaNs are assumed to be treated
- according to IEEE 754 rules. That is, signaling NaN values are quieted in
- arithmetic operations, and the floating-point exception ``Invalid`` is
- raised if an operand of such an operation is a signaling NaN. If this
- attribute is absent, signaling NaNs are assumed to be treated identically to
- quiet NaNs. This attribute cannot be set if the target architecture does not
- support IEEE 754 compatible signaling NaN handling.
+``signaling_nans``
+ If a function has this attribute, a signaling NaN (sNaN) is assumed to be
+ treated according to IEEE 754 rules. That is, arithmetic operations never
+ return an sNaN, and the ``Invalid`` exception is raised if an operand of
+ such an operation is an sNaN. If this attribute is absent, an sNaN is assumed
+ to be treated identically to a quiet NaN: it can be produced by an operation
+ (so that the transformation `x + 0 -> x` becomes valid), and an operation on
+ such a NaN does not raise the "Invalid" exception. This attribute cannot be
+ set if the target architecture does not support IEEE 754 compatible
+ sNaN handling.
``speculative_load_hardening``
This attribute indicates that
`Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h
index 42427a475c283..b81b1dae27471 100644
--- a/llvm/include/llvm/Analysis/SimplifyQuery.h
+++ b/llvm/include/llvm/Analysis/SimplifyQuery.h
@@ -126,14 +126,6 @@ struct SimplifyQuery {
/// Otherwise always return false.
LLVM_ABI bool isUndefValue(Value *V) const;
- bool hasSignalingNaNs() const {
- if (CxtI)
- if (const BasicBlock *BB = CxtI->getParent())
- if (const Function *F = BB->getParent())
- return F->hasFnAttribute("signaling-nans");
- return false;
- }
-
SimplifyQuery getWithoutDomCondCache() const {
SimplifyQuery Copy(*this);
Copy.DC = nullptr;
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index af4cac996a78c..f3f200ebcdb01 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -821,6 +821,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 33b15d0c305f1..fcd7f68260856 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);
@@ -5979,14 +5975,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;
@@ -6041,14 +6037,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;
@@ -6056,13 +6052,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 2bd251efd05ce..115bf3313fdf4 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2309,6 +2309,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 c4058fe66ff0b..9424f77849a02 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1010,6 +1010,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 22393a8180a937078d20980d20f6f5b5fdd5fada Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 27 Apr 2026 23:08:31 +0700
Subject: [PATCH 03/10] Fix tests and documentation
Also changes intersect behavior to IntersectPreserve, because
IntersectCustom is implemented only forIntAttr.
---
clang/test/CodeGen/attr-signaling-nans.c | 4 ++--
clang/test/CodeGen/fp-floatcontrol-stack.cpp | 12 ++++++------
.../cl20-device-side-enqueue-attributes.cl | 2 +-
clang/test/Options/Gis.hlsl | 2 +-
llvm/docs/LangRef.rst | 12 ++++++------
llvm/include/llvm/IR/Attributes.td | 3 ++-
llvm/lib/IR/Attributes.cpp | 5 -----
7 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index eef64875f04a6..2cfbfc5b9b6f5 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -10,5 +10,5 @@ float func_01(float x) {
// CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
-// SIGNALING: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
-// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} "signaling-nans" {{.*}}}
+// SIGNALING: attributes #[[ATTR]] = {{{.*}} signaling_nans {{.*}}}
+// NO-SIGNALING-NOT: attributes #[[ATTR]] = {{{.*}} signaling_nans {{.*}}}
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 237c9d4f9a37e..098bd9a73eaa1 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -7,7 +7,7 @@
(float z) { return n * z + n; }
// CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
-// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+// CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
// CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
// CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
float fun_default FUN(1)
@@ -33,9 +33,9 @@ float fun_default FUN(1)
#pragma float_control(except, on)
#endif
// CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
- // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
- // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
- // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+ // CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+ // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
+ // CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
float exc_on FUN(2)
//CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
#if DEFAULT
@@ -55,7 +55,7 @@ float fun_default FUN(1)
#pragma float_control(pop)
// CHECK-DDEFAULT: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
- // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone strictfp{{$$}}
+ // CHECK-DEBSTRICT: Function Attrs: mustprogress noinline nounwind optnone signaling_nans strictfp{{$$}}
// CHECK-FAST: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
// CHECK-NOHONOR: Function Attrs: mustprogress noinline nounwind optnone{{$$}}
float exc_pop FUN(5)
@@ -76,7 +76,7 @@ float fun_default FUN(1)
#pragma float_control(except, off)
float exc_off FUN(5)
-//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}
+//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}{
#if DEFAULT
//CHECK-DDEFAULT: call float @llvm.fmuladd{{.*}}
#endif
diff --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index e42105b699e39..a05327e1aa322 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -203,7 +203,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
// SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
// SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size" }
//.
-// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "signaling-nans" "stack-protector-buffer-size"="8" }
+// STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp signaling_nans "stack-protector-buffer-size"="8" }
// STRICTFP: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
// STRICTFP: attributes #[[ATTR2]] = { convergent noinline nounwind optnone strictfp "stack-protector-buffer-size"="8" }
// STRICTFP: attributes #[[ATTR3:[0-9]+]] = { nocallback nofree nosync nounwind strictfp willreturn memory(inaccessiblemem: readwrite) }
diff --git a/clang/test/Options/Gis.hlsl b/clang/test/Options/Gis.hlsl
index c60fd1854bdc9..4191236889d39 100644
--- a/clang/test/Options/Gis.hlsl
+++ b/clang/test/Options/Gis.hlsl
@@ -1,7 +1,7 @@
// RUN: %clang_dxc -T lib_6_4 -Gis %s 2>&1 -### | FileCheck -check-prefix=Gis %s
// RUN: %clang_dxc -T lib_6_4 %s 2>&1 -### | FileCheck -check-prefix=NO_Gis %s
-// Gis: "-ffp-contract=off" "-frounding-math" "-ffp-exception-behavior=strict" "-complex-range=full"
+// Gis: "-ffp-contract=off" "-frounding-math" "-ffp-exception-behavior=strict" "-fsignaling-nans" "-complex-range=full"
// assert expected floating point options are present
// NO_Gis-NOT: "-ffp-contract=off"
// NO_Gis-NOT: "-frounding-math"
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 3006ab0dc8596..516926f4c826e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2634,12 +2634,12 @@ For example:
If a function has this attribute, a signaling NaN (sNaN) is assumed to be
treated according to IEEE 754 rules. That is, arithmetic operations never
return an sNaN, and the ``Invalid`` exception is raised if an operand of
- such an operation is an sNaN. If this attribute is absent, an sNaN is assumed
- to be treated identically to a quiet NaN: it can be produced by an operation
- (so that the transformation `x + 0 -> x` becomes valid), and an operation on
- such a NaN does not raise the "Invalid" exception. This attribute cannot be
- set if the target architecture does not support IEEE 754 compatible
- sNaN handling.
+ such an operation is an sNaN and strictfp is also enabled. If this attribute
+ is absent, an sNaN is assumed to be treated identically to a quiet NaN: it
+ can be produced by an operation (so that the transformation `x + 0 -> x`
+ becomes valid), and an operation on such a NaN does not raise the "Invalid"
+ exception. This attribute cannot be set if the target architecture does not
+ support IEEE 754 compatible sNaN handling.
``speculative_load_hardening``
This attribute indicates that
`Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 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 8750cee27d439d420a0c967789a6055d188ad22c 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.rst | 3 +--
clang/include/clang/Basic/CodeGenOptions.def | 5 +----
clang/include/clang/Options/Options.td | 17 ++++++++---------
clang/lib/CodeGen/CodeGenFunction.cpp | 7 +------
clang/lib/Driver/ToolChains/Clang.cpp | 12 ++++--------
clang/test/CodeGen/attr-signaling-nans.c | 2 +-
clang/test/CodeGen/fp-floatcontrol-stack.cpp | 10 +++++-----
.../cl20-device-side-enqueue-attributes.cl | 2 +-
clang/test/Driver/clang_f_opts.c | 3 ---
clang/test/Driver/fp-model.c | 8 ++++----
clang/test/Preprocessor/predefined-macros.c | 2 +-
11 files changed, 27 insertions(+), 44 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index d47fa91b2d63a..e97544cc0df0b 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1908,8 +1908,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 201021b195dde..1f6505382ffbc 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -536,11 +536,8 @@ ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
/// Adds attributes that prevent outlining (`-mno-outline`)
CODEGENOPT(DisableOutlining, 1, 0, Benign)
-/// Whether compiler should treat signaling NaNs according to IEEE 754. Use
-/// separate options for positive and negative settings, this allows us to
-/// represent "unspecified" setting.
+/// Whether compiler should treat signaling NaNs according to IEEE 754.
CODEGENOPT(SignalingNans, 1, 0, Benign)
-CODEGENOPT(NoSignalingNans, 1, 0, Benign)
/// FIXME: Make DebugOptions its own top-level .def file.
#include "DebugOptions.def"
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index e960906a9f788..973082dc8700f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3047,15 +3047,14 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
" Default is 'fast' for CUDA, 'fast-honor-pragmas' for HIP, and 'on' otherwise.">,
HelpText<"Form fused FP ops (e.g. FMAs)">,
Values<"fast,on,off,fast-honor-pragmas">;
-def fsignaling_nans : Flag<["-"], "fsignaling-nans">,
- Visibility<[ClangOption, CC1Option]>,
- MarshallingInfoFlag<CodeGenOpts<"SignalingNans">>,
- HelpText<"Assume signaling NaNs behave according to IEEE rules and can"
- "raise floating-point exceptions">;
-def fno_signaling_nans : Flag<["-"], "fno-signaling-nans">,
- Visibility<[ClangOption, CC1Option]>,
- MarshallingInfoFlag<CodeGenOpts<"NoSignalingNans">>,
- HelpText<"Assume signaling NaNs are treated identically to quiet NaNs">;
+defm signaling_nans
+ : BoolFOption<"signaling-nans", CodeGenOpts<"SignalingNans">, DefaultFalse,
+ PosFlag<SetTrue, [], [ClangOption, CC1Option],
+ "Assume signaling NaNs behave according to IEEE rules "
+ "and can raise floating-point exceptions">,
+ NegFlag<SetFalse, [], [ClangOption],
+ "Assume signaling NaNs are treated identically to "
+ "quiet NaNs">>;
defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 69f3585e0cfe4..92d305e209664 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1005,13 +1005,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
Fn->addFnAttr(llvm::Attribute::HybridPatchable);
if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
- if (CGM.getCodeGenOpts().SignalingNans) {
+ if (CGM.getCodeGenOpts().SignalingNans)
Fn->addFnAttr(llvm::Attribute::SignalingNans);
- } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
- // Both options are absent, - calculate default setting.
- if (D->hasAttr<StrictFPAttr>())
- Fn->addFnAttr(llvm::Attribute::SignalingNans);
- }
}
if (D) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 7e73253a15721..bbc394611b161 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2933,7 +2933,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
@@ -3447,12 +3447,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())
@@ -3463,7 +3459,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 727650be1a154..08e1bb6ffaba1 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -354,6 +354,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 a84ccc4ff08338fcc6f38eb20045d8f296cf9a22 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 d9e4858f5f648..f52d2ae566eab 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 8aee45b5dc644..d37092bd94941 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -153,6 +153,11 @@ bool CompilerInstance::createTarget() {
// FIXME: can we disable FEnvAccess?
}
+ if (!getTarget().hasSignalingNaNs() && getCodeGenOpts().SignalingNans) {
+ getDiagnostics().Report(diag::warn_fe_backend_unsupported_signaling_nans);
+ getCodeGenOpts().SignalingNans = false;
+ }
+
// We should do it here because target knows nothing about
// language options when it's being created.
if (getLangOpts().OpenCL &&
diff --git a/clang/test/CodeGen/attr-signaling-nans.c b/clang/test/CodeGen/attr-signaling-nans.c
index c2715f20a88d0..4f58936ceed65 100644
--- a/clang/test/CodeGen/attr-signaling-nans.c
+++ b/clang/test/CodeGen/attr-signaling-nans.c
@@ -1,12 +1,14 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,SIGNALING
// Here msp430 is used as a target that do not support signaling NaNs.
-// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
+// RUN: %clang_cc1 -triple msp430-unknown-unknown -fsignaling-nans -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NO-SIGNALING
float func_01(float x) {
return x / 11.0F;
}
+// expected-warning@*{{signaling NaNs are unsupported on this target}}
+
// CHECK: define{{.*}} float @func_01(float noundef{{.*}}) #[[ATTR:[0-9]+]] {
>From bb07c8ebda9411b1b5db8eb2981a2f51fd4c3b40 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 975294ab00f73..5304fa28e1900 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 542dbde33a097bd6c43aeb79b0eaafae6e08161d 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 cdbbb61c7bb7031d4d63e249a6883d9b7590c8fb Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 25 May 2026 23:03:40 +0700
Subject: [PATCH 08/10] Document that `signaling_nans` requires `strictfp`.
---
llvm/docs/LangRef.rst | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 516926f4c826e..1549c302bc372 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2639,7 +2639,8 @@ For example:
can be produced by an operation (so that the transformation `x + 0 -> x`
becomes valid), and an operation on such a NaN does not raise the "Invalid"
exception. This attribute cannot be set if the target architecture does not
- support IEEE 754 compatible sNaN handling.
+ support IEEE 754 compatible sNaN handling. This attribute requires that the
+ ``strictfp`` attribute also be set.
``speculative_load_hardening``
This attribute indicates that
`Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
>From 5e1c439076648ebe50e2a6755d76d89db9c81ca1 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 15 Jun 2026 00:14:58 +0700
Subject: [PATCH 09/10] Revert "Remove default setting signaling_nan attribute
for strictfp functions"
Restore the previous behavior, where a strictfp function implicitly got
the `singaling_nans` attribute. Difficulty in explaining the behavior to
users is not an acceptable reason for changing the default behavior.
Previously, this behavior was also undocumented. Assuming
`signaling_nans` by default in `strictfp` functions is safer and
maintains compatibility.
This reverts commit 1c9601c52e8f396d024e4c3032047dce87b288b8.
---
clang/docs/UsersManual.rst | 3 ++-
clang/include/clang/Basic/CodeGenOptions.def | 5 ++++-
clang/include/clang/Options/Options.td | 17 +++++++++--------
clang/lib/CodeGen/CodeGenFunction.cpp | 7 ++++++-
clang/lib/Driver/ToolChains/Clang.cpp | 12 ++++++++----
clang/test/CodeGen/attr-signaling-nans.c | 2 +-
clang/test/CodeGen/fp-floatcontrol-stack.cpp | 10 +++++-----
clang/test/Driver/clang_f_opts.c | 3 +++
clang/test/Driver/fp-model.c | 8 ++++----
clang/test/Preprocessor/predefined-macros.c | 2 +-
10 files changed, 43 insertions(+), 26 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index e97544cc0df0b..d47fa91b2d63a 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1908,7 +1908,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 1f6505382ffbc..201021b195dde 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -536,8 +536,11 @@ ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
/// Adds attributes that prevent outlining (`-mno-outline`)
CODEGENOPT(DisableOutlining, 1, 0, Benign)
-/// Whether compiler should treat signaling NaNs according to IEEE 754.
+/// Whether compiler should treat signaling NaNs according to IEEE 754. Use
+/// separate options for positive and negative settings, this allows us to
+/// represent "unspecified" setting.
CODEGENOPT(SignalingNans, 1, 0, Benign)
+CODEGENOPT(NoSignalingNans, 1, 0, Benign)
/// FIXME: Make DebugOptions its own top-level .def file.
#include "DebugOptions.def"
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 973082dc8700f..e960906a9f788 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3047,14 +3047,15 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
" Default is 'fast' for CUDA, 'fast-honor-pragmas' for HIP, and 'on' otherwise.">,
HelpText<"Form fused FP ops (e.g. FMAs)">,
Values<"fast,on,off,fast-honor-pragmas">;
-defm signaling_nans
- : BoolFOption<"signaling-nans", CodeGenOpts<"SignalingNans">, DefaultFalse,
- PosFlag<SetTrue, [], [ClangOption, CC1Option],
- "Assume signaling NaNs behave according to IEEE rules "
- "and can raise floating-point exceptions">,
- NegFlag<SetFalse, [], [ClangOption],
- "Assume signaling NaNs are treated identically to "
- "quiet NaNs">>;
+def fsignaling_nans : Flag<["-"], "fsignaling-nans">,
+ Visibility<[ClangOption, CC1Option]>,
+ MarshallingInfoFlag<CodeGenOpts<"SignalingNans">>,
+ HelpText<"Assume signaling NaNs behave according to IEEE rules and can"
+ "raise floating-point exceptions">;
+def fno_signaling_nans : Flag<["-"], "fno-signaling-nans">,
+ Visibility<[ClangOption, CC1Option]>,
+ MarshallingInfoFlag<CodeGenOpts<"NoSignalingNans">>,
+ HelpText<"Assume signaling NaNs are treated identically to quiet NaNs">;
defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 92d305e209664..69f3585e0cfe4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1005,8 +1005,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
Fn->addFnAttr(llvm::Attribute::HybridPatchable);
if (D && getContext().getTargetInfo().hasSignalingNaNs()) {
- if (CGM.getCodeGenOpts().SignalingNans)
+ if (CGM.getCodeGenOpts().SignalingNans) {
Fn->addFnAttr(llvm::Attribute::SignalingNans);
+ } else if (!CGM.getCodeGenOpts().NoSignalingNans) {
+ // Both options are absent, - calculate default setting.
+ if (D->hasAttr<StrictFPAttr>())
+ Fn->addFnAttr(llvm::Attribute::SignalingNans);
+ }
}
if (D) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index bbc394611b161..7e73253a15721 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2933,7 +2933,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
@@ -3447,8 +3447,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())
@@ -3459,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)
+ (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 08e1bb6ffaba1..727650be1a154 100644
--- a/clang/test/Preprocessor/predefined-macros.c
+++ b/clang/test/Preprocessor/predefined-macros.c
@@ -354,6 +354,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 c5dbfb76f9d17d7ff45dc8c9135cc70ec3ea3602 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Mon, 15 Jun 2026 00:37:16 +0700
Subject: [PATCH 10/10] Fix documentation
---
clang/docs/UsersManual.rst | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index d47fa91b2d63a..ba0082a675592 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1596,6 +1596,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"
+ "fsignaling-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
@@ -1649,6 +1650,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.
@@ -1681,8 +1684,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:
@@ -1907,9 +1908,7 @@ for more details.
architecture does not implement signaling NaNs according to IEEE-754. On the
supporting architectures, it can enable additional optimization opportunities.
- If more than one option is specified, the last one takes effect. If none is
- specified, the compiler assumes ``-fno-signaling-nans``, unless the code is
- compiled as strictfp functions, in which case ``-fsignaling-nans`` is assumed.
+ If more than one option is specified, the last one takes effect.
.. option:: -ffp-model=<value>
More information about the flang-commits
mailing list