qemu-ppc
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] target/ppc: Fix bcdsub. emulation when result overflows


From: David Gibson
Subject: Re: [PATCH] target/ppc: Fix bcdsub. emulation when result overflows
Date: Tue, 23 Feb 2021 11:29:04 +1100

On Mon, Feb 22, 2021 at 04:40:35PM -0300, Fabiano Rosas wrote:
65;6203;1c> The commit d03b174a83 (target/ppc: simplify bcdadd/sub functions)
> meant to simplify some of the code but it inadvertently altered the
> way the CR6 field is set after the operation has overflowed.
> 
> The CR6 bits are set based on the *unbounded* result of the operation,
> so we need to look at the result before returning from bcd_add_mag,
> otherwise we will look at 0 when it overflows.
> 
> Consider the following subtraction:
> 
> v0 = 0x9999999999999999999999999999999c (maximum positive BCD value)
> v1 = 0x0000000000000000000000000000001d (negative one BCD value)
> bcdsub. v0,v0,v1,0
> 
> The Power ISA 2.07B says:
> If the unbounded result is greater than zero, do the following.
>   If PS=0, the sign code of the result is set to 0b1100.
>   If PS=1, the sign code of the result is set to 0b1111.
>   If the operation overflows, CR field 6 is set to 0b0101. Otherwise,
>   CR field 6 is set to 0b0100.
> 
> POWER9 hardware:
> vr0 = 0x0000000000000000000000000000000c (positive zero BCD value)
> cr6 = 0b0101 (0x5) (positive, overflow)
> 
> QEMU:
> vr0 = 0x0000000000000000000000000000000c (positive zero BCD value)
> cr6 = 0b0011 (0x3) (zero, overflow) <--- wrong
> 
> This patch reverts the part of d03b174a83 that introduced the
> problem and adds a test-case to avoid further regressions:
> 
> before:
> $ make run-tcg-tests-ppc64le-linux-user
> (...)
>   TEST    bcdsub on ppc64le
> bcdsub: qemu/tests/tcg/ppc64le/bcdsub.c:58: test_bcdsub_gt:
> Assertion `(cr >> 4) == ((1 << 2) | (1 << 0))' failed.
> 
> Fixes: d03b174a83 (target/ppc: simplify bcdadd/sub functions)
> Reported-by: Paul Clarke <pc@us.ibm.com>
> Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>

Applied to ppc-for-6.0, thanks.

> ---
>  target/ppc/int_helper.c           |  13 ++-
>  tests/tcg/configure.sh            |   6 ++
>  tests/tcg/ppc64/Makefile.target   |  13 +++
>  tests/tcg/ppc64le/Makefile.target |  12 +++
>  tests/tcg/ppc64le/bcdsub.c        | 130 ++++++++++++++++++++++++++++++
>  5 files changed, 171 insertions(+), 3 deletions(-)
>  create mode 100644 tests/tcg/ppc64/Makefile.target
>  create mode 100644 tests/tcg/ppc64le/Makefile.target
>  create mode 100644 tests/tcg/ppc64le/bcdsub.c
> 
> diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c
> index 0b682a1f94..429de28494 100644
> --- a/target/ppc/int_helper.c
> +++ b/target/ppc/int_helper.c
> @@ -2175,14 +2175,17 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b)
>      return 0;
>  }
>  
> -static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int 
> *invalid,
> +static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int 
> *invalid,
>                         int *overflow)
>  {
>      int carry = 0;
>      int i;
> +    int is_zero = 1;
> +
>      for (i = 1; i <= 31; i++) {
>          uint8_t digit = bcd_get_digit(a, i, invalid) +
>                          bcd_get_digit(b, i, invalid) + carry;
> +        is_zero &= (digit == 0);
>          if (digit > 9) {
>              carry = 1;
>              digit -= 10;
> @@ -2194,6 +2197,7 @@ static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, 
> ppc_avr_t *b, int *invalid,
>      }
>  
>      *overflow = carry;
> +    return is_zero;
>  }
>  
>  static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int 
> *invalid,
> @@ -2225,14 +2229,15 @@ uint32_t helper_bcdadd(ppc_avr_t *r,  ppc_avr_t *a, 
> ppc_avr_t *b, uint32_t ps)
>      int sgnb = bcd_get_sgn(b);
>      int invalid = (sgna == 0) || (sgnb == 0);
>      int overflow = 0;
> +    int zero = 0;
>      uint32_t cr = 0;
>      ppc_avr_t result = { .u64 = { 0, 0 } };
>  
>      if (!invalid) {
>          if (sgna == sgnb) {
>              result.VsrB(BCD_DIG_BYTE(0)) = bcd_preferred_sgn(sgna, ps);
> -            bcd_add_mag(&result, a, b, &invalid, &overflow);
> -            cr = bcd_cmp_zero(&result);
> +            zero = bcd_add_mag(&result, a, b, &invalid, &overflow);
> +            cr = (sgna > 0) ? CRF_GT : CRF_LT;
>          } else {
>              int magnitude = bcd_cmp_mag(a, b);
>              if (magnitude > 0) {
> @@ -2255,6 +2260,8 @@ uint32_t helper_bcdadd(ppc_avr_t *r,  ppc_avr_t *a, 
> ppc_avr_t *b, uint32_t ps)
>          cr = CRF_SO;
>      } else if (overflow) {
>          cr |= CRF_SO;
> +    } else if (zero) {
> +        cr |= CRF_EQ;
>      }
>  
>      *r = result;
> diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh
> index 551c02f469..a0b709948c 100755
> --- a/tests/tcg/configure.sh
> +++ b/tests/tcg/configure.sh
> @@ -251,6 +251,12 @@ for target in $target_list; do
>                  echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak
>              fi
>          ;;
> +        ppc*)
> +            if do_compiler "$target_compiler" $target_compiler_cflags \
> +               -mpower8-vector -o $TMPE $TMPC; then
> +                echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak
> +            fi
> +        ;;
>      esac
>  
>      enabled_cross_compilers="$enabled_cross_compilers $target_compiler"
> diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target
> new file mode 100644
> index 0000000000..0c6a4585fc
> --- /dev/null
> +++ b/tests/tcg/ppc64/Makefile.target
> @@ -0,0 +1,13 @@
> +# -*- Mode: makefile -*-
> +#
> +# ppc64 specific tweaks
> +
> +VPATH += $(SRC_PATH)/tests/tcg/ppc64
> +VPATH += $(SRC_PATH)/tests/tcg/ppc64le
> +
> +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),)
> +PPC64_TESTS=bcdsub
> +endif
> +bcdsub: CFLAGS += -mpower8-vector
> +
> +TESTS += $(PPC64_TESTS)
> diff --git a/tests/tcg/ppc64le/Makefile.target 
> b/tests/tcg/ppc64le/Makefile.target
> new file mode 100644
> index 0000000000..1acfcff94a
> --- /dev/null
> +++ b/tests/tcg/ppc64le/Makefile.target
> @@ -0,0 +1,12 @@
> +# -*- Mode: makefile -*-
> +#
> +# ppc64le specific tweaks
> +
> +VPATH += $(SRC_PATH)/tests/tcg/ppc64le
> +
> +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),)
> +PPC64LE_TESTS=bcdsub
> +endif
> +bcdsub: CFLAGS += -mpower8-vector
> +
> +TESTS += $(PPC64LE_TESTS)
> diff --git a/tests/tcg/ppc64le/bcdsub.c b/tests/tcg/ppc64le/bcdsub.c
> new file mode 100644
> index 0000000000..8c188cae6d
> --- /dev/null
> +++ b/tests/tcg/ppc64le/bcdsub.c
> @@ -0,0 +1,130 @@
> +#include <assert.h>
> +#include <unistd.h>
> +#include <signal.h>
> +
> +#define CRF_LT  (1 << 3)
> +#define CRF_GT  (1 << 2)
> +#define CRF_EQ  (1 << 1)
> +#define CRF_SO  (1 << 0)
> +#define UNDEF   0
> +
> +#define BCDSUB(vra, vrb, ps)                    \
> +    asm ("bcdsub. %1,%2,%3,%4;"                 \
> +         "mfocrf %0,0b10;"                      \
> +         : "=r" (cr), "=v" (vrt)                \
> +         : "v" (vra), "v" (vrb), "i" (ps)       \
> +         : );
> +
> +#define TEST(vra, vrb, ps, exp_res, exp_cr6)    \
> +    do {                                        \
> +        __int128 vrt = 0;                       \
> +        int cr = 0;                             \
> +        BCDSUB(vra, vrb, ps);                   \
> +        if (exp_res)                            \
> +            assert(vrt == exp_res);             \
> +        assert((cr >> 4) == exp_cr6);           \
> +    } while (0)
> +
> +
> +/*
> + * Unbounded result is equal to zero:
> + *   sign = (PS) ? 0b1111 : 0b1100
> + *   CR6 = 0b0010
> + */
> +void test_bcdsub_eq(void)
> +{
> +    __int128 a, b;
> +
> +    /* maximum positive BCD value */
> +    a = b = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c);
> +
> +    TEST(a, b, 0, 0xc, CRF_EQ);
> +    TEST(a, b, 1, 0xf, CRF_EQ);
> +}
> +
> +/*
> + * Unbounded result is greater than zero:
> + *   sign = (PS) ? 0b1111 : 0b1100
> + *   CR6 = (overflow) ? 0b0101 : 0b0100
> + */
> +void test_bcdsub_gt(void)
> +{
> +    __int128 a, b, c;
> +
> +    /* maximum positive BCD value */
> +    a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c);
> +
> +    /* negative one BCD value */
> +    b = (__int128) 0x1d;
> +
> +    TEST(a, b, 0, 0xc, (CRF_GT | CRF_SO));
> +    TEST(a, b, 1, 0xf, (CRF_GT | CRF_SO));
> +
> +    c = (((__int128) 0x9999999999999999) << 64 | 0x999999999999998c);
> +
> +    TEST(c, b, 0, a, CRF_GT);
> +    TEST(c, b, 1, (a | 0x3), CRF_GT);
> +}
> +
> +/*
> + * Unbounded result is less than zero:
> + *   sign = 0b1101
> + *   CR6 = (overflow) ? 0b1001 : 0b1000
> + */
> +void test_bcdsub_lt(void)
> +{
> +    __int128 a, b;
> +
> +    /* positive zero BCD value */
> +    a = (__int128) 0xc;
> +
> +    /* positive one BCD value */
> +    b = (__int128) 0x1c;
> +
> +    TEST(a, b, 0, 0x1d, CRF_LT);
> +    TEST(a, b, 1, 0x1d, CRF_LT);
> +
> +    /* maximum negative BCD value */
> +    a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999d);
> +
> +    /* positive one BCD value */
> +    b = (__int128) 0x1c;
> +
> +    TEST(a, b, 0, 0xd, (CRF_LT | CRF_SO));
> +    TEST(a, b, 1, 0xd, (CRF_LT | CRF_SO));
> +}
> +
> +void test_bcdsub_invalid(void)
> +{
> +    __int128 a, b;
> +
> +    /* positive one BCD value */
> +    a = (__int128) 0x1c;
> +    b = 0xf00;
> +
> +    TEST(a, b, 0, UNDEF, CRF_SO);
> +    TEST(a, b, 1, UNDEF, CRF_SO);
> +
> +    TEST(b, a, 0, UNDEF, CRF_SO);
> +    TEST(b, a, 1, UNDEF, CRF_SO);
> +
> +    a = 0xbad;
> +
> +    TEST(a, b, 0, UNDEF, CRF_SO);
> +    TEST(a, b, 1, UNDEF, CRF_SO);
> +}
> +
> +int main(void)
> +{
> +    struct sigaction action;
> +
> +    action.sa_handler = _exit;
> +    sigaction(SIGABRT, &action, NULL);
> +
> +    test_bcdsub_eq();
> +    test_bcdsub_gt();
> +    test_bcdsub_lt();
> +    test_bcdsub_invalid();
> +
> +    return 0;
> +}

-- 
David Gibson                    | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au  | minimalist, thank you.  NOT _the_ _other_
                                | _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]