diff mbox series

[v8,16/35] Hexagon (target/hexagon/conv_emu.[ch]) utility functions

Message ID 1612763186-18161-17-git-send-email-tsimpson@quicinc.com (mailing list archive)
State New, archived
Headers show
Series [v8,01/35] Hexagon Update MAINTAINERS file | expand

Commit Message

Taylor Simpson Feb. 8, 2021, 5:46 a.m. UTC
Signed-off-by: Taylor Simpson <tsimpson@quicinc.com>
---
 target/hexagon/conv_emu.h |  31 ++++++++
 target/hexagon/conv_emu.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 208 insertions(+)
 create mode 100644 target/hexagon/conv_emu.h
 create mode 100644 target/hexagon/conv_emu.c

Comments

Richard Henderson Feb. 14, 2021, 8:57 p.m. UTC | #1
On 2/7/21 9:46 PM, Taylor Simpson wrote:
> +uint64_t conv_sf_to_8u(float32 in, float_status *fp_status);
> +uint32_t conv_sf_to_4u(float32 in, float_status *fp_status);
> +int64_t conv_sf_to_8s(float32 in, float_status *fp_status);
> +int32_t conv_sf_to_4s(float32 in, float_status *fp_status);
> +
> +uint64_t conv_df_to_8u(float64 in, float_status *fp_status);
> +uint32_t conv_df_to_4u(float64 in, float_status *fp_status);
> +int64_t conv_df_to_8s(float64 in, float_status *fp_status);
> +int32_t conv_df_to_4s(float64 in, float_status *fp_status);

You need to either use the normal float conversion routines, or document what
the differences are.

