summaryrefslogtreecommitdiff
path: root/inline.h
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2020-08-27 17:08:03 +0100
committerDavid Mitchell <davem@iabyn.com>2020-08-27 17:21:12 +0100
commitcd304e76e0a975a4d5600077b2891ed469708009 (patch)
treeef7325e698dd9c505361828f99bea6ca4324438d /inline.h
parent8672d1554b3c704813a3bb693057e97ace01ffbe (diff)
downloadperl-cd304e76e0a975a4d5600077b2891ed469708009.tar.gz
S_lossless_NV_to_IV(): skip Perl_isnan
This inline function was added by v5.31.0-27-g3a019afd6f to consolidate similar code in several places, like pp_add(). It also avoided undefined behaviour, as seen by ASan, by no longer unconditionally trying to cast an NV to IV - ASan would complain when nv was -Inf for example. However that commit introduced a performance regression into common numeric operators like pp_and(). This commit partially claws back performance by skipping the initial test of 'skip if Nan' which called Perl_isnan(). Instead, except on systems where NAN_COMPARE_BROKEN is true, it relies on NaN being compared to anything always being false, and simply rearranges existing conditions nv < IV_MIN etc to be nv >= IV_MIN so that any NaN comparison will trigger a false return. This claws back about half the performance loss. The rest seems unavoidable, since the two range tests for IV_MIN..IV_MAX are an unavoidable part of avoiding undefined behaviour.
Diffstat (limited to 'inline.h')
-rw-r--r--inline.h10
1 files changed, 6 insertions, 4 deletions
diff --git a/inline.h b/inline.h
index 6fbd5abfea..29ad5a2d4d 100644
--- a/inline.h
+++ b/inline.h
@@ -1962,15 +1962,17 @@ S_lossless_NV_to_IV(const NV nv, IV *ivp)
PERL_ARGS_ASSERT_LOSSLESS_NV_TO_IV;
-# if defined(Perl_isnan)
-
+# if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+ /* Normally any comparison with a NaN returns false; if we can't rely
+ * on that behaviour, check explicitly */
if (UNLIKELY(Perl_isnan(nv))) {
return FALSE;
}
-
# endif
- if (UNLIKELY(nv < IV_MIN) || UNLIKELY(nv > IV_MAX)) {
+ /* Written this way so that with an always-false NaN comparison we
+ * return false */
+ if (!(LIKELY(nv >= IV_MIN) && LIKELY(nv <= IV_MAX))) {
return FALSE;
}