diff options
author | David Mitchell <davem@iabyn.com> | 2020-08-27 17:08:03 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2020-08-27 17:21:12 +0100 |
commit | cd304e76e0a975a4d5600077b2891ed469708009 (patch) | |
tree | ef7325e698dd9c505361828f99bea6ca4324438d /inline.h | |
parent | 8672d1554b3c704813a3bb693057e97ace01ffbe (diff) | |
download | perl-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.h | 10 |
1 files changed, 6 insertions, 4 deletions
@@ -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; } |