> +static uint64_t conv_f64_to_8u_n(float64 in, int will_negate,
> +                                 float_status *fp_status)
> +{
> +    uint8_t sign = float64_is_neg(in);
> +    if (float64_is_infinity(in)) {
> +        float_raise(float_flag_invalid, fp_status);
> +        if (float64_is_neg(in)) {
> +            return 0ULL;

This isn't right for will_negate.


r~
Taylor Simpson March 18, 2021, 3:57 a.m. UTC | #2
> -----Original Message-----
> From: Richard Henderson <richard.henderson@linaro.org>
> Sent: Sunday, February 14, 2021 2:57 PM
> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
> Cc: philmd@redhat.com; alex.bennee@linaro.org; laurent@vivier.eu;
> ale@rev.ng; Brian Cain <bcain@quicinc.com>
> Subject: Re: [PATCH v8 16/35] Hexagon (target/hexagon/conv_emu.[ch])
> utility functions
>
> On 2/7/21 9:46 PM, Taylor Simpson wrote:
> > +uint64_t conv_sf_to_8u(float32 in, float_status *fp_status);
> > +uint32_t conv_sf_to_4u(float32 in, float_status *fp_status);
> > +int64_t conv_sf_to_8s(float32 in, float_status *fp_status);
> > +int32_t conv_sf_to_4s(float32 in, float_status *fp_status);
> > +
> > +uint64_t conv_df_to_8u(float64 in, float_status *fp_status);
> > +uint32_t conv_df_to_4u(float64 in, float_status *fp_status);
> > +int64_t conv_df_to_8s(float64 in, float_status *fp_status);
> > +int32_t conv_df_to_4s(float64 in, float_status *fp_status);
>
> You need to either use the normal float conversion routines, or document
> what the differences are.

There are some differences in floating point flags raised, so I could write something like this:
    if (float32_is_infinity(RsV)) {
        float_raise(float_flag_invalid, &env->fp_status);
        if (float32_is_neg(RsV)) {
            RddV = 0ULL;
        } else {
            RddV = ~0ULL;
        }
    } else if (float32_is_any_nan(RsV)) {
        float_raise(float_flag_invalid, &env->fp_status);
        RddV = ~0ULL;
    } else if (float32_is_zero(RsV)) {
        RddV = 0;
    } else if (float32_is_neg(RsV)) {
        float_raise(float_flag_invalid, &env->fp_status);
        RddV = 0;
    } else {
        RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status);
    }

Does that work?


Thanks,
Taylor
Richard Henderson March 18, 2021, 1:30 p.m. UTC | #3
On 3/17/21 9:57 PM, Taylor Simpson wrote:
> 
> 
>> -----Original Message-----
>> From: Richard Henderson <richard.henderson@linaro.org>
>> Sent: Sunday, February 14, 2021 2:57 PM
>> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
>> Cc: philmd@redhat.com; alex.bennee@linaro.org; laurent@vivier.eu;
>> ale@rev.ng; Brian Cain <bcain@quicinc.com>
>> Subject: Re: [PATCH v8 16/35] Hexagon (target/hexagon/conv_emu.[ch])
>> utility functions
>>
>> On 2/7/21 9:46 PM, Taylor Simpson wrote:
>>> +uint64_t conv_sf_to_8u(float32 in, float_status *fp_status);
>>> +uint32_t conv_sf_to_4u(float32 in, float_status *fp_status);
>>> +int64_t conv_sf_to_8s(float32 in, float_status *fp_status);
>>> +int32_t conv_sf_to_4s(float32 in, float_status *fp_status);
>>> +
>>> +uint64_t conv_df_to_8u(float64 in, float_status *fp_status);
>>> +uint32_t conv_df_to_4u(float64 in, float_status *fp_status);
>>> +int64_t conv_df_to_8s(float64 in, float_status *fp_status);
>>> +int32_t conv_df_to_4s(float64 in, float_status *fp_status);
>>
>> You need to either use the normal float conversion routines, or document
>> what the differences are.
> 
> There are some differences in floating point flags raised, so I could write something like this:
>      if (float32_is_infinity(RsV)) {
>          float_raise(float_flag_invalid, &env->fp_status);
>          if (float32_is_neg(RsV)) {
>              RddV = 0ULL;
>          } else {
>              RddV = ~0ULL;
>          }

This isn't different from softfloat.c.

>      } else if (float32_is_any_nan(RsV)) {
>          float_raise(float_flag_invalid, &env->fp_status);
>          RddV = ~0ULL;

Nor is this.

>      } else if (float32_is_zero(RsV)) {
>          RddV = 0;

Not exactly a special case.

>      } else if (float32_is_neg(RsV)) {
>          float_raise(float_flag_invalid, &env->fp_status);
>          RddV = 0;

Not different.

>      } else {
>          RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status);
>      }
> 
> Does that work?

This is 100% identical with round_to_uint_and_pack as used by 
float32_to_uint64_round_to_zero.  It's all straight IEEE 754.


r~
Taylor Simpson March 18, 2021, 2:11 p.m. UTC | #4
> -----Original Message-----
> From: Richard Henderson <richard.henderson@linaro.org>
> Sent: Thursday, March 18, 2021 8:30 AM
> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
> Cc: philmd@redhat.com; alex.bennee@linaro.org; laurent@vivier.eu;
> ale@rev.ng; Brian Cain <bcain@quicinc.com>
> Subject: Re: [PATCH v8 16/35] Hexagon (target/hexagon/conv_emu.[ch])
> utility functions
>
> On 3/17/21 9:57 PM, Taylor Simpson wrote:
> >
> >
> >> -----Original Message-----
> >> From: Richard Henderson <richard.henderson@linaro.org>
> >> Sent: Sunday, February 14, 2021 2:57 PM
> >> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
> >> Cc: philmd@redhat.com; alex.bennee@linaro.org; laurent@vivier.eu;
> >> ale@rev.ng; Brian Cain <bcain@quicinc.com>
> >> Subject: Re: [PATCH v8 16/35] Hexagon (target/hexagon/conv_emu.[ch])
> >> utility functions
> >>
> >> On 2/7/21 9:46 PM, Taylor Simpson wrote:
> >>> +uint64_t conv_sf_to_8u(float32 in, float_status *fp_status);
> >>> +uint32_t conv_sf_to_4u(float32 in, float_status *fp_status);
> >>> +int64_t conv_sf_to_8s(float32 in, float_status *fp_status);
> >>> +int32_t conv_sf_to_4s(float32 in, float_status *fp_status);
> >>> +
> >>> +uint64_t conv_df_to_8u(float64 in, float_status *fp_status);
> >>> +uint32_t conv_df_to_4u(float64 in, float_status *fp_status);
> >>> +int64_t conv_df_to_8s(float64 in, float_status *fp_status);
> >>> +int32_t conv_df_to_4s(float64 in, float_status *fp_status);
> >>
> >> You need to either use the normal float conversion routines, or document
> >> what the differences are.
> >
> > There are some differences in floating point flags raised, so I could write
> something like this:
> >      if (float32_is_infinity(RsV)) {
> >          float_raise(float_flag_invalid, &env->fp_status);
> >          if (float32_is_neg(RsV)) {
> >              RddV = 0ULL;
> >          } else {
> >              RddV = ~0ULL;
> >          }
>
> This isn't different from softfloat.c.
>
> >      } else if (float32_is_any_nan(RsV)) {
> >          float_raise(float_flag_invalid, &env->fp_status);
> >          RddV = ~0ULL;
>
> Nor is this.

Actually, softfloat raises inexact instead of invalid.  Is there a way to override?

Thanks,
Taylor
Richard Henderson March 18, 2021, 3:35 p.m. UTC | #5
On 3/18/21 8:11 AM, Taylor Simpson wrote:
> Actually, softfloat raises inexact instead of invalid.  Is there a way to override?

Not true:

     switch (p.cls) {
     case float_class_snan:
     case float_class_qnan:
         s->float_exception_flags = orig_flags | float_flag_invalid;
         return max;


r~
Taylor Simpson March 18, 2021, 6:03 p.m. UTC | #6
> -----Original Message-----
> From: Richard Henderson <richard.henderson@linaro.org>
> Sent: Thursday, March 18, 2021 10:36 AM
> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
> Cc: philmd@redhat.com; alex.bennee@linaro.org; laurent@vivier.eu;
> ale@rev.ng; Brian Cain <bcain@quicinc.com>
> Subject: Re: [PATCH v8 16/35] Hexagon (target/hexagon/conv_emu.[ch])
> utility functions
>
> On 3/18/21 8:11 AM, Taylor Simpson wrote:
> > Actually, softfloat raises inexact instead of invalid.  Is there a way to
> override?
>
> Not true:
>
>      switch (p.cls) {
>      case float_class_snan:
>      case float_class_qnan:
>          s->float_exception_flags = orig_flags | float_flag_invalid;
>          return max;

Sorry, I was mistaken.  The difference is in the negative case.
    } else if (float32_is_neg(RsV)) {
        float_raise(float_flag_invalid, &env->fp_status);
        RddV = 0;

For many cases, this is what softfloat does.  The difference comes  when value is both negative and near zero (exponent is negative).  Softfloat checks the exponent before checking the sign.  round_to_int does this first
        if (a.exp < 0) {
            bool one;
            /* all fractional */
            s->float_exception_flags |= float_flag_inexact;
Then it sets
     a.cls = float_class_zero;
So round_to_uint_and_pack does this
    case float_class_zero:
        return 0;


Here's an example from float_convs
    from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
Softfloat:to uint64: 0 (INEXACT )
Hexagon:to uint64: 0 (INVALID)


So, just looking at the float_convs tests the Hexagon version of f32->uint64 would be
    if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) {
        float_raise(float_flag_invalid, &env->fp_status);
        RddV = 0;
    } else {
        RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status);
    }


Thanks,
Taylor
Richard Henderson March 18, 2021, 6:32 p.m. UTC | #7
On 3/18/21 12:03 PM, Taylor Simpson wrote:
> Here's an example from float_convs
>      from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
> Softfloat:to uint64: 0 (INEXACT )
> Hexagon:to uint64: 0 (INVALID)

Ahh, so an ieee conformance issue in hexagon -- failure to defer the sign check 
til after rounding.

> So, just looking at the float_convs tests the Hexagon version of f32->uint64 would be
>      if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) {
>          float_raise(float_flag_invalid, &env->fp_status);
>          RddV = 0;
>      } else {
>          RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status);
>      }

Looks good.  Just add a comment too.


r~
diff mbox series

Patch

diff --git a/target/hexagon/conv_emu.h b/target/hexagon/conv_emu.h
new file mode 100644
index 0000000..cade9de
--- /dev/null
+++ b/target/hexagon/conv_emu.h
@@ -0,0 +1,31 @@ 
+/*
+ *  Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEXAGON_CONV_EMU_H
+#define HEXAGON_CONV_EMU_H
+
+uint64_t conv_sf_to_8u(float32 in, float_status *fp_status);
+uint32_t conv_sf_to_4u(float32 in, float_status *fp_status);
+int64_t conv_sf_to_8s(float32 in, float_status *fp_status);
+int32_t conv_sf_to_4s(float32 in, float_status *fp_status);
+
+uint64_t conv_df_to_8u(float64 in, float_status *fp_status);
+uint32_t conv_df_to_4u(float64 in, float_status *fp_status);
+int64_t conv_df_to_8s(float64 in, float_status *fp_status);
+int32_t conv_df_to_4s(float64 in, float_status *fp_status);
+
+#endif
diff --git a/target/hexagon/conv_emu.c b/target/hexagon/conv_emu.c
new file mode 100644
index 0000000..3985b10
--- /dev/null
+++ b/target/hexagon/conv_emu.c
@@ -0,0 +1,177 @@ 
+/*
+ *  Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "fpu/softfloat.h"
+#include "macros.h"
+#include "conv_emu.h"
+
+#define LL_MAX_POS 0x7fffffffffffffffULL
+#define MAX_POS 0x7fffffffU
+
+static uint64_t conv_f64_to_8u_n(float64 in, int will_negate,
+                                 float_status *fp_status)
+{
+    uint8_t sign = float64_is_neg(in);
+    if (float64_is_infinity(in)) {
+        float_raise(float_flag_invalid, fp_status);
+        if (float64_is_neg(in)) {
+            return 0ULL;
+        } else {
+            return ~0ULL;
+        }
+    }
+    if (float64_is_any_nan(in)) {
+        float_raise(float_flag_invalid, fp_status);
+        return ~0ULL;
+    }
+    if (float64_is_zero(in)) {
+        return 0;
+    }
+    if (sign) {
+        float_raise(float_flag_invalid, fp_status);
+        return 0;
+    }
+    if (float64_lt(in, float64_half, fp_status)) {
+        /* Near zero, captures large fracshifts, denorms, etc */
+        float_raise(float_flag_inexact, fp_status);
+        switch (get_float_rounding_mode(fp_status)) {
+        case float_round_down:
+            if (will_negate) {
+                return 1;
+            } else {
+                return 0;
+            }
+        case float_round_up:
+            if (!will_negate) {
+                return 1;
+            } else {
+                return 0;
+            }
+        default:
+            return 0;    /* nearest or towards zero */
+        }
+    }
+    return float64_to_uint64(in, fp_status);
+}
+
+static void clr_float_exception_flags(uint8_t flag, float_status *fp_status)
+{
+    uint8_t flags = fp_status->float_exception_flags;
+    flags &= ~flag;
+    set_float_exception_flags(flags, fp_status);
+}
+
+static uint32_t conv_df_to_4u_n(float64 fp64, int will_negate,
+                                float_status *fp_status)
+{
+    uint64_t tmp;
+    tmp = conv_f64_to_8u_n(fp64, will_negate, fp_status);
+    if (tmp > 0x00000000ffffffffULL) {
+        clr_float_exception_flags(float_flag_inexact, fp_status);
+        float_raise(float_flag_invalid, fp_status);
+        return ~0U;
+    }
+    return (uint32_t)tmp;
+}
+
+uint64_t conv_df_to_8u(float64 in, float_status *fp_status)
+{
+    return conv_f64_to_8u_n(in, 0, fp_status);
+}
+
+uint32_t conv_df_to_4u(float64 in, float_status *fp_status)
+{
+    return conv_df_to_4u_n(in, 0, fp_status);
+}
+
+int64_t conv_df_to_8s(float64 in, float_status *fp_status)
+{
+    uint8_t sign = float64_is_neg(in);
+    uint64_t tmp;
+    if (float64_is_any_nan(in)) {
+        float_raise(float_flag_invalid, fp_status);
+        return -1;
+    }
+    if (sign) {
+        float64 minus_fp64 = float64_abs(in);
+        tmp = conv_f64_to_8u_n(minus_fp64, 1, fp_status);
+    } else {
+        tmp = conv_f64_to_8u_n(in, 0, fp_status);
+    }
+    if (tmp > (LL_MAX_POS + sign)) {
+        clr_float_exception_flags(float_flag_inexact, fp_status);
+        float_raise(float_flag_invalid, fp_status);
+        tmp = (LL_MAX_POS + sign);
+    }
+    if (sign) {
+        return -tmp;
+    } else {
+        return tmp;
+    }
+}
+
+int32_t conv_df_to_4s(float64 in, float_status *fp_status)
+{
+    uint8_t sign = float64_is_neg(in);
+    uint64_t tmp;
+    if (float64_is_any_nan(in)) {
+        float_raise(float_flag_invalid, fp_status);
+        return -1;
+    }
+    if (sign) {
+        float64 minus_fp64 = float64_abs(in);
+        tmp = conv_f64_to_8u_n(minus_fp64, 1, fp_status);
+    } else {
+        tmp = conv_f64_to_8u_n(in, 0, fp_status);
+    }
+    if (tmp > (MAX_POS + sign)) {
+        clr_float_exception_flags(float_flag_inexact, fp_status);
+        float_raise(float_flag_invalid, fp_status);
+        tmp = (MAX_POS + sign);
+    }
+    if (sign) {
+        return -tmp;
+    } else {
+        return tmp;
+    }
+}
+
+uint64_t conv_sf_to_8u(float32 in, float_status *fp_status)
+{
+    float64 fp64 = float32_to_float64(in, fp_status);
+    return conv_df_to_8u(fp64, fp_status);
+}
+
+uint32_t conv_sf_to_4u(float32 in, float_status *fp_status)
+{
+    float64 fp64 = float32_to_float64(in, fp_status);
+    return conv_df_to_4u(fp64, fp_status);
+}
+
+int64_t conv_sf_to_8s(float32 in, float_status *fp_status)
+{
+    float64 fp64 = float32_to_float64(in, fp_status);
+    return conv_df_to_8s(fp64, fp_status);
+}
+
+int32_t conv_sf_to_4s(float32 in, float_status *fp_status)
+{
+    float64 fp64 = float32_to_float64(in, fp_status);
+    return conv_df_to_4s(fp64, fp_status);
+}