diff options
author | Serge Pavlov <sepavloff@gmail.com> | 2021-08-04 15:27:49 +0700 |
---|---|---|
committer | Serge Pavlov <sepavloff@gmail.com> | 2021-08-04 15:27:49 +0700 |
commit | 16ff91ebccda1128c43ff3cee104e2c603569fb2 (patch) | |
tree | 445ddf072aaecd9eccc7ad5f810c0d0ebcd5b957 /llvm/lib/Analysis/ConstantFolding.cpp | |
parent | 2f002817fb462d01d26374015421a24fa2a5a676 (diff) | |
download | llvm-16ff91ebccda1128c43ff3cee104e2c603569fb2.zip llvm-16ff91ebccda1128c43ff3cee104e2c603569fb2.tar.gz llvm-16ff91ebccda1128c43ff3cee104e2c603569fb2.tar.bz2 |
Introduce intrinsic llvm.isnan
Clang has builtin function '__builtin_isnan', which implements C
library function 'isnan'. This function now is implemented entirely in
clang codegen, which expands the function into set of IR operations.
There are three mechanisms by which the expansion can be made.
* The most common mechanism is using an unordered comparison made by
instruction 'fcmp uno'. This simple solution is target-independent
and works well in most cases. It however is not suitable if floating
point exceptions are tracked. Corresponding IEEE 754 operation and C
function must never raise FP exception, even if the argument is a
signaling NaN. Compare instructions usually does not have such
property, they raise 'invalid' exception in such case. So this
mechanism is unsuitable when exception behavior is strict. In
particular it could result in unexpected trapping if argument is SNaN.
* Another solution was implemented in https://reviews.llvm.org/D95948.
It is used in the cases when raising FP exceptions by 'isnan' is not
allowed. This solution implements 'isnan' using integer operations.
It solves the problem of exceptions, but offers one solution for all
targets, however some can do the check in more efficient way.
* Solution implemented by https://reviews.llvm.org/D96568 introduced a
hook 'clang::TargetCodeGenInfo::testFPKind', which injects target
specific code into IR. Now only SystemZ implements this hook and it
generates a call to target specific intrinsic function.
Although these mechanisms allow to implement 'isnan' with enough
efficiency, expanding 'isnan' in clang has drawbacks:
* The operation 'isnan' is hidden behind generic integer operations or
target-specific intrinsics. It complicates analysis and can prevent
some optimizations.
* IR can be created by tools other than clang, in this case treatment
of 'isnan' has to be duplicated in that tool.
Another issue with the current implementation of 'isnan' comes from the
use of options '-ffast-math' or '-fno-honor-nans'. If such option is
specified, 'fcmp uno' may be optimized to 'false'. It is valid
optimization in general, but it results in 'isnan' always returning
'false'. For example, in some libc++ implementations the following code
returns 'false':
std::isnan(std::numeric_limits<float>::quiet_NaN())
The options '-ffast-math' and '-fno-honor-nans' imply that FP operation
operands are never NaNs. This assumption however should not be applied
to the functions that check FP number properties, including 'isnan'. If
such function returns expected result instead of actually making
checks, it becomes useless in many cases. The option '-ffast-math' is
often used for performance critical code, as it can speed up execution
by the expense of manual treatment of corner cases. If 'isnan' returns
assumed result, a user cannot use it in the manual treatment of NaNs
and has to invent replacements, like making the check using integer
operations. There is a discussion in https://reviews.llvm.org/D18513#387418,
which also expresses the opinion, that limitations imposed by
'-ffast-math' should be applied only to 'math' functions but not to
'tests'.
To overcome these drawbacks, this change introduces a new IR intrinsic
function 'llvm.isnan', which realizes the check as specified by IEEE-754
and C standards in target-agnostic way. During IR transformations it
does not undergo undesirable optimizations. It reaches instruction
selection, where is lowered in target-dependent way. The lowering can
vary depending on options like '-ffast-math' or '-ffp-model' so the
resulting code satisfies requested semantics.
Differential Revision: https://reviews.llvm.org/D104854
Diffstat (limited to 'llvm/lib/Analysis/ConstantFolding.cpp')
-rw-r--r-- | llvm/lib/Analysis/ConstantFolding.cpp | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index b28a0d6..4a943a1 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -1579,9 +1579,10 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { return !Call->isStrictFP(); // Sign operations are actually bitwise operations, they do not raise - // exceptions even for SNANs. + // exceptions even for SNANs. The same applies to classification functions. case Intrinsic::fabs: case Intrinsic::copysign: + case Intrinsic::isnan: // Non-constrained variants of rounding operations means default FP // environment, they can be folded in any case. case Intrinsic::ceil: @@ -2002,6 +2003,9 @@ static Constant *ConstantFoldScalarCall1(StringRef Name, return ConstantInt::get(Ty, Int); } + if (IntrinsicID == Intrinsic::isnan) + return ConstantInt::get(Ty, U.isNaN()); + if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy()) return nullptr; |