78f1eb
From 3a019afd6f6291c3249c254b5c01e244e4ec83ab Mon Sep 17 00:00:00 2001
78f1eb
From: Karl Williamson <khw@cpan.org>
78f1eb
Date: Sun, 28 Apr 2019 17:42:44 -0600
78f1eb
Subject: [PATCH 1/3] Create fcn for lossless conversion of NV to IV
78f1eb
MIME-Version: 1.0
78f1eb
Content-Type: text/plain; charset=UTF-8
78f1eb
Content-Transfer-Encoding: 8bit
78f1eb
78f1eb
Essentially the same code was being used in three places, and had
78f1eb
undefined C behavior for some inputs.
78f1eb
78f1eb
This consolidates the code into one inline function, and rewrites it to
78f1eb
avoid undefined behavior.
78f1eb
78f1eb
Signed-off-by: Petr Písař <ppisar@redhat.com>
78f1eb
---
78f1eb
 embed.fnc |  1 +
78f1eb
 embed.h   |  3 +++
78f1eb
 inline.h  | 34 ++++++++++++++++++++++++++++++++++
78f1eb
 pp.c      | 20 ++++----------------
78f1eb
 pp_hot.c  | 10 ++--------
78f1eb
 proto.h   |  7 +++++++
78f1eb
 6 files changed, 51 insertions(+), 24 deletions(-)
78f1eb
78f1eb
diff --git a/embed.fnc b/embed.fnc
78f1eb
index 45597f67b6..259affded0 100644
78f1eb
--- a/embed.fnc
78f1eb
+++ b/embed.fnc
78f1eb
@@ -2272,6 +2272,7 @@ sR	|SV*	|refto		|NN SV* sv
78f1eb
 : Used in pp_hot.c
78f1eb
 pRxo	|GV*	|softref2xv	|NN SV *const sv|NN const char *const what \
78f1eb
 				|const svtype type|NN SV ***spp
78f1eb
+inR	|bool	|lossless_NV_to_IV|const NV nv|NN IV * ivp
78f1eb
 #endif
78f1eb
 
78f1eb
 #if defined(PERL_IN_PP_PACK_C)
78f1eb
diff --git a/embed.h b/embed.h
78f1eb
index 75c91f77f4..9178c51e92 100644
78f1eb
--- a/embed.h
78f1eb
+++ b/embed.h
78f1eb
@@ -1924,6 +1924,9 @@
78f1eb
 #define do_delete_local()	S_do_delete_local(aTHX)
78f1eb
 #define refto(a)		S_refto(aTHX_ a)
78f1eb
 #  endif
78f1eb
+#  if defined(PERL_IN_PP_C) || defined(PERL_IN_PP_HOT_C)
78f1eb
+#define lossless_NV_to_IV	S_lossless_NV_to_IV
78f1eb
+#  endif
78f1eb
 #  if defined(PERL_IN_PP_CTL_C)
78f1eb
 #define check_type_and_open(a)	S_check_type_and_open(aTHX_ a)
78f1eb
 #define destroy_matcher(a)	S_destroy_matcher(aTHX_ a)
