summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--target/ppc/int_helper.c13
-rwxr-xr-xtests/tcg/configure.sh6
-rw-r--r--tests/tcg/ppc64/Makefile.target13
-rw-r--r--tests/tcg/ppc64le/Makefile.target12
-rw-r--r--tests/tcg/ppc64le/bcdsub.c130
5 files changed, 171 insertions, 3 deletions
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 36b8a73a54..ce304f4933 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;
+}