diff mbox

[6/7] softfloat: Add float*_min() and float*_max() functions

Message ID 1299867146-22049-7-git-send-email-peter.maydell@linaro.org
State Accepted
Commit 274f1b041e0d550750cc6992092ad0197b2c5320
Headers show

Commit Message

Peter Maydell March 11, 2011, 6:12 p.m. UTC
Add min and max operations to softfloat. This allows us to implement
propagation of NaNs and handling of negative zero correctly (unlike
the approach of having target helper routines return one of the operands
based on the result of a comparison op).

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 fpu/softfloat.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
 fpu/softfloat.h |    4 ++++
 2 files changed, 53 insertions(+), 0 deletions(-)

Comments

Nathan Froyd March 11, 2011, 6:28 p.m. UTC | #1
On Fri, Mar 11, 2011 at 06:12:25PM +0000, Peter Maydell wrote:
> Add min and max operations to softfloat. This allows us to implement
> propagation of NaNs and handling of negative zero correctly (unlike
> the approach of having target helper routines return one of the operands
> based on the result of a comparison op).

Are these useful anyplace beside ARM?  i.e. do they implement the
correct AltiVec/VSX/SSE* (any others?) semantics?

-Nathan
Peter Maydell March 11, 2011, 11:31 p.m. UTC | #2
On 11 March 2011 18:28, Nathan Froyd <froydnj@codesourcery.com> wrote:
> On Fri, Mar 11, 2011 at 06:12:25PM +0000, Peter Maydell wrote:
>> Add min and max operations to softfloat. This allows us to implement
>> propagation of NaNs and handling of negative zero correctly (unlike
>> the approach of having target helper routines return one of the operands
>> based on the result of a comparison op).
>
> Are these useful anyplace beside ARM?  i.e. do they implement the
> correct AltiVec/VSX/SSE* (any others?) semantics?

I had a look at the Altivec manual and I believe they're the
right semantics for Altivec/VSX vminfp/vmaxfp. So that's two
use cases (currently PPC has some hand-rolled stuff in
op_helper.c which specialcases NaNs but a quick glance
suggests it will get the -0 case wrong.)

Intel's SSE MAXSD/MINSD etc have very weird behaviour for
the special cases: if both operands are 0.0s of any sign
you always get the second operand, so max(-0,+0) != max(+0,-0),
and if only one operand is a NaN then the second operand
is returned whether it is the NaN or not (so max(NaN, 3)
!= max(3, NaN). This is clearly hopelessly target-specific :-)

I think the ARM/PPC semantics are the "obvious" ones:
 * handle input denormals by squashing to zero like other ops
   (you can always flip the status flags in your target wrapper
   if a particular instruction behaves differently)
 * handle NaN propagation in the same way as all other ops
 * maximum of (-0,+0) is +0, minimum of (-0,+0) is -0

so I think they have a pretty good chance of being useful for
other architectures that have a floating point min/max.
(They're the Java Math.min() and Math.max() semantics too.)

-- PMM
diff mbox

Patch

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 30b07e9..55f02ae 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -6052,6 +6052,55 @@  int float128_compare_quiet( float128 a, float128 b STATUS_PARAM )
     return float128_compare_internal(a, b, 1 STATUS_VAR);
 }
 
+/* min() and max() functions. These can't be implemented as
+ * 'compare and pick one input' because that would mishandle
+ * NaNs and +0 vs -0.
+ */
+#define MINMAX(s, nan_exp)                                              \
+INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b,     \
+                                        int ismin STATUS_PARAM )        \
+{                                                                       \
+    flag aSign, bSign;                                                  \
+    bits ## s av, bv;                                                   \
+    a = float ## s ## _squash_input_denormal(a STATUS_VAR);             \
+    b = float ## s ## _squash_input_denormal(b STATUS_VAR);             \
+    if (float ## s ## _is_any_nan(a) ||                                 \
+        float ## s ## _is_any_nan(b)) {                                 \
+        return propagateFloat ## s ## NaN(a, b STATUS_VAR);             \
+    }                                                                   \
+    aSign = extractFloat ## s ## Sign(a);                               \
+    bSign = extractFloat ## s ## Sign(b);                               \
+    av = float ## s ## _val(a);                                         \
+    bv = float ## s ## _val(b);                                         \
+    if (aSign != bSign) {                                               \
+        if (ismin) {                                                    \
+            return aSign ? a : b;                                       \
+        } else {                                                        \
+            return aSign ? b : a;                                       \
+        }                                                               \
+    } else {                                                            \
+        if (ismin) {                                                    \
+            return (aSign ^ (av < bv)) ? a : b;                         \
+        } else {                                                        \
+            return (aSign ^ (av < bv)) ? b : a;                         \
+        }                                                               \
+    }                                                                   \
+}                                                                       \
+                                                                        \
+float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM)  \
+{                                                                       \
+    return float ## s ## _minmax(a, b, 1 STATUS_VAR);                   \
+}                                                                       \
+                                                                        \
+float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM)  \
+{                                                                       \
+    return float ## s ## _minmax(a, b, 0 STATUS_VAR);                   \
+}
+
+MINMAX(32, 0xff)
+MINMAX(64, 0x7ff)
+
+
 /* Multiply A by 2 raised to the power N.  */
 float32 float32_scalbn( float32 a, int n STATUS_PARAM )
 {
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index fd61dc4..1d37d65 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -333,6 +333,8 @@  int float32_le_quiet( float32, float32 STATUS_PARAM );
 int float32_lt_quiet( float32, float32 STATUS_PARAM );
 int float32_compare( float32, float32 STATUS_PARAM );
 int float32_compare_quiet( float32, float32 STATUS_PARAM );
+float32 float32_min(float32, float32 STATUS_PARAM);
+float32 float32_max(float32, float32 STATUS_PARAM);
 int float32_is_quiet_nan( float32 );
 int float32_is_signaling_nan( float32 );
 float32 float32_maybe_silence_nan( float32 );
@@ -445,6 +447,8 @@  int float64_le_quiet( float64, float64 STATUS_PARAM );
 int float64_lt_quiet( float64, float64 STATUS_PARAM );
 int float64_compare( float64, float64 STATUS_PARAM );
 int float64_compare_quiet( float64, float64 STATUS_PARAM );
+float64 float64_min(float64, float64 STATUS_PARAM);
+float64 float64_max(float64, float64 STATUS_PARAM);
 int float64_is_quiet_nan( float64 a );
 int float64_is_signaling_nan( float64 );
 float64 float64_maybe_silence_nan( float64 );