78f1eb
diff --git a/inline.h b/inline.h
78f1eb
index 654f801b75..de1e33e8ce 100644
78f1eb
--- a/inline.h
78f1eb
+++ b/inline.h
78f1eb
@@ -1913,6 +1913,40 @@ S_should_warn_nl(const char *pv) {
78f1eb
 
78f1eb
 #endif
78f1eb
 
78f1eb
+#if defined(PERL_IN_PP_C) || defined(PERL_IN_PP_HOT_C)
78f1eb
+
78f1eb
+PERL_STATIC_INLINE bool
78f1eb
+S_lossless_NV_to_IV(const NV nv, IV *ivp)
78f1eb
+{
78f1eb
+    /* This function determines if the input NV 'nv' may be converted without
78f1eb
+     * loss of data to an IV.  If not, it returns FALSE taking no other action.
78f1eb
+     * But if it is possible, it does the conversion, returning TRUE, and
78f1eb
+     * storing the converted result in '*ivp' */
78f1eb
+
78f1eb
+    PERL_ARGS_ASSERT_LOSSLESS_NV_TO_IV;
78f1eb
+
78f1eb
+#  if  defined(Perl_isnan)
78f1eb
+
78f1eb
+    if (UNLIKELY(Perl_isnan(nv))) {
78f1eb
+        return FALSE;
78f1eb
+    }
78f1eb
+
78f1eb
+#  endif
78f1eb
+
78f1eb
+    if (UNLIKELY(nv < IV_MIN) || UNLIKELY(nv > IV_MAX)) {
78f1eb
+        return FALSE;
78f1eb
+    }
78f1eb
+
78f1eb
+    if ((IV) nv != nv) {
78f1eb
+        return FALSE;
78f1eb
+    }
78f1eb
+
78f1eb
+    *ivp = (IV) nv;
78f1eb
+    return TRUE;
78f1eb
+}
78f1eb
+
78f1eb
+#endif
78f1eb
+
78f1eb
 /* ------------------ pp.c, regcomp.c, toke.c, universal.c ------------ */
78f1eb
 
78f1eb
 #define MAX_CHARSET_NAME_LENGTH 2
78f1eb
diff --git a/pp.c b/pp.c
78f1eb
index c89cb7198c..0956121b27 100644
78f1eb
--- a/pp.c
78f1eb
+++ b/pp.c
78f1eb
@@ -1268,16 +1268,10 @@ PP(pp_multiply)
78f1eb
             NV nr = SvNVX(svr);
78f1eb
             NV result;
78f1eb
 
78f1eb
-            if (
78f1eb
-#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
78f1eb
-                !Perl_isnan(nl) && nl == (NV)(il = (IV)nl)
78f1eb
-                && !Perl_isnan(nr) && nr == (NV)(ir = (IV)nr)
78f1eb
-#else
78f1eb
-                nl == (NV)(il = (IV)nl) && nr == (NV)(ir = (IV)nr)
78f1eb
-#endif
78f1eb
-                )
78f1eb
+            if (lossless_NV_to_IV(nl, &il) && lossless_NV_to_IV(nr, &ir)) {
78f1eb
                 /* nothing was lost by converting to IVs */
78f1eb
                 goto do_iv;
78f1eb
+            }
78f1eb
             SP--;
78f1eb
             result = nl * nr;
78f1eb
 #  if defined(__sgi) && defined(USE_LONG_DOUBLE) && LONG_DOUBLEKIND == LONG_DOUBLE_IS_DOUBLEDOUBLE_128_BIT_BE_BE && NVSIZE == 16
78f1eb
@@ -1849,16 +1843,10 @@ PP(pp_subtract)
78f1eb
             NV nl = SvNVX(svl);
78f1eb
             NV nr = SvNVX(svr);
78f1eb
 
78f1eb
-            if (
78f1eb
-#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
78f1eb
-                !Perl_isnan(nl) && nl == (NV)(il = (IV)nl)
78f1eb
-                && !Perl_isnan(nr) && nr == (NV)(ir = (IV)nr)
78f1eb
-#else
78f1eb
-                nl == (NV)(il = (IV)nl) && nr == (NV)(ir = (IV)nr)
78f1eb
-#endif
78f1eb
-                )
78f1eb
+            if (lossless_NV_to_IV(nl, &il) && lossless_NV_to_IV(nr, &ir)) {
78f1eb
                 /* nothing was lost by converting to IVs */
78f1eb
                 goto do_iv;
78f1eb
+            }
78f1eb
             SP--;
78f1eb
             TARGn(nl - nr, 0); /* args not GMG, so can't be tainted */
78f1eb
             SETs(TARG);
78f1eb
diff --git a/pp_hot.c b/pp_hot.c
78f1eb
index 7d5ffc02fd..2df5df8303 100644
78f1eb
--- a/pp_hot.c
78f1eb
+++ b/pp_hot.c
78f1eb
@@ -1435,16 +1435,10 @@ PP(pp_add)
78f1eb
             NV nl = SvNVX(svl);
78f1eb
             NV nr = SvNVX(svr);
78f1eb
 
78f1eb
-            if (
78f1eb
-#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
78f1eb
-                !Perl_isnan(nl) && nl == (NV)(il = (IV)nl)
78f1eb
-                && !Perl_isnan(nr) && nr == (NV)(ir = (IV)nr)
78f1eb
-#else
78f1eb
-                nl == (NV)(il = (IV)nl) && nr == (NV)(ir = (IV)nr)
78f1eb
-#endif
78f1eb
-                )
78f1eb
+            if (lossless_NV_to_IV(nl, &il) && lossless_NV_to_IV(nr, &ir)) {
78f1eb
                 /* nothing was lost by converting to IVs */
78f1eb
                 goto do_iv;
78f1eb
+            }
78f1eb
             SP--;
78f1eb
             TARGn(nl + nr, 0); /* args not GMG, so can't be tainted */
78f1eb
             SETs(TARG);
78f1eb
diff --git a/proto.h b/proto.h
78f1eb
index 0f8feed187..74a8e46ab7 100644
78f1eb
--- a/proto.h
78f1eb
+++ b/proto.h
78f1eb
@@ -5224,6 +5224,13 @@ STATIC SV*	S_refto(pTHX_ SV* sv)
78f1eb
 
78f1eb
 #endif
78f1eb
 #if defined(PERL_IN_PP_C) || defined(PERL_IN_PP_HOT_C)
78f1eb
+#ifndef PERL_NO_INLINE_FUNCTIONS
78f1eb
+PERL_STATIC_INLINE bool	S_lossless_NV_to_IV(const NV nv, IV * ivp)
78f1eb
+			__attribute__warn_unused_result__;
78f1eb
+#define PERL_ARGS_ASSERT_LOSSLESS_NV_TO_IV	\
78f1eb
+	assert(ivp)
78f1eb
+#endif
78f1eb
+
78f1eb
 PERL_CALLCONV GV*	Perl_softref2xv(pTHX_ SV *const sv, const char *const what, const svtype type, SV ***spp)
78f1eb
 			__attribute__warn_unused_result__;
78f1eb
 #define PERL_ARGS_ASSERT_SOFTREF2XV	\
78f1eb
-- 
78f1eb
2.20.1
78f1